Why you should deeply nest your CSS selectors.
February 12th, 2010
Most developers, even experienced developers, don’t understand the value of deeply nesting CSS selectors. Although writing the same selectors over and over might seem redundant, it’s worth the extra effort. Your code will be better organized, easier to read, and more maintainable.
What do you mean by deeply nested selectors?
Most of us learn to write CSS using shallow selectors. Essentially, you give an element a hook (class or id) and use it to write some style:
#our_team { height: 230px; margin: 60px 0 0 0; }
.employee { float: left; margin: 0 0 0 10px; }
.name { text-decoration: underline; }
.position { color: #add036; font-size: 10px; }
But you can also trace each selector back to its common ancestor element:
#our_team { height: 230px; margin: 60px 0 0 0; }
#our_team .employee { width: 230px; height: 65px; float: left; }
#our_team .employee a img { float: left; border: thin solid #5F6568; }
#our_team .employee p { float: left; margin: 0 0 0 10px; }
#our_team .employee p span { display: block; margin: 0; }
#our_team .employee p a span.name { line-height: 16px; font-size: 14px; }
#our_team .employee p a:hover span.name { text-decoration: underline; }
#our_team .employee p span.position { color: #add036; font-size: 10px; }
And why is this better?
1 It’s easy to quickly trace a given selector back to its parent element.
In the above example, we know at a glance that .position refers to an employee’s position and not something else (e.g. a utility class that adjusts an element’s position). This keeps things more explicit and thus less confusing.
2 It divides your code into distinctive visual blocks.
Take the above example, not only can you quickly identify all the selectors that belong to #our_team but you can also see how the style cascades through the element. This is especially useful when your stylesheet grows past a few hundred lines.
3 Your rules won’t inadvertently override each other.
Because all selectors trace back to a unique ancestor element, you can be sure that everything nested within that selector will only effect its intended target. This means you can use common class names (e.g. .first, .title, .column, etc.) with confidence that you won’t be breaking the styling elsewhere on your site.
4 You get the benefits of cascading without the headaches.
When using shallow selectors, you often rely on the order that selectors appear in your stylesheet. For example, if you have two selectors of the same value, priority will be given to whichever selector appears last in your stylesheet. That’s fine except when rules start cascading in unintended ways. Then rules become unresponsive because some other selector, four hundred lines lower, is running the show.
But deeply nested selectors don’t have this problem. Because everything traces back to a unique selector, you can be sure your rules will stick no matter where they appear in the stylesheet. And, of course, you can still enjoy the benefits of cascading within the scope of a unique ancestor.
5 You can safely use @import to organize your stylesheets.
Stylesheets can get really long and most of us have devised clever means of trying to keep things organized (e.g. headers, sub-sections, comments, etc.). But the best strategy is to divide your stylesheet into logical chunks, save those chunks as separate files, and include everything (as needed) into a few master stylesheets using the @import directive. This is easier on your server (since you’re only linking to a few stylesheets) and on your brain (since you’re not scanning through a stylesheet that’s 3000 lines long).
But this strategy is problematic since all @import directives must be placed at the very top of your stylesheet. And, because the normal rules of cascading still apply, you can run into inheritance problems pretty quickly if you try to do something like this:
@import url("reset.css");
@import url("header.css");
@import url("footer.css");
/* ...a bunch of selectors that will likely over-
ride what you're trying to do in footer.css... */
But if your selectors are deeply nested you can use @import with impunity. So long as your selectors trace back to a unique ancestor the order of inclusion makes no difference. You can import footer.css before reset.css and it’s not going to do any harm.
This means you can use @import the way would a php include() statement or a rails :partial in a template. In other words, you can really break things up and keep your stylesheets super organized:
@import url("reset.css");
@import url("utilities.css");
@import url("typography.css");
@import url("page_structure.css");
@import url("header.css");
@import url("main_nav.css");
@import url("footer.css");
/* ...the specific style for each individual page
with all the shared styles neatly partitioned into
separate files... */
Hope this helps get those stylesheets under control.
Tags: Best-Practices, CSS
I’d be really interested to see a speed test to see which is more efficient. Nice article.
Reply
February 22nd, 2010
Matthew ChandlerI have a question actually. Don’t you think if we import too many external stylesheet (like the last example), it will takes more loading time ??
just curious.
btw, it’s good article with well explanation by you.
Thanks
Reply
February 22nd, 2010
TaufikIt seems that if you take this approach you would sacrifice some speed. I do like how it is convenient to trace back a style though. Google writes about this in their page speed site: http://code.google.com/speed/page-speed/docs/rendering.html#UseEfficientCSSSelectors
Reply
February 22nd, 2010
Mike HendersonI don’t know if it makes a noticeable difference anymore with today’s fairly speedy browsers, but this method slows down the rendering process. CSS reads selectors from right to left — NOT left to right — so the more stuff you put in there the more it has to check.
You also shouldn’t import CSS files like that as each one is an HTTP request and is thus quite slow.
Reply
February 22nd, 2010
kylegetsspamThis is great info. However, I read that link is faster than @import. Thanks for the tips!
Reply
February 22nd, 2010
ChrisInteresting, although…
Using “deeply nested selectors” violates Google’s “use efficient CSS selectors” page speed suggestion (Performance benefits debatable).
And don’t use “@import” as it blocks parallel downloads.
Reply
February 22nd, 2010
Andrew Church“Your code will be better organized, easier to read, and more maintainable.”
Indeed — your deeply nested markup is lovely to behold. Unfortunately the true quality test of CSS (or any markup) is not how nice it is to look at in code-view, but how well it renders, and redundant markup = needless extra characters and lines = poor web performance.
We’re entering a web world where page-load speed is an increasingly important factor in web indexing (Google has said that they are moving toward page-speed as their primary ranking method), and as such, optimized markup becomes a crucial component of SEO best practices. There are other ways of organizing CSS markup to make it easier to read/maintain without adding bulk to your stylesheets; you’re better of focusing on the end-product of your markup than on how it looks pre-implementation..
Reply
February 22nd, 2010
Sara WThanks for all the feedback:
Concerning slower page loads, those are good points. This method will cause a slightly lower page load, as you’re probably having to import more stylesheets. Also, if Google doesn’t like this, I suppose it could hurt your SEO as well. While these detriments would be very slight, that’s no reason to disregard them.
With that in mind, you should be using something to combine/minify your stylesheets prior to deployment. This means in production, you’ll link to one massive stylesheet – with no @import directives. Consequently, your stylesheets are optimized regardless of how organize them.
Oddly, I often hear people talk about the importance of condensing/minifying javascripts and stylesheets but I rarely see it done. This leads me to believe the benefit is slight. But the truth is I’m not sure. If anyone could share a solid link, I’d definitely be interested.
Also, I write my CSS with SASS which dynamically combines all my @import directives into a single master stylesheet at run time. I recommend checking it out, as it saves you the trouble have having to condense your stylesheets before deploying.
Reply
February 22nd, 2010
Cory SchiresWhat the article doesn’t point out, is that deeply nested css should be used only after shallow css has been used to define the base/shared/common properties for page elements. Deeply nested rules can then be applied to affect and target specific elements. It has to be used responsibly, or the performance concerns raised will be an issue.
It does, without a doubt, lend more to readability and maintainability than development with only shallow css. Despite Sara W’s comments, a 500 ms gain in rendering time, for example, does not usually win out over the time and cost of development with poorly structured and/or poorly organized css.
I demand excellent organization in all facets of development from my developers, and the combination of shallow + deep nesting is a fantastic technique.
Reply
February 22nd, 2010
Andrew PowellI used to code this way, but sometimes it becomes less maintenable when you use really deep nesting, unless you actually need it so rules aren’t overrided.
I think that if a class exists and it’s used only once within a containing div, just use the containing div and and that class.
Also a good example is for lists. Instead of doing this:
ul li a { styles... }
It’s better to do this:
ul a { styles... }
That is, if only one link type exists in that list. Otherwise use classes, and use the same technique.
Reply
February 22nd, 2010
NameDeeply nested CSS selectors is bad for rendering process in browser.
Reply
February 22nd, 2010
zolotoyOn other hand, you could think css in an object oriented way : making them reusable in different contexts and build a lighter code. Nicole Sullivan’s initiative named OOCSS ( http://oocss.org/ ) is a good sample.
Reply
February 23rd, 2010
gaspardI am also a fan of CSS deep linking,
I wonder what you think of indenting your css file? it help a lot for readability.
Reply
February 25th, 2010
Cedric DugasI use deeply nested css selector quite often, but the speed test for firefox says that this rules are inefficent and should be shorter.
any thoughts?
Reply
February 26th, 2010
FedericoFederico:
Yes. Most browsers do render deeply nested selectors more slowly. The reason is simple enough. Time is wasted reading through superfluous selectors before getting to the last few relevant selectors. But how much does this matter?
Well, it depends on your preference. I really like the flexibility and modularity that comes with deep nesting, and I’m willing to trade a small amount of performance for those gains. But if you’re a geek for optimization, on the other hand, then this method is probably not for you.
That said, I think developers should be careful about optimizing performance to absurd extremes. There are plenty of other factors that go into writing good code – stability, clarity, flexibility, etc. And often these other factors come only at the price of speed.
For example, would you write a web application in C? Of course not. You’d use a higher level language, like Java or Ruby. No one cares that C is about four times faster than Ruby or Java. We’re happy to trade performance for the many benefits of developing in a higher level language.
It’s simplistic to think about speed as the ultimate measure of good code. It’s much better to think of it like a currency. Speed is useful because we can choose to trade it for other gains – tightened security, new features, usability, etc.
Reply
February 26th, 2010
Cory Schiresit’s not always practical. you may find yourself creating duplicating custom classes in practice.
for example, what if you need another class that has the exact same styling as
#our_team .employee { width: 230px; height: 65px; float: left; }
but inside of another parent id called “#their_team ?”
do you create another whole nested classes for “#their_team?”
Reply
March 2nd, 2010
JinJin,
I agree. Of course, you wouldn’t want to write duplicate selectors. It’s not only bad for performance but also for code maintainability. In the example you described, I would abstract those rules out into a more general (more shallowly nested) selector.
Just to be clear, I’m not suggesting you deeply nest everything just for the sake of doing it. General rules — list-styles, basic typography, page structure — should not use deep selectors.
The main benefit here is that you can quickly determine the specificity of a set of rules by glancing at the selector (i.e. shorter selectors indicate more general rules whereas longer selectors indicate more specific rules.)
In other words, if you deeply nest things unnecessarily, it not only makes for bad code but also destroys the benefit of using the method in the first place.
Reply
March 2nd, 2010
Cory SchiresCory – love the article.
To all the “Use efficient CSS selectors” optimisation gurus: Get a grip and concentrate your development time on areas that get good performance improvements. Does changing selectors actually make a noticable difference in page load? Are you pre-optimising code to get a negligible speedup? Does IE process selectors right to left? I would be loath to lose the advantages of tidy code for a change if it only gives an insignificant performance improvement.
The only relevant selector performance testing I have found so far is: http://www.stevesouders.com/blog/2009/03/10/performance-impact-of-css-selectors/ (and even that is not for a realistic page).
Reply
March 9th, 2010
robocat