CSS : Summary of Best Practices

Brian J.

This is currently a working draft. Feel free to look it over and let me know if you have any suggestions, corrections or comments.

Intro

This document is not necessarily meant to be a "jumping off point" for the uninitiated. Instead, it is meant to document best practices, tips and tricks that can (hopefully) have a beginner make full and proper use of CSS. All the while, avoiding many of the stumbling blocks - and incorporating some of the best practices available. You will not find step-by-step construction of pages, but links will be given to examples that can be deconstructed for the curious.

Additionally, many of the points in this document are made as a statement of opinion over fact. These opinions have often been the result of many years of figuring out what best works for me - and often, under a democratic poll, been incorporated in at least some way to larger teams. That being said, this document will highlight an underlying style guide that works quite well.

I am opinionated - you may disagree. In which case, feel free to make your corrections and redistribute. This document is available freely under the Creative Commons license.

Terms

To try and keep things consistent and a bit shorter, I may use shorthand names or muddle definitions for similar things. To keep it clear, here are common (mis)used terms:

IE
Internet Explorer. If IE6 is used, I'm referencing Internet Explorer, version 6.
FF
Firefox
Developer
For this document, let's ignore back end developers. This term is used solely to refer to front end web developers.
Selector
The bit of CSS that specifies an element.
Declaration
The bit of CSS that falls within a curly brace.
Curly brace
These guys: {}
em
A measure based on the current font size. An "em" is the width of the widest character in the font set.
px
A measure based on screen pixels.
hasLayout
I will knowingly misuse this term. It will be covered in more detail later (don't worry if this doesn't make sense). Internet Explorer has a condition that if a parent contains only children that are floated, the parent itself will collapse unless "hasLayout" is triggered. Similarly, other browsers will be collapsed by way of block formatting context. For simplicities sake, I will consider all conditions when a parent element becomes collapsed by way of all children being floated simply as "hasLayout."

What isn't covered

For brevity's sake, I will not be covering HTML. If you are not knowledgeable about (proper) HTML, this document will be of limited use, as CSS and HTML are closely tied to one another.

On the same token, Javascript will not be covered. If you have interest in Javascript, I suggest you look watch Douglas Crockford's lectures hosted on YUI theater http://developer.yahoo.com/yui/theater/video.php?v=crockonjs-1 and check out his book http://oreilly.com/catalog/9780596517748.

CSS is more than learning the basic terms. To keep focused on the "weird bits" of CSS, the specific declarations will mostly be ignored. If you'd like to learn about all the different CSS declarations available, you can find them on W3Schools http://www.w3schools.com/CSS/css_reference.asp.

I will not be covering things I consider to be bad practice - unless used solely as a demonstration of bad practice. This includes: conditional comments and inline styles.

Why CSS is great

If you're reading this, you probably don't need the sales pitch. Sadly, many beginners tout the benefits of CSS - but demonstrate a clear misunderstanding of it's power and benefit.

CSS is applied globally. If a rule matches a set of markup, it's declarations will be directly applied. If something later has equal or higher specificity, those relevant declarations will be overwritten. If a rule is not understood by a browser (such as IE6 and min-height), it will be ignored completely (leaving you with what is presumed to be a graceful degradation).

Developers seem to live in a strange little place between the creative and the scientific. They write code that manifests itself in some creative way. Writing CSS is not necessarily a creative endeavor - and aesthetic design skill is not a prerequisite. To be clear - it is a programming language written by developers. There is often fair overlap between designers and developers, but a good designer may be a horrible developer and a horrible designer may be an good developer.

State of affairs

Unlike most product life cycles, the Web is tied to the cycles of many different pieces of software. Some people are tied to their browser because they don't realize they're out of date, their work requires a specific version or any number of reasons. In any case, the point is clear that IE6, especially, is very much alive. Our code must be made to support this browser in at least a basic way. This is not to say, however, that what you see in a modern browser must be exactly the same - an important distinction that (hopefully) designers and managers everywhere can agree on.

This concept is of a progressive enhancement. You create a base functionality - something that works for everyone, and start piling on additional features for those browsers that support them. We will talk later about the specifics of what progressive enhancements can be easily made.

Alternatively, you may sometimes be required to use graceful degradation. You create a base functionality - but, for some reason, a particular browser doesn't support the one standard bit of code you're trying to use. An example may be the position property of fixed, which IE6 does not understand - and degrades to the position of static (instead of the generally more desirable absolute in this instance). Using browser specific targeting, you may want to use absolute positioning to gracefully degrade the behavior to something more along the lines of what you're developing (although absolute and static are still very different - this may sometimes be considered "close enough").

Tools of the trade

Of course, you'll need a text editor. Autocomplete and syntax highlighting are nice. I won't name any, since there are tons of them out there - just find something that works for you.

Firebug has long been the tool of choice for developers. It lets you inspect specific elements and see and change declarations and see inheritance. It also displays computed values and other handy bits. It is, however, only available on Firefox. One critical flaw of the Firefox / Firebug combo is their efficiencies. It's not uncommon for complicated pages to become incredibly slow while inspecting - especially comparing to the alternatives. Install Firebug by visiting http://getfirebug.com/ in Firefox and click "Install".

Both Chrome and Safari offer an excellent Web Inspector built right in. Their features compare well with Firebug and I have found them to be much more efficient. Enable the inspector in Chrome or Safari with Shift-Cntrl-I or Apple-Alt-I.

Opera has it's own tool called Dragonfly. Built entirely of Javascript, there is no updating required, as the latest version is fetched on each execution. I've found the features and performance of Dragonfly to be lacking compared to the alternatives. It can also be enabled with the Shift-Cntrl-I or Apple-Alt-I keys.

Internet Explorer comes with a web inspector - unfortunately, it's near useless and painfully slow. A decent alternative may be to use a Firebug Lite bookmarklet, which gives the base functionality of Firebug to most any browser that supports JS. It can be installed at http://getfirebug.com/firebuglite.

One tool that developers often overlook is CSS itself. Many times, I've seen other developers not understanding why a specific style would not be applied only to find out they were editing the wrong file. Never underestimate the power of debug borders. If you're trying to understand a layout - try applying a border: 1px solid red; to the element or it's parent. If styles are not being applied, make sure you're editing the correct file by applying a body * { border: 1px solid red; }.

Another common problem is that some developers - especially beginners - will start blaming CSS for their layout issues, when it's often invalid markup. Be sure to always validate your markup before debugging. There are many extensions and tools that can be used to automatically validate code. I prefer and highly suggest the Firefox Add-On HTML Validator available at http://users.skynet.be/mgueury/mozilla/.

Natural selectors

Natural or native selectors are those that use element types instead of classes or IDs. An example would be:

#myModule h4 {
  color: #CCC;
}

The ID of myModule is used to namespace the natural selector, preventing it from polluting the global namespace.

If many different elements share common styling, you may "stack" those selectors onto one declaration, such as:

#myModule h4,
#myModule p {
  color: #CCC;
}

This will apply the same coloring for headers and paragraphs within the myModule namespace. It is not uncommon to then need to specify another selector for specifics for the header and/or paragraph. If practical (more than a few simple declarations), it's often easier to maintain and read to have multiple declaration blocks, such as:

#myModule h4,
#myModule p {
  color: #CCC;
  padding: .5em 0;
}

#myModule h4 {
  text-align: center;
}

In this example, it made more sense to keep the base stylings of the header and paragraph together - and simply add extra styling for the header.

Class vs. ID and Specificity

There is a clear hierarchy of specificity - meaning, which style to apply if there's a conflict. As a general rule of thumb, natural selectors are very weak, classes are less weak and IDs are very strong. You can "drill down" and have many combinations, such as:

body.products div#myModule ul.products li {
  border: 1px solid #000;
}

body.products #myModule ul.products li {
  border: none;
}

In this example, the border would be applied, as the first declaration has higher specificity (notice that the div was explicit in the #myModule portion of the selector).

It is important to always try and maintain the least amount of specificity possible. If, later, you are required to overwrite a previous selector for any reason, you do not want to start a war of escalation by creating selectors that become more elaborate and more difficult to maintain. In general, you should always maintain a proper module namespace, then use only natural selectors. Classes should be used only sparingly within these modules. Additionally, for proper page structure, you may need to add specificity to designate which portion of the page the module may be existing, such as:

.header #myModule {
  border: none;
}

.secondary #myModule {
  border: 1px solid #000;
}

In this way, you may have a module of agnostic markup be portable to different sections of a page with only minor CSS changes to accommodate.

Declarations

I won't go into great detail about CSS declarations, as they can viewed in great detail elsewhere, such as W3Schools: http://www.w3schools.com/CSS/css_reference.asp.

One important note I would like to make regarding declarations of measure: be aware of it's relative use. Often, beginner developers will use font-size defined in pixels. In general, this should be avoided as it can be confusing in terms accessibility. Font-size should (in general) be applied either as a measure of em or percentage. Measures relative to images (or similarly fixed size elements such as Flash or native video) should be measured in pixels.

Namespace

As mentioned above, IDs are "stronger" than classes. Further, they may only be used once per page, as they are designated as unique elements to a page. For most development, pages can be broken into "modules" - each with it's unique properties and styles. This namespacing of modules is highlighted in the concept of standard module formatting for HTML. With modules developed in this format - and embedded in a page with correctly namespaced regions (header, nav, content, primary content, secondary content, footer, etc) - you can more easily port different modules to different regions with very limited CSS changes.

This is where the benefits of CSS shine. Assuming you have a module with both a general module class applied (say, mod) and a unique namespace ID applied (say, myModule), you can set generic styles for different regions:

.mod {
  color: #CCC;
}

.header .mod {
  text-align: center;
}

.content .primary .mod {
  border: 1px solid #000;
  color: #000;
}

.content .secondary #myModule {
  border-color: #CCC;
}

In this example, we create a base styling for all modules - simply set the font color to gray. All modules in the header are to have text-alignment centered. All modules in the primary content area will have a black border and font-color of black. The secondary content area (this could be styled as a right-hand column) will have a gray border - but only for the one myModule module.

View demo

Hacks

It's not uncommon for some browsers to misbehave or lack full CSS support. One example may be IE6's lack of support for the min-height property. Unintentionally, IE6 compensates for this by incorrectly treating the standard height property - it's treated the same as min-height in other browsers. In order to give all browsers the min-height property but not the height property, we will need to target IE6 specifically. This can be done a number of ways - however many methods have several drawbacks. The use of conditional commenting requires another HTTP request (a performance hit) and the special selectors require the use of an additional styling block. Although a matter of opinion, I believe the "star" and "underscore" hacks to be the best in terms of effectiveness, performance and maintainability.

#myModule {
  _height: 150px;
  min-height: 150px;
}

In this example, IE6 will read any declaration prefixed with an underscore - so the height: 150px will be applied only to IE6. As min-height is not understood by IE6, it will be ignored. For standards compliant browsers, they will not see the "height" declaration because of the malformed leading character, but will apply the min-height property correctly.

You may also effectively target IE7 and IE6 (together) using the "star hack":

#myModule {
  display: -moz-inline-stack;
  display: inline-block;
  *display: inline;
  zoom: 1;
}

This example will be expanded on later, but the important thing to note is the display property is being overwritten within the same selector - and the *display: inline is being applied only to IE6 and IE7, just as the previous example of _height: 150px was only being applied to IE6.

View demo

Things I wish I had known when I started

  • IE6's Broken Box Model

    IE6 has a broken box model when a page is not rendered in standards compliant mode. With this broken box model, the value of width is reduced to account for the width of any padding or border applied to the element. This bug has been a great point of ire for many developers. I suggest you ignore it entirely. Code your HTML correctly - with a doctype, thereby triggering standards compliant mode and overlook this bug entirely.

  • Global CSS Reset

    Different browser assign default values to properties differently. Thankfully, everyone's agreed that the default colors will be black on white - but there are still a number of properties that misbehave between browsers. One way of normalizing your pages is to include a "global reset". A simple method may be to apply:

    * {
      margin: 0;
      padding: 0;
    }
    

    Although this will fix many issues, it is not without it's faults. Some argue that there are performance implications to applying global styles like this. Some argue that it doesn't go far enough. There are many different global reset base stylesheets available. It's a good idea to find one you like, customize it as you see fit and reuse it regularly.

    View demo

  • hasLayout / Block Formatting Context

    If you have a node the contains only floating children, the height of that parent node will be reduced by the height of it's children. That's a mouth-full and pretty hard to grasp. Let's say you have something really simple:

    <ul>
      <li>List item</li>
      <li>Second list item</li>
      <li>Last list item</li>
    </ul>
    

    Just a standard unordered list. Now, if we wanted to style this list to have all the children floated - as is common with many (most?) navigations, you would think you would simply need to have this styling:

    #myModule ul li {
      float: left;
    }
    

    There - all the list items are floating left. Everything is good, right? Wrong. To clearly see the problem, we'll add a border to our unordered list (the parent node):

    #myModule ul {
      border: 1px solid #000;
    }
    

    Using this, you should see that the black border does not surround the list items as you may initially suspect. The "problem" is that the list-items are floated out of the standard document flow. The parent is no longer bound to that height - and the calculated height of the parent will disregard the height of any floating children. That is, to say, unless you trigger hasLayout and proper block formatting context. There are several ways of achieving this - however most have some unforeseen drawbacks. Most solutions will require you to have one method for IE - and another method for all other browsers. For IE, you will simply (mis)use the magical declaration of "zoom: 1;" - a fairly nonsensical declaration. Zoom is meant to allow the contents to be...zoomed (as you would suspect from the name). So, zooming to 1 (or 100%) is a redefinition of the implied - but it triggers hasLayout in Internet Explorer. It's also a never-used declaration that can be safely used without any detrimental cascading affects. So far, our HTML is (and will remain) the same. Our CSS now looks like:

    #myModule ul {
      border: 1px solid #000;
      zoom: 1;
    }
    
    #myModule ul li {
      float: left;
    }
    

    For other browsers, the best method (in my opinion) is the use of a CSS pseudo-selector :after. This selector will select the area after the given element. It's widely supported in most all modern browsers (excluding IE). We will use our parent with this pseudo-selector to add "content" after the element to insert an innocent piece of text. The use of content is controversial and I would only advise it's use in this context only. After all, you're using CSS to separate presentation from content anyway, right? This text we've inserted will then need to be made a clearing element by using clear: both and display: block. To prevent the content from actually displaying, we will both set the height: 0 and visibility: hidden. The end result will look like this:

    #myModule ul {
      border: 1px solid #000;
      zoom: 1;
    }
    
    #myModule ul:after {
      clear: both;
      content: '.';
      display: block;
      height: 0;
      visibility: hidden;
    }
    
    #myModule ul li {
      float: left;
    }
    

    As the declaration of all this is fairly long, I usually include one declaration at the bottom of my CSS file with many stacked selectors.

    If you're not a fan of all this extra CSS, you can often simply add overflow: hidden to shrink-wrap the parent container - but, as you'd expect, it can have some unforseen side-effects. For simple use, it is good to know and to use sparingly.

    View demo

  • Height in Percent

    Height can only be set in percentages if a parent element has an explicit height.

    View demo

  • Centering Block Level Elements

    Centering a block level element requires an explicit width to be set (as the default width of a block level element is 100% anyway) and a margin to be set:

    #myModule div {
      margin: 0 auto;
      width: 50%;
    }
    
  • IE6 and min-height

    Although min-height is not (correctly) supported in IE6, it can be widely used by using an underscore hack to make use of IE6's incorrect interpretation of the standard height declaration:

    #myModule div {
      _height: 250px;
      min-height: 250px;
    }
    

    IE6 will use the height value in exactly the same way other browsers will use the min-height. As we've used the underscore hack, other browsers will not see the height value - and in contrast, as IE6 does not understand a min-height declaration, it will be ignored in that browser.

  • IE6 and position: fixed

    IE6 does not understand the position property of fixed, but, depending on your design requirements, you may be able to make use of a suitable position: absolute alternative. Be aware, however, that the more logical fallback is not by default. As IE6 will not understand position: fixed, it will fall back to the default position of static.

    #myModule {
      bottom: 10px;
      position: fixed;
      _position: absolute;
      right: 10px;
    

    View demo

  • position: absolute

    position: absolute positions an element outside the standard document flow. If you give an explicit top, bottom, left or right value, it will be bound to that measure relative to it's next ancestor with position: absolute or relative. If no ancestor has a position declaration of absolute or relative, the element will be bound by that measure relative to the document itself. In short:

    #myModule {
      margin-top: 100px;
      position: relative;
    }
    
    #myModule img {
      top: 0;
    }
    

    All images will be at the very top of the myModule node, which lies 100px below any element above. However, simply setting:

    #myModule {
      margin-top: 100px;
    }
    
    #myModule img {
      top: 0;
    }
    

    The position, margin or any other property of the myModule node is of no consequence. Assuming no ancestor of the myModule node has a position value of absolute or relative, all images within the myModule node will appear at the very top of the current document.

  • display: inline-block

    The use of inline-block for a display declaration has always been hit and miss. With browsers being updated, many of these problems are a thing of the past - considering you're willing to make a few accommodations. For use in large lists (say, of products), you would have markup within a myModule node as:

    <ul>
      <li>
        <div>
          List item
        </div>
      </li>
      <li>
        <div>
          Second list item
        </div>
      </li>
      <li>
        <div>
          Last list item
        </div>
      </li>
    </ul>
    

    The extra divs are required for inline-block in cases that require a vertical-align: top baseline.

    And associated CSS as:

    #myModule li {
      border: 1px solid #CCC;
      display: -moz-inline-stack;
      display: inline-block;
      *display: inline;
      list-style: none;
      margin: .5em;
      vertical-align: top;
      width: 200px;
      zoom: 1;
    }
    

    The border and margin are just for demonstration - the use of list-style and width are nothing magical and only there to keep things styled appropriate. The real demonstration is in the use of display (first set to moz-inline-block for FF2 and below, then to inline-block for all compliant browsers, then for inline for IE). vertical-align is required to establish a correct base-line of alignment and, finally, zoom is added for IE to enable hasLayout.

    View demo

  • Logo Spriting

    It's very common for designers to request a stylized text or logo. Until web fonts become more popular, the most effective, semantic and accessible way of replacing a logo, icon or stylized text is by using a sprite (discussed later in more detail).

    <h1>My awesome company!</h1>
    

    A standard header or logo. We want to replace this boring text with a logo that looks way cooler:

    #myModule h1 {
      background: transparent url(../myCoolLogo.png) no-repeat 0 0;
      display: block;
      height: 50px;
      overflow: hidden;
      text-indent: -900px;
      width: 300px;
    }
    

    We assign a transparent background with an image (myCoolLogo.png), we don't repeat the logo and position it to the top left. We declare that the element is block (h1 is by default, but for demonstration purposes, I've made it explicit), set the height and width to that of the image. The overflow property is set in order that our reverse indented text will not be visible - thereby preventing it from obscuring our new logo image.

  • Clickable Overlays

    Once you learn the power of absolute positioning, you may be tempted to create an anchor tag that enables a large clickable area - perhaps an entire area of a block element. As it is not valid HTML to have block level elements within an anchor tag (inline element), you can position the anchor to achieve a similar effect:

    <div>
      <h4>Video of my dog</h4>
      <p>
        This is a video of my dog playing.
      </p>
      <a href="#" title="Watch video of my dog playing">Watch video of my dog playing</a>
    </div>
    

    This markup makes reasonable semantic sense: a title, description and link to an item (video, product, etc). You can then, with CSS, have the anchor be non-visible, but contain all this content:

    #myModule div {
      height: 250px;
      position: relative;
    }
    
    #myModule div a {
      height: 100%;
      left: 0;
      overflow: hidden;
      position: absolute;
      text-indent: -900px;
      top: 0;
      width: 100%;
    }
    

    Similar to our spriting example above, we're pushing all the content of the anchor tag off-stage. However, IE has a special little problem with this. As no content is on-stage, the content below it will "bleed through", thereby making the link ineffective in some areas (and often flickers). To get around this, the most effective method I've found is simply trick IE into believing there's a background to the anchor - which seems to convince it that it is then clickable. This CSS works as expected, but requires the addition of a nonsensical background-image:

    #myModule div {
      height: 250px;
      position: relative;
    }
    
    #myModule div a {
      *background-image: url(#);
      height: 100%;
      left: 0;
      overflow: hidden;
      position: absolute;
      text-indent: -900px;
      top: 0;
      width: 100%;
    }
    

    View demo

  • Vendor Specific CSS

    Browser makers have adopted a way of integrating newer CSS declarations in a safe and reasonable method. This, however, means that for each of these special cases, you must declare this special new declaration to support each browser vendor. These declarations include standardized prefixes to match that browser vendor, including "-moz", "-webkit" and "-o" for Mozilla, Webkit and Opera respectively. In most cases, these proprietary prefixed declarations are practically identical to their pure declaration counterparts. An example of their use can be seen in an example of applying rounded corners:

    #myModule {
      border: 1px solid #000;
      -moz-border-radius: 1em;
      -webkit-border-radius: 1em;
      border-radius: 1em;
    }
    

    A 1px black border is applied to the myModule node, for all browsers. If a browser supports the native border-radius property, it is applied. For older versions of Mozilla that do not support this native declaration (FF2), it is applied using the proprietary prefix - and finally, the proprietary prefix is applied for older version of Webkit.

    In the "bad old days" of the web, it was common to have to target specific browsers in order to achieve the aesthetic experience you wanted. On the surface, this proprietary prefixed method echos that experience - but with one major difference: the proprietary prefixes are applied only out of necessity - with the expressed intent of deprecating them once the pure declaration counterparts are either fully integrated or the specification is solidified. In short: use this liberally. They are not hacks. If we do not slowly evolve the Web in this way, it will be a very long and boring process.

  • 8-bit PNG Use and IE6

    IE6 does not support alpha transparency in PNG images. It also does not support transparency in 24 bit PNG images. That being said, you can use 8 bit PNG images that do not have alpha transparency (meaning a pixel is either completely transparent or completely visible - but not 30% alpha). This effectively opens the option to use a transparent PNG image with limited colors. These features and limitations match those of GIF images, but have the added benefit of having better compression (most of the time). When creating sprites or images that do not require alpha transparency, PNG 8 is your best option.

  • PNG-24 Filter Use and IE6

    If you must make use of alpha transparency or more colors that PNG 8 provides, you still have a few options. One, you may create faux transparency using a standard JPG image - integrating the background into the image, giving the appearance of transparency. This is often too restrictive for designs. Another option is to create a 24 bit PNG as a background image for all modern browsers - and degrade the experience for IE6 to use the lower quality 8 bit PNG:

    #myModule {
      background: transparent url(../myBack.png) no-repeat 0 0;
      _background-image: url(../myBack-ie.png);
    }
    

    Another option is using the filter declaration (an IE specific CSS extension) to have IE6 render the background image appropriately. Be aware that this method incurs a performance hit for each rendered filter images - regardless if the image is simply re-used. For each instance, it will calculate the appropriate alpha. This may not be significant for few images, but it should be considered if widely used:

    #myModule {
      background: transparent url(../myBack.png) no-repeat 0 0;
      _filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='../myBack.png');
    }
    
  • IE6's 4096 Selector Limit

    If you combohandle your CSS files (discussed later), you may find that your resulting CSS file may have many selectors. For larger sites, hitting the "4096 selector limit" in IE6 is quite possible. IE6 will only read the first 4096 selectors in a single CSS file. After this limit has been reached, additional CSS declarations will be entirely ignored.

  • Quick CSS Comments

    CSS supports C-style commenting:

    #myModule {
      /* background: red; */
    }
    

    In this example, we wanted to comment out debugging CSS. However, we were required to put in at least four characters - on different ends of the declaration. It would be much more convenient if we could make use of C++ style commenting, but it is not supported in all browsers. An alternative is to simply break your CSS declaration with a preceeding character:

    #myModule {
      xbackground: red;
    }
    

    As the declaration of xbackground is unknown, it will be ignored - effectively "commented out". This, of course, will prevent your CSS from validating - but it makes for quicker testing than typing out the full comment, and can be removed before your code is released.

    View demo

Style

Beyond simply writing code that's machine readable, it's a developer's responsibility to make sure that the code is written to be easily read by humans. This makes editing of your CSS much easier - especially if it's someone else that has to do the editing. Throughout the demonstrations, you may have noticed some standard conventions. Here are some basic rules that I follow and I find to be very helpful in maintaining clean and consistent code:

  • Have a new line for each selector.

  • An open curly brace should appear on the same line as the last selector - the closing curly brace should be on it's own line.

  • Declarations should be indented two spaces and one per line. For debugging purposes, I'll often create temporary declarations and not indent it. This way, the declaration stands out and it can be removed.

  • Alphabetize your declarations. When editing CSS, it makes things much quicker to find. You will know that backgrounds will be toward the top and widths toward the bottom. Others like to group the declarations, but that leaves far too much room for interpretation and is too difficult to notice for someone new to your system. Any code standard too complicated to be understood easily is doomed to be ignored and abandoned. Everyone can understand alphabetizing.

  • Don't use !important. It can cause confusion and start a specificity "war of escalation" between different selectors. It's better just left alone.

  • Don't worry about validating your CSS - but any time you break spec, be sure you understand exactly why. Validators may complain of some of the aforementioned proprietary bits or hacks. Save yourself some sleep and skip validation.

  • For short-hand friendly measures (such as padding or margin), I find it easier and more readable to never use the three-value shorthand. As margin: 10px 0 0; leaves ambiguity to novice developers - and gives pause, even to seasoned veterans, it's often a matter of convenience to opt for margin: 10px 0 0 0; for the explicit clarity.

For more details, refer to the coding convention.

Performance

One of the great benefits of CSS is it's performance. However, if used incorrectly, you can undermine or entirely negate most of these benefits. To best use CSS in a performance conscious way you should be focused on reducing byte-weight, reducing the number of connections (file references) used and reusing assets, as they will be cached on the viewing client. Examples of these principles include:

  • Put all your CSS in an externally referenced file. This file will be cached by the client - so putting the burden of weight on this will create only a single point of delay, as opposed to every page load as would be the case with inline only styles.

  • Avoid conditional commenting. This method of targeting different versions of IE requires an additional connection. If the stare and underscore hacks are used properly, the need for conditional commenting will be resolved.

  • Combohandling is the practice of serving all CSS from one file reference. Many enterprise sites require many CSS files, but are combined into one file for delivery to the client. This can greatly reduce the number of connections required to render the page. The fewer CSS files, the faster the load time.

  • Minifying is the practice of creating terse, machine-readable code out of well documented and human readable code. Many code minifiers are available - they will allow a developer to create CSS that is very readable and maintainable and "smush" the code into the smallest package possible by removing needless whitespace, removing comments and any other non-critical bytes. Other languages, such as JS, are also an excellent candidate for minification.

  • PNG images are well suited for the Web (but note the bit IE6 caveat). However, many image editing programs will attach meta data to the resulting PNG images increasing their byte-weight. Several "crushing" programs are available to remove this extra data from the PNG image, thereby reducing this (often) needlessly excessive byte-weight. Pngcrush is a popular, open source tool: http://pmt.sourceforge.net/pngcrush/.

  • Use shorthand CSS when possible. Instead of applying:

    #myModule {
      margin-bottom: 0;
      margin-left: 0;
      margin-right: 0;
      margin-top: 10px;
    }
    

    You can apply the same style simply with:

    #myModule {
      margin: 10px 0 0 0;
    }
    

    Mentioned previously, you should avoid using shorthand that leaves ambiguity for new developers - in the case of setting a margin: 10px 0 0; - instead, opt for the more verbose margin: 10px 0 0 0; and let the minifier make the 2-byte savings for you.

  • Spriting is the idea of using one large image and positioning it as a background image to select the viewing area desired. By using CSS sprites, you reduce the connections required to download each image. Spriting should be used liberally. It's suggested that you create sprite images with a logical grouping - say, one for "icons", one for "logos", one for "headings", etc.

    Use whitespace liberally within sprites. For PNG images (the file type of choice for sprites), transparent pixels are nearly free of any byte-weight. Instead of clumping icons together, space them out horizontally - one icon for every 100px (for example). When positioning sprites, you then only need to know which number it is in the sequence instead of manually using trial and error to position it correctly. Some tests have shown that PNG is ever so slightly more optimized for horizontal images as opposed to vertical images (a 2000px by 25px set may, theoretically, be more optimized than a 25px by 2000px image of the same icons).

    View demo

  • Don't be afraid to use techniques such as RGBA background colors, border-radius, box-shadow, etc if they will greatly reduce your workload, required connections and byte-weight. Often, shadows, rounded corners and transparencies are "nice to have", but rarely an aesthetic necessity for a page. Consider having a gracefully degrated experience for less capable browsers and save the time, effort and download speeds of sliding doors images, CSS and extra markup.

  • Use a Content Delivery Network (CDN) if possible. For enterprise development, this becomes much more important. If you're making a smaller site, the expense of this service is often unreasonable considering it's relatively minor benefits. However, CDNs are optimized to quickly deliver large assets (images, video, etc) and remove the burden from your own sever for the delivery of these static assets.

  • Use as few domain names as possible, thereby reducing the number of times the client will require a DNS lookup of a domain.

  • Use the IE proprietary filter property sparingly, as it requires the browser to do fairly heavy calculations for every use - even if it's the same selector. For each instance of it's display, it will run these calculations. For simple use as a header image, for example, only displayed once per page - it's not a concern. But used heavily, it can bring IE to a crawl.

  • Declare your CSS early in the document. I find it convenient to declare CSS files at the bottom of my document <head>. As this will download (and cache) the CSS file on the client, the page will be rendered as it should - instead of an unstyled page being rendered, then styled.

Debugging

The art of debugging requires knowledge of tools, understanding of the underlying technology, a fair bit of opinion and a lot of "this works for me". I won't go into detail about each developer tool, but instead offer insight as to my favorite practices that have served me well for debugging my own code.

There are, however, a number of "defacto standards" for developer tools among different browsers:

Firebug
Firefox
http://getfirebug.com/
Web Inspector
Safari and Google Chrome
http://webkit.org/blog/197/web-inspector-redesign/
Dragonfly
Opera
http://www.opera.com/dragonfly/
Developer Toolbar
IE
http://www.microsoft.com/downloads/details.aspx?familyid=e59c3964-672d-4511-bb3e-2d5e1db91038
Firebug Lite
All major browsers
http://getfirebug.com/firebuglite

One common mistake for developers is that they are editing the wrong file. If you make a CSS change and it's not being reflected, try adding:

body * { border: 1px solid red; }

It only takes a moment and you may be surprised at how many times you're just working on the wrong thing.

If your site is behaving strangely, reduce the number of steps it takes to get to the user. If you're making use of minifying or combohandling, disable them to see if it resolves your problem. Although this shouldn't be a permanent fix, you may find it helpful in troubleshooting.

If you find a problem that you are convinced is a problem with a specific browser - prove it. Distill your problem to the very basics - the fewest lines of code possible to demonstrate the core of the problem. In most cases, you may find that you simply made a mistake - or have rediscovered a well documented bug (in which case - you've learned something new). In some cases, you may find a bug that isn't well known. In this case, you've just created an excellent test-case to submit to the browser maker to help fix the problem in a later version. If nothing else, your distilled example becomes a test-case for yourself others. Browsers are far from perfect - and many browser makers are receptive to these demonstrations.

Looking ahead

With the introduction of CSS3, there are many new and exciting things on the horizon. Many were not covered as they are not of fully practical use just yet - however, all developers should be aware of (and excited for) the introduction of new CSS3 pseudo selectors (attribute, sibling (next and all), nth child, first-line, first-letter, child, etc) and declarations (text-shadow, box-shadow, border-radius, web fonts, multiple background, border images, etc).

Another subject of further exploration may be media queries - which give you the ability to serve CSS to specialized devices - such as printers, handheld devices and others.

If you've gotten this far and are still interested in learning CSS, there's nothing better than doing it yourself. Learning by doing - and forming your own opinions is one of the most valuable and rewarding ways of learning.

Creative Commons Attributionion 3.0 License

This page is licensed under a Creative Commons Attribution 3.0 License