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.