Wibbly Stuff

A better approach to nesting in Sass

We've come a long way from completely hand-written CSS to generated CSS via pre-processors. In the early days, I tried to avoid pre-processors. The idea that a tool will generate CSS for you was discouraging. But eventually, the advantages provided by them proved to be much more useful in a big project, and I adopted LESS, then switched to Sass. But I was trapped in the convenience provided by Sass. I did things I shouldn't have done, like nesting.

Nesting is awesome because it imitates your html markup. The code is organised in a more familiar way. But the convenience has a cost, CSS specificity!

Take an example of the following HTML,

<ul class="grocerylist">
    <li class="groceryitem">Tomatoes</li>
    <li class="groceryitem">Brinjals</li>
    <li class="groceryitem">Broccolis</li>
</ul>

And the Sass code,

.grocerylist {
    .groceryitem { color: red; }
}

When it's compiled to CSS, the following is generated,

.grocerylist .groceryitem { color: red; }

Now later you decided you wanted the already purchased grocery items should be green. So you go and add a class purchased to your html,

<ul class="grocerylist">
    <li class="groceryitem purchased">Tomatoes</li>
    <li class="groceryitem purchased">Brinjals</li>
    <li class="groceryitem">Broccolis</li>
</ul>

And say you add the following in your Sass code,

.purchased { color: green; }

Then hit your head on the wall figuring out why it doesn't work. Well, it doesn't, because .grocerylist .groceryitem has a higher specificity than .purchased. This is just a very basic example. But if you want to know more about specificity, +Chris Coyier has written a detailed post on CSS-Tricks about it.

I won't go into deep about specificity. The point is, nested selectors in Sass create more specific selectors. Hence it becomes hard to override their styles. And more levels of selector means the browser has to do much more parsing than it needed in case of a single selector. So, nesting is evil. There is even an inception rule which advises not to go deeper than 4 nesting levels. But even 4 levels of nesting is bad IMO.

It's like the movie Megamind. Megamind, who was initially a good lad, turned evil super villain. But even he turned into a hero again. Cannot we do something for nesting, so that it's no longer the evil villain?

Luckily we can, if we follow certain conventions in naming our CSS classes, we can again use nesting without worrying about specificity issues.

I always follow the convention of naming my css classes according to the place they belong. For example, say there is a sidebar, I will add the class sidebar. Then for each block in the sidebar, I add the class sidebar-block and for each item inside that block, I add the class sidebar-block-item. If you follow this naming convention, now you can now write your Sass like,

.sidebar {
    // styles

    &-block {
        // styles

        &-item {
            // styles
        }
    }
}

Now the generated CSS code will be,

.sidebar {
    // styles
}

.sidebar-block {
    // styles
}

.sidebar-block-item {
    // styles
}

Yay! No more specificity issues. Each selector is cleanly separated without nesting in the generated CSS. Heck, Sass even generates CSS indented to look like the nested Sass code. So no more issues!

Of course, if you follow this approach excessive nesting will require very long CSS selector names. But then, excessive nesting is still bad. Nesting will improve readability. Excessive nesting will not. For me, the inception rule still applies with this approach.

Do you follow any other techniques to organise your Sass code better? Or do you think this approach is not good? If you've any opinions, feel free to comment.