NDP Software 

Compelling Software • Design & Construction

CSS Best Practices

CSS doesn't get the love it deserves. But it's what allows us to have beautiful, post-1994 web pages.

It grows organically and is often a developer's secondary responsibility or priority. It's an all too common appearance to see a developer futzing around with a stylesheet, typing seemingly random characters to get a selector to "select".

Even though it gets less attention than the "code" (Java, C++, etc.), it's a level harder to manage. The cascade makes it complex: CSS rules come from multiple files (or are built into the browser chrome), and there is no "compile-time" or run-time checking. When they don't work, things just don't draw right. It's easy to miss for a week or month. At least Javascript-- notoriously hard to debug-- generates an error message.

I've collected success stories from my own experiences, colleagues, and the web. This is what I call "CSS Best Practices". I've put them in a rough order, based on the ones that I think are most valuable first.

It's a work in progress, but I trust it will be valuable. If you have additions or corrections, please let me know via email.

CSS-Specific Guidelines

  1. Undo CSS

    It's become fairly standard practice to use a style sheet that removes all the browser's default stylings. This removes all the browser-specific default padding, font sizing, etc. Pioneer Tantek Celik called this undo html, but it's more often called reset.css, based on its appearance in YUI.

    This is the biggest time saver you can adopt. Starting with this will keep you from creating all sorts of cross-browser inconsistencies.

    There's the not-so-well known notion of how to "get it back". YUI has one called CSS Base. I have found it's useful to have a little more control, so I'm created a class I can put on any element. (I call it html.) Its behavior is to re-style HTML elements inside it. Mine looks like:

    .html :link,.html :visited { text-decoration:underline; color: blue }
    .html h1 { font-size: x-large }    	
    .html h2 { font-size: large }    	
    ...    
        	
    This needn't be complete, but it's useful very early on in page mock up, and when you're displaying someone else's HTML.
  2. Pick an appropriate font sizing approach and consistent measurement units

    Pick the right font-sizing strategy for your site. If the design is flexible, use the simple font-size: small, medium, large model. This avoids the pitfalls of the other schemes. If the design needs pixel-level control, spend some time researching the choices-- they all present some difficult trade-offs.

    The same goes for all your other measurement units. Decide what you need, and stick too it. For example, decide from the start to only use ems. This may not be the ideal technique, but you will get advantages because you have a consistent, understandable system.

    For larger graphically demanding sites, consider YUI's approach and implementation. It's complicated to get used to, but will provide reliable, cross-browser control.

    Mostly, though, it's important to be consistent, so gather the team together, debate for a few minutes, and then pick a scheme that everyone can follow. Font Sizing Strategy

  3. XML

    If possible, use XHTML for your HTML. This will guarantee that elements are closed and properly nested. This will reduce your debugging burden significantly. If this isn't possible, see what incremental steps you can take towards this ideal.

  4. Use semantic markup

    Things have gone a long way since the blink tag. Know and use the built-in HTML elements. Don't be bullied into using div's and span's everywhere-- that's less readable than older alternatives. For example, if an element is body text, simply use a <p> tag. Avoiding it leads to the all-too-common <div class="bodytext">. Here's a few you might not know about:

    • abbr
    • acronym
    • address
    • blockquote
    • caption (for tables)
    • cite
    • code
    • dl, dt, dd
    • dfn
    • kbd
    • q

  5. Use tables for tables

    Don't be afraid of using tables when appropriate. A few simple tables will be faster and easier to understand than <div>s nested four levels deep.

  6. Add elements to support styling

    Although having clean, semantic markup is important, don't be afraid to add elements to facilitate styling later on. For an example of this, see Stretchy/Fixed Page Layout Experiment

  7. Add classes and IDs at the highest level possible

    Use an enclosing style or class when possible. For example:

    <ul>
    <li class="album">Let It Be</li>
    <li class="album">Abbey Road</li>
    </li>
    
    is more concisely expressed as:
    <ul  class="albums">
    <li>Let It Be</li>
    <li>Abbey Road</li>
    </ul>
    
    This may seem obvious, but quite common. There are more complex cases, that are harder to see. Basically, try to determine the meaningful outside element, and write selectors in terms of that.

  8. Identify "outermost" elements

    Any modular piece of HTML code, whether in a JSP file, tag file, PHP function or whatever, should have a class (or id) on the outermost element. Name the outermost class identical to the function that creates it. Write the CSS relative to this. For example,

    function writeSideNav() {
    	<php 
    	<ul class="SideNav">
    		<li>Menu 1</li>
    		<li>Menu 2</li>
    		<li>Menu 3</li>
    	</ul>
    	?>
    }
    

    Write the CSS in terms of that outermost selector, in this case .SideNav and put it in a separate file called SideNav.css:

    ul.SideNav {
    	margin: 0;
    	border: 1px solid red;
    	padding: 5px;
    	display: block;
    	width: 800px;
    }
    ul.SideNav li {
    	background-color: green;
    	color: white;
    }
    ul.SideNav li a:link {
    	background-color: yellow;
    	color: black;
    }
    	etc.
    

    This tends to reduce the number of classes needed, and keeps the coupling between selectors low.

    As a corollary to this, I recommend you put a class (or id) on the <body> tag. (I'd put class="home" in home.html. This will allow you to add page-specific behavior easy, and clearly, when the time comes.

  9. Modularize geographically

    Organize and break up the CSS using the same principles you use to organize the other code and markup. For example, if you are using JSPs and you create a tag file for the footer called footer.tag, place corresponding CSS rules in footer.css, and place that file in a path identical to the tag file.

    This leads you to modularize CSS based on geographical areas of the page. This is a simple and understandable organizing principal. It also facilitates debugging-- there's one very clear place to look for selectors.

    Unless trivial, it's best to create a css file for each page with custom selectors. For a page with its own styles, an embedded <style> element may be fine. Just remember that if the CSS is in a separate file, the browser can cache, which may be important.

    The only variation on this "geographical" organization is forms. If you have a consistent form styling, it seems reasonable create a form.css to encapsulate it.

  10. Order the rules within the file based on the order of the markup.

    If the first element of the page is an <h1>, put its selector first, and so on. As you're switching back and forth between the two files, it'll be easier to match up selectors and tags.

  11. Echo the markup with the selectors.

    If the markup looks like this:

    <div class="nav">
    	<ul class="company">
    		<li><a href="#">About us</a></li>
    		<li><a href="#">Jobs</a></li>
    	</ul>
    	<ul class="products">
    		<li><a href="#">Enterprise</a></li>
    		<li><a href="#">Small Business</a></li>
    		<li><a href="#">Free Stuff!</a></li>
    	</ul>
    </div>

    having the selectors echo the markup makes the intention clear:

    .nav {
    	...
    }
    .nav ul.company {
    	background-color: magenta;
    	...
    }
    .nav ul.products {
    	background-color: magenta;
    	...
    }
    .nav ul.company li a:link { 
    	...
    }
    	

    The last selector probably could be shortened to .company a:link. But this doesn't add any value, and makes the file harder to scan.

    I think this is great, but not everyone agrees.

General Development Guidelines

  1. Naming

    Create a naming scheme. People use camelCase, lowercasenames, hyphens-between-words, and even underlines_to_separate_words. Pick one. If you don't, at some point you'll waste a few frustrating minutes trying to figure out why your selector doesn't match, only to realize it was the same words but different formatting.

  2. Learn the full language

    Understand the full range of selectors, and understand the values that are allowed. With some technologies you can muddle around with partial understanding, picking it up as you go along. You can do that with CSS, but it's not a good method. The reason is this: the most common problem is not seeing a rule work, but it's fairly easy to reach a solution without knowing why. It didn't work because the selector didn't match, it was overridden by some more specific rule, or some other error in the rule caused the processing to stop. If you don't know the language well, it's difficult to guess what's wrong. It's not the only way, but typically people end up adding more rules than they need just to get something working-- until they have a mess.

  3. Use tools

    Firebug now has excellent CSS debugging, and the Web Developer's Extension has a cool "Show Element Information" that I use all the time.

  4. Work towards separate, modular files

    Begin the development process with inline styles and move towards separate (modular) files by the end of the project. This is quite common.

  5. Avoid a global CSS file

    This is the converse of the above practice, but a common problem. From my experience, most projects start with a single CSS file. That file quickly becomes huge, with numerous dependencies between all the selectors. The task of breaking up this file will be daunting (much worse than refactoring code). I've just had to "live with it" more than once. It's better to start modularly and combine later if it becomes a problem.

  6. Clean up occasionally

    (I think my mom said that.)

    Get the project set up so you can quickly do global searches for styles, and remove the old ones. I've never met a large CSS file that didn't have at least one unused style.

  7. Keep it simple

    This is the hardest thing to do. When you find yourself creating selectors to undo your own styles, it's time to refactor. For example, if you defined

    li { display: inline }
    

    in your main stylesheet, but then in a sidebar, you need to override it:

    div.sidebar li { display: block; border-left: 3px solid #00f; ... }
    

    and then somewhere else you do something else. It's probably better not to undo the initial style by changing the default li. It's just causing confusion.

  8. Save optimization until the end

    Here are a few techniques:

    If you're not using it, add gzip compression to your web server (like Apache). This will significantly reduce the transfer size of the CSS files, low-risk and easy. (This will compress HTML and Javascript files as well; I recall a 30% bandwidth savings in one case.)

    There are various scripts out there that "compress" CSS files. I have used them, and they work. That being said, I don't really like the idea: you'll be deploying something different than what you are developing with. (No, don't try developing with compressed CSS files.)

    In the same vein, you can combine numerous CSS files into one, reducing the number of HTTP requests the browser will have to make. I question the efficacy of this, but have seen it mentioned several places.

    You can generally wait until late in the development process. That being said, there are a few things you can do from the beginning. These will reduce file size, but can also increase readability:

    • Group selectors that have the same style. This is fairly standard practice, and I haven't seen people argue against this.
    • And it's okay to use the shorthand styles. They are cryptic and can be mis-read and mis-typed, to an experienced developer they will be easier to read.

    If you choose to adopt these techniques, adopt them universally.

References

  1. Brookgroup CSS Best Practices - some complementary ideas
  2. Mezzoblue's contribution needs heavy editing