One of the best things about CSS is the cascade, itself. It can also be one of the worst things about CSS. Styles applied to an HTML element are automatically inherited by their children. Cascading is what allows web designers to set styles and keep them consistent throughout a website without repeating themselves. With a single line of CSS you can, for example, set the typeface that will be applied to every element on every page of your website:
body {
font-family: Helvetica;
}
Because every element is a child of the body
, those elements will inherit the font style unless that property is otherwise overridden with a more specific rule. That’s where things get tricky. We might want to use Helvetica throughout the website except in the sidebar, where we’ll use Georgia. To reset the font-family rule for the sidebar element we must write something like this:
body aside {
font-family: Georgia;
}
This is how we write CSS. We set styles at the top level and then proceed to override the inherited rules with increasingly specific selectors. There are esoteric rules of inheritance built into the CSS cascade that let designers target elements by id, class, or by relative position in the document. Each time you reset a style using specificity, overriding that style on a subsequent child element requires an even more specific selector.
Within complex website designs this can begin to feel like an arms race. You add classes and containers to your mark-up and more elements to your selectors. As you add increasingly specific (and increasingly long) selectors it becomes equally difficult to override them later. This problem is compounded after the initial composition of the CSS is completed and you begin to edit, maintain, or add new styles later in the life of the website. Even in the most carefully composed stylesheet it can be difficult to completely grasp the overall scheme – especially if you’re coming in behind the original developer to make changes. The safe thing – we’ve all done it – is to write a very specific selector for any new styles so you’re certain yours take precedence. Each round a salvo in the specificity arms race.
So what can be done? We’ve been experimenting with a few techniques that we think make a big difference. There are three parts to this formula: compiled CSS, structured mark-up, and a neglected CSS selector.
Part 1: Compiled CSS
Complied CSS frameworks add shortcuts, stricter syntax and additional features like reusable code and variables to standard CSS. You write your styles using the framework which then compiles into plain CSS before being served to the browser.
We started experimenting with compiled CSS over a year ago, first with Iterations, and then with Basecamp Mobile a few months later. While it was convenient to write and we enjoyed some of the special features (such as LESS CSS color functions) we weren’t convinced it offered enough benefits to make it a permanent part of our workflow. It took a some time before things really started to click.
(Note: We have used both LESS and SASS in production projects but we’re currently using SCSS and will refer to compiled CSS as SCSS for the rest of this post. The techniques discussed, however, will work with either framework and presumably other compiled CSS frameworks that we’re not aware of.)
What we liked most about using SCSS is its most basic benefit: nesting. With SCSS you can avoid repeating selectors by literally nesting them inside other rules. So, for example, this rule in SCSS:
body {
section {
header {color: red;}
p {color: black;}
}
}
… compiles into this plain CSS:
body section header {color: red;}
body section p {color: black;}
For the developer this offers a couple of benefits. First, the body section
selector only has to be written once. And it’s immediately clear that both the header
and p
styles are applied at the same level. So this is a win for clarity and efficiency. That was nice but it didn’t really change the way we wrote CSS, nor did it solve our cascading problem.
Part 2: Structure and content
It wasn’t until we had begun our third project using SCSS that we really embraced nesting for more than convenience. We began to notice that when used properly, nested CSS started to look a lot like the HTML document it is applied to. Compare this HTML mark-up:
<html>
<body>
<section>
<header></header>
<article>
<header></header>
</article>
</section>
</body>
</html>
… with this nested CSS:
html {
body {
section {
header {}
article {
header {}
}
}
}
}
It’s nearly identical! Now we’re getting somewhere. Writing styles that match the mark-up exposes some additional benefits. When composing styles for the above mark-up, it’s immediately clear where in the stylesheet those styles belong. Styles that apply to the article
element can easily be plugged-in right where they are expected in the nested CSS. Another developer reading this CSS later can easily understand how these styles interact with each other and what the original developer intended. This changed the way we thought about our mark-up, too.
HTML5 Elements
Throughout these projects we also experimented with the new elements offered as part of HTML5. We liked that the new elements had intrinsic meaning resulting in fewer classes, especially now that SCSS had us relying more on structure to target our rules.
Similarly we began to see a relationship between our mark-up and the underlying data structure in our app’s models. HTML5’s section
came to represent the model, article
the individual records. For example:
<html>
<body>
<section class="todolists">
<header>Todos</header>
<article class="todolist">
<header>Launch list</header>
<ul>
<li>Get final sign-off</li>
<li>Deploy to production</li>
<li>Publish launch post</li>
</ul>
</article>
</section>
</body>
</html>
Now we had a meaningful representation of the model in our mark-up and styles. This made writing, reading, and understanding our view code much easier for both designers and programmers. We had a predicable structure along with CSS selectors that are specific and intentional across the app. Now we had the pieces in place to really take control of our CSS.
Part 3: The Child Selector
The last piece of this idea was surprisingly simple – and had been available all along: the CSS child selector. While it’s been around since CSS2 it seems to be mostly forgotten (probably because it wasn’t supported by Internet Explorer 6).
So what does it do? The child selector targets an element that is a direct child of its parent and only if it’s a direct child – it doesn’t cascade any further. Aha! So let’s return to our sample mark-up and styles:
<html>
<body>
<section>
<header></header>
<article>
<header></header>
</article>
</section>
</body>
</html>
html {
body {
section {
header {color: green;}
article {
header {}
}
}
}
}
That’s going to compile to this CSS:
html body section header {color: green;}
The style will make both header
elements green since they are both inside the section
. But by adding a child selector we can change that:
html {
body {
section {
> header {color: green;}
article {
header {}
}
}
}
}
Now only the header
just inside the section
will be green, the header
inside the article
element (event though it is also inside the section
) is un-touched. We’ve just stopped the green color from cascading any further than the element we intended. One character in our CSS frees us from having to override the rule at the article
level. It also matches the original intent that is implicit in our nested CSS. It already looks like the green should only apply to the higher level section
element. Furthermore, we found that the more child selectors we used, the more control we had:
html {
body {
> section {
> header {color: green;}
> article {
> header {}
}
}
}
}
For the first time we could write CSS that we knew wouldn’t cause problems later on because they couldn’t cascade out of control. This opened us up to create elements with self contained styles that could be dropped onto most any page and they’d just work. Here’s an example of styles for a to-do list:
article.todolist {
> header {
> h1 {color: red; font-weight: bold;}
> a {color: blue;}
}
> ul {
margin: 10px 0;
padding: 0;
> li {font-size: 13px;}
}
}
Because we know which styles will and will not cascade into our to-do list we waste no effort in overriding styles or protecting from possible cascade conflicts and we can be sure that our article.todolist
is going to look the same no matter where we display it in the app.
Final thoughts
We’ve been using and refining these techniques since early in the summer and we’re very happy with the results. Our stylesheets are logical and carefully controlled. We find that once they get used to the SCSS syntax new people brought onto the project pick up the mental model of the app styles quickly without the mess of the specificity arms race. We’re also seeing better collaboration between designers and programmers who can more easily parse our logical CSS. JavaScript also benefits from these predictable selectors.
We’ll share more as we continue to refine this approach but we’d love to hear what you think.
Matthew
on 12 Sep 11This actually will end up outputting the slowest possible CSS selectors ou could write. Overly specified selectors moving RIGHT TO LEFT will run a check each step of the way. It may not matter to most people how performant their selectors are but it’s worth noting.
Berserk
on 12 Sep 11It would be interesting to see a benchmark comparing the extra processing time caused by slow selectors. That can then be compared with extra brain time for the developer.
Matt
on 12 Sep 11I’ve always wondered about the speed of CSS selectors and how big of an impact that is. The articles I looked at seemed a little older (written a few years ago) and I found some people saying that new browsers had made the speed impact negligible. Theoretical problem or actual problem?
Zac
on 12 Sep 11I’ve also found the behind underlying Nicole Sullivan’s Object Oriented CSS to be extremely helpful in solving this problem on large projects.
Zac
on 12 Sep 11Err, that should have been “the concepts behind…”.
Zindustry
on 12 Sep 11Thanks Jason. This will really help me as I do my CSS. I’ve always wanted to try some of this, but this gives me the push I need.
Chad
on 12 Sep 11I believe there are some performance drawbacks to using the child selector, but those drawbacks aren’t really apparent unless you are repeating elements. This test by Dave Gregory is a pretty extensive. The article that goes along with this can be found here. In short, OOCSS gives you the best bang for the buck performance wise. I do however find this technique of making the CSS reflect the markup interesting.
Joe Van Dyk
on 12 Sep 11I’m assuming the projects where you are using the html5 elements don’t support older browsers (such as IE < 9)?
Carson
on 12 Sep 11@Joe Van Dyk – polyfill
Leonid Khachaturov
on 12 Sep 11As already mentioned, long element selectors are the worst-performing CSS possible, which is quite noticeable if you care to measure that. An approach I’ve been using for the past two years is called BEM (stands for “Blocks-Elements-Modifiers”) and it is described in English here: bem.github.com/bem-method/html/all.en.html
The basic premise of this approach is about decomposing everything into blocks (logical parts of a page), elements (something that is used inside a block), and modifiers (used to describe all the variations of blocks’ appearance and behavior). It may look slightly scary in the beginning – lots of CSS classes with long, seemingly too explicit names, but when you start using it there’s really no looking back as it successfully solves all the typical CSS maintenance problems you’ve described in the post, while making JavaScript code dealing with DOM beautiful as well.
Sam B
on 12 Sep 11I was under the impression that style sheets are called cascading style sheets because they cascade in order of priority (inline CSS takes precedent over external stylesheets, etc), not because their properties are inherited by child styles and classes. Minor point, though.
Leonid Khachaturov
on 12 Sep 11@Sam B well, it’s both of course, since cascading doesn’t define, but always redefines something, be it default element styles, or lower-priority styles.
Abe Yang
on 12 Sep 11We’ve recently been looking into Less vs. Sass—would you mind sharing (if just a bit) as to why you went for Sass and not Less? The points that you mentioned in this post can be accomplished by Less as well, so I was wondering if there was something special about Sass that made you lean towards it.
Daniel Sim
on 12 Sep 11I’ve been taking a similar nesting approach for the last year. I’m starting to find it’s feeling less and less readable, and catching myself scrolling up and down the window to confirm the scope of the styles I’m writing. One way I’ve had success alleviating that is by breaking up the stylesheet into lots of small ones (given that they’re compiled anyway) and having same-named files for .less, .mustache template, .js class and .rb view. Though I still get lost in scope. Have been considering minimising my use of nesting, Leonid’s mentioned BEM approach looks smart.
Mario "Kuroir" Ricalde
on 13 Sep 11Like a couple of people stated before me, having long selectors doesn’t only affect performance but also can become a specificity unneeded pain.
Nesting is good when it make sense, when it doesn’t just don’t do it. Follow the Inception Rule:
Jethro Larson
on 13 Sep 11As someone who has been using sass for a while I totally agree with the Inception Rule.
I understand how nice it is for newbies to be able to mirror the DOM in the SCSS document but the generated CSS can be enormous and have serious specificity inflation.
Ken Snyder
on 13 Sep 11A selector like `body section header` is about the worst you can write. Such an approach is pretty much the opposite of object-oriented css. I have found it best to avoid tag names and IDs as much as possible. I love Nicole Sullivan’s presentation: http://www.slideshare.net/stubbornella/our-best-practices-are-killing-us
I am working on a project now where we got in serious selector wars. The designers used selectors like `article aside section h3` which didn’t tell anyone that the contents were commenter’s names. Just use `.commenter-name`! When we went to version 2 and added complex layouts that had three types of articles on a page, the selector wars began. Then when version 3 required ability to white label, everything fell apart and we redid the HTML and CSS from scratch.
Forget more than four levels deep, stick to 1 and sometimes 2!
Mario "Kuroir" Ricalde
on 13 Sep 11@Ken Snyder, What you’re reporting is a bad practice. They didn’t add a class or an id to explain what’s going on and without context or documentation everything it’s bad.
I have to disagree with your last statement, just because you’ve had a bad experience with a some bad front-end engineer doesn’t nesting is bad.
dmr
on 13 Sep 11Nice article, validating some of my own journey with CSS. For years I was targeting things with mostly IDs, but still writing trim & elegant CSS.
Nowadays it’s modular and well-formed html5, so 3 levels of specificity is the most I typically need, using basic elements and class names as hooks: Chapter 7 .chapter-intro header .chapter-number { color: red }Specific class names that capture the context of the information, but still thinking general and modular.
CSS3’s flexibility is amazing, targeting modular elements like drop caps, first paragraphs, lists that follow headers — all these are easily done with a few well-crafted targeting statements. :first-child (header + p:first-letter), adjacent selector (h2 + ol) are all things I use daily now.
Corachán
on 13 Sep 11Such an interesting discussion. To nest or not to nest ;-)
I had the same bad experiences than @Ken Snyder points on his post with some projects and “classic” programmers that goes into the HTML with Firebug, and their bloat your CSS with tons of nested elements that are really hard to maintain.
We must think over this articles and opinions, not as strict rules to follow, but just as considerations for future projects. We can learn a lot sharing our problems and solutions, and that’s my favourite thing of the Internet, I always learn new things from people around the world.
My two cents of what have been said so far:
- I’ll never use again #ids to match cascading elements of the DOM. Maybe #header, #footer and #wrapper, but I’ll try to use classes on all elements from now on. - Try to follow as much as possible the Inception Rule, and don’t nest more than 4 elements. - Last but not least, use CSS selectors to match elements - I’m not sure about LESS and SASS for my current projects, but some ideas are good enough to think about them in the near future.
Thanks for sharing your thoughts!!
Milan Stosic
on 13 Sep 11In the past few weeks I read again this Google’s article – http://code.google.com/speed/page-speed/docs/rendering.html and after that started to ask my self is there any way to make my css shorter and easier to maintain?
I talked on some forums about that and got information about OOCSS project, someone allready mentioned in comments. I’ve tried this approach and for me it makes a lot of sense, over my old way with nesting elements.
IMHO it is everything about balancing – so you have to make some mix of your css – to suit needs for easy developing and maintenance and to suit needs of browser rendering.
I sugest to take a look at OOCSS examples and try to find the way it is best or you.
Regards
Renee
on 13 Sep 11weawfv
Tim Watson
on 13 Sep 11C’mon Jason, we’ve talked about this before and looks like these kind folks agree, OOCSS is where it’s at (Search your feelings. You know it to be true).
:) – “Darth” timus
Joshua
on 13 Sep 11I think what the people suggesting a more OOCSS approach are saying is that overly nested styles like this will still cause the problem that you are trying to avoid. You will end up with custom styles everywhere and it will be difficult to predict what will override your new styles.
Instead, you should ask why your “article.todolist” styles aren’t just ”.todolist” or even just a generic list style.
Now that you’ve simplified to this point though, make that last little push into OOCSS and SASS syntax!
JZ
on 13 Sep 11@Joshua, Ken, Mario, et al. RE: ...overly nested styles…
There seems to be some confusion here that I’d like to clear up. The examples above are illustrations, not code samples that you can or should drop into your project. Nesting
html > body > section > whatever
isn’t something I’d recommend or do – it’s not necessary. What it does is illustrate the point that making a connection between the DOM and your CSS using nesting provides clarity and modularity.RE: I think what the people suggesting a more OOCSS approach are saying is that overly nested styles like this will still cause the problem that you are trying to avoid. You will end up with custom styles everywhere and it will be difficult to predict what will override your new styles
Except that it doesn’t. The methodology is to nest as little as necessary and generalize judiciously. So you don’t override a style, you refactor. If we find that a rule is too specific and it needs to apply more broadly, it gets moved up the tree, not overridden or redefined.
RE: Instead, you should ask why your “article.todolist” styles aren’t just ”.todolist” or even just a generic list style.
Why? If it’s always going to be an
article
, why not be clear about it? If I’m only going to use this list style here, why generalize it? That’s the kind of thing we’d like to avoid – having to override or redefine it later.We don’t feel like we have to be dogmatic in this approach or any other. We’re using the concepts from OOCSS that make sense in this project – this isn’t a line in the sand. We aren’t dismissing OOCSS, it isn’t an all or nothing choice. Thanks for the discussion :)
Per Wiklander
on 13 Sep 11@JZ Can you publish some of your uncompiled SCSS code for us to look at?
And why don’t you minimize the CSS on this site? Who is going to read the comments in the compiled and deployed CSS files?
Btw, posting this gave me:
Application error
Change this error message for exceptions thrown outside of an action (like in Dispatcher setups or broken Ruby code) in public/500.html
Nicole Sullivan
on 14 Sep 11@Zac – LOL. Seriously, thanks for that, I actually laughed out loud here in my office. :D
@Jason – Looking at these examples, I really dig the predictability you’ve been able to bring to CSS, that is a huge win. OTOH, the predictability is expensive because you are going to have a lot of duplication.
What happens if you need a green heading, blue link, or unordered list somewhere else in the document? You can’t use them in both places without either losing the predictability or duplicating a lot of property value pairs.
Performance-wise, the most expensive thing you can do is have a lot of duplication. If you control duplication, your selectors can be less efficient and it doesn’t really matter much because browsers are pretty good at making sense of selectors quickly. OTOH, if you have a ton of CSS, those inefficient selectors start to slow stuff down… even worse, they can make your JS feel clunky if reflows/repaints take a long time.
It is kind of cool to have the CSS, HTML, and data abstraction match, but it isn’t as efficient as coding each in the best way for that layer and then defining an “API” of sorts to show how they interact.
JZ
on 14 Sep 11@Per – the project is still in-progress, we’ll share more later after it launches. SvN hasn’t been redesigned in quite awhile so we haven’t adopted any of these newer ideas there yet. And sorry about the posting error. We’ve got an elusive bug there that we’re trying to track.
@Nicole – thanks for weighing-in, I appreciate your insight because I know you’ve done your homework on this stuff. As I’ve mentioned to others the examples are over simplified. We’re shooting for predictability but not to the point of absurdity (I hope ;). So we nest as little as necessary and still employ a more OO approach for commonly used rules that can’t be scoped to the DOM tree. Being mindful of the DOM in our CSS gives us predictability without requiring us to precisely model it.
It also helps that the project we’re working on doesn’t require a massive amount of CSS so we may be getting away with something performance-wise such that this wouldn’t be as practical for something more complex. We don’t know at this point – performance hasn’t been an issue.
Kyle
on 14 Sep 11My reaction to point 1 was, ok, yeah, that makes sense… then I got to point 2 and my head hit my desk. The whole point of CSS was to decouple visual design from physical page layout, I.E. get style info out of the HTML, and there you go putting it right back in. By tightly coupling your styles with the layout of the DOM you’re making your design fragile, any little change to the page layout will completely break the styling.
Sometimes knowing a bit about the layout of the DOM is a necessary evil, in that the child of .someWidgetClass needs to have a particular property (even then you could do something like .someWidgetClass .childClass), but that should be the exception and not the rule. If you’re going to tightly couple your style to the layout of the DOM you might as well just save time and embed it directly in the HTML, then there’s no need to duplicate declarations.
HTML element selectors should be used to provide a default styling to large swaths of the HTML, and if there’s some need to override in particular instances using a child of a class selector can be used to add an exception to the rule. In part 3 you correctly identify an appropriate use of the child selector in that you don’t want to style all header elements, but a particular one, that is, an exception to the general rule, and as such it rightfully should be a child selector. However I think where you make a mistake is in explicitly duplicating the entire DOM layout, the appropriate selector in your example should be section > header, or even better assuming this isn’t a style you want to use in other sections .todolists > header.
MattO
on 14 Sep 11@kyle I understand that css and html are suppose to decouple content from format, but in practice i find the line isn’t that neat. Think of the last time you used a page position to name a class like .rightSidebar or all of the javascript that sneaks into the content of the html file. As in all areas of engineering, there is a point where practicality overrules best practices.
Sounds like selectors and the html page layout just seemed to happen to line up. not all that surprising since a the box model would kinda promote this. I don’t think the nesting of compiled css breaks the decoupling rule all that bad, but the amount of readability that results is worth bending the rules.
p.s. anyone know how expensive long selectors or duplication really is(quantitatively) ?
p.s.s. kyle dont mean to be attacking. just though my opinion was a good counter-point to the case i thought you were trying to propose.
Kyle
on 15 Sep 11@MattO To use your example, the .rightSidebar class is perfectly named and isn’t coupling the design to the format at all. There are two kinds of “layout” on a page, the visual layout, that is, what the page looks like after being rendered by the browser, and the data layout, that is what the DOM looks like. CSS was created to decouple the visual layout from the DOM layout. Calling something .rightSidebar is perfectly acceptable, because it describes the visual layout of the component without saying anything at all about the data layout. Saying the third child div of the body element is the rightSidebar on the other hand is tightly coupling the visual layout to the data layout (or more accurately it’s coupling the declaration of the visual layout to the data layout which is nearly as bad).
As for javascript in html, that’s another issue entirely in that javascript is usually used to fill the role of something akin to a controller from the MVC design, and so naturally has knowledge of both the View (the visual layout, CSS, formatting, etc.) and the Model (the html/DOM). Even there best practice is to try to keep as much of the javascript in separate files as possible and to use selectElementById whenever possible rather than traversing the DOM by hand for much the same reason that class selectors are preferred in CSS.
To be clear I’m not opposed to nesting of CSS declarations such as what SCSS allows you to do. What I’m opposed to is having your CSS selectors mirror the DOM except when absolutely necessary. The style of a particular element should not be dependent on its location in the DOM because then any minor change to the DOM breaks large swaths of the visual layout. Something as simple as re-parenting a div, or swapping the order of a couple divs would break not just the styling on those elements, but also anything that was a child of them.
There’s also the practical consideration in that often times the visual design of a site is being worked on in parallel to the data design and usually by two separate people or teams. If you use primarily class selectors the person doing the data design can arbitrarily style an element with the appropriate style simply by giving it the proper class and is otherwise free to arrange elements as required. Likewise the person doing the visual design doesn’t need to constantly check the latest version of the HTML being generated to make sure his CSS selectors still match the latest version of the code.
Readability is definitely a nice thing to have, but I don’t think it’s worth the sacrifice in this case. In general readability should only trump design in the most trivial of cases, particularly when readability directly reduces ease of maintenance as in this case.
I’m not trying to “attack” anyone here, I’m just trying to demonstrate that there’s value in not coupling your CSS any tighter than necessary to the layout of the DOM. From reading some of the responses to other comments it sounds like the article author may not have been intending to suggest that’s what people do, but whether he intended it or not that’s the way the article reads and the way it looks like a great many people interpreted it.
As an aside my take on this is probably slightly different from at lot of the other people commenting here in that my background is not design (although I’ve done my share of HTML, CSS, and graphics work), but rather CS. As such I draw parallels to concepts such as Object Oriented design and MVC patterns, and useful concepts from those such as loose coupling, and encapsulation. Time and again it’s been shown that tightly coupled systems are inherently fragile and difficult to change, and as such tight coupling should be avoided whenever possible.
Marco
on 16 Sep 11Ugh the child selector… I often overlook this one when I shouldn’t and tend to use classes :)
Jessica
on 16 Sep 11This would make my brain go bonkers.
Manuel E.
on 16 Sep 11Best damn blog in the world!!
Thibaut Assus
on 18 Sep 11You are totally awesome !! Brilliant ! Never thought this way, and that seems so amazing !
Thibaut Assus
on 18 Sep 11You are totally awesome !! Brilliant ! Never thought this way, and that seems so amazing !
Thibaut Assus
on 18 Sep 11You are totally awesome !! Brilliant ! Never thought this way, and that seems so amazing !
This discussion is closed.