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.
-
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.
-
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.
-
Use Semantic Markup
Know and use the built-in HTML elements. Don't be enticed into using div's and span's everywhere. If an element is body text, use a <p> tag instead of the more awkward <div class="bodytext"> that's all too common. Here's a few you might not know about:
- abbr
- acronym
- address
- blockquote
- caption (for tables)
- cite
- code
- dl, dt, dd
- dfn
- kbd
- q
-
Use tables for tables
The anti-table religion is as ridiculous and costly. A few simple tables will be faster and easier to understand than <div>s nested four levels deep.
-
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.
-
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.
-
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. -
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.
-
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.
-
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.
-
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.
-
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.
-
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.
-
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
-
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.
-
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.
-
Font-sizing Strategy
Pick the right font-sizing strategy for your site. If you're flexible, use the small, medium, large model. If you need pixel level control, spend some time researching the choices-- they all present some difficult trade-offs. See Font Sizing Strategy
-
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
- Brookgroup CSS Best Practices - some complementary ideas
- Mezzoblue's contribution needs heavy editing