I recently did a knowledge share at work on how to write efficient CSS, and one of my colleagues – a senior developer with a lot of experience – said "I felt the penny drop". High praise indeed, so I thought it was worth reproducing it here.
The write-up is pretty long, so I've divided the divs into Basic CSS in which I cover:
and Evaluation of CSS in which I cover how CSS is evaluated and some things we can do to make it more efficient.The key information is these two points:
The style system breaks rules up into four categories by key selector.
The CSS engine then takes each html element in the document in turn. If it has an ID, then the engine searches through the style rules and checks rules that match that element's ID. If it has a class, only Class Rules for a class found on the element will be checked. Only Tag Rules that match the tag will be checked. Universal Rules will always be checked.
So in the example button#backButton { ... } the key is the id "backButton". In the example I give in Basic CSS the key is the class "blog-post".
The engine starts with the key and then evaluates the rule from right to left. So if you have a button with an id of "backButton", the engine first matches the id to the id and then compares the next selector – is the element a button? In the example from Basic CSS the evaluation for the second selector, ul#nav.dhtml li a.blog-post, is as follows. Does the element have a class of blog-post? If so, is it a link? If so, is there anywhere in its ancestry a list item? If so, is there anywhere in the ancestry of that list item an unordered list element with a class of dhtml and an id of nav?
You may be getting a slight clue now as to why I think that selector is inefficient.There are some obvious ways we can start here.
These are just recommendations as to how to write the CSS. If you care about your website's performance you should already be minimising and gzipping your CSS. So how much of an edge will these recommendations give you?
CSS and performance is a fairly hot topic right now, especially with all the cool things that you can do using CSS3. Dave Hyatt, architect for Safari and Webkit, said "The sad truth about CSS3 selectors is that they really shouldn't be used at all if you care about page performance." (The comment can be found here).
That's certainly something I've heard, for example at conferences. However, another web giant, Steve Souders (works at Google on web performance) has a different opinion. It's worth reading the piece in full (there are charts and everything!), but the takeaway here is: "On further investigation, I'm not so sure that it's worth the time to make CSS selectors more efficient. I'll go even farther and say I don't think anyone would notice if we woke up tomorrow and every web page's CSS selectors were magically optimized."
So why am I bothering with this? Well, a few reasons. One is I think it's always worth taking the time to find out how things work and I'm glad to be able to make a reasoned judgment on this.
But also, there are various things to consider when thinking about writing efficient CSS. There is performance, obviously, but two other major concerns are ease of writing and (in my view more important) ease of reading. I love clean code, and I think it's crucial that code is easy for other developers to read. I'm not sure I care whether or not ul#nav.dhtml li a.blog-post is performant or not, it's certainly not clean, and it took me some brow furrowing to work out when it would apply. So personally, I'm going to follow the rules I've outlined above. What do you think? I'd love to hear your opinion.
I gleaned most of the information about how the CSS engine works from Page Speed documentation and the Mozilla blog. I couldn't find any information about how IE evaluates CSS, please do let me know if you have any.
To take things further with writing nice CSS, you could look at Compass/SASS, or my current favourite, OOCSS.
If you’d like to be notified when I publish a new post, and possibly receive occasional announcements, sign up to my mailing list: