HTML & CSS
Article

Beware of Selector Nesting in Sass

By Hugo Giraudel

A couple of days ago, I tweeted about how I thought selector nesting actually caused more problems than it solved.

Some people agreed, some did not. In any case, it raised a couple of interesting thoughts so I thought I would put together an article to talk about the topic.

What is selector nesting?

Selector nesting is a feature of CSS preprocessors, allowing authors to nest selectors within selectors in order to create shortcuts. For instance:

.parent {
  color: red;

  .child {
    color: blue;
  }
}

Which would compile to the following CSS:

.parent {
  color: red;
}

.parent .child {
  color: blue;
}

In this example, .child is nested within .parent in order to avoid repeating the parent selector.

While this can be quite useful, I feel like this feature is being largely overused to the extent that we now try to solve issues we inflicted ourselves while nesting too much.

What’s wrong with nesting?

In itself, there’s absolutely nothing wrong with nesting. The feature makes sense. The problem, as is often the case, is not the feature but how we use it. Allow me to start with a couple of examples I stumbled upon.

There is this one from Micah Godbolt:

.tabs {
  .tab {
    background: red;

    &:hover {
      background: white;
    }

    .tab-link {
      color: white;

      @at-root #{selector-replace(&, '.tab', '.tab:hover')} {
        color: red;
      }
    }
  }
}

Or this one from ZI Qiu:

.root {
  width: 400px;
  margin: 0 auto;

  .links {
    .link {
      display: inline-block;

      & ~ .link {
        margin-left: 10px;
      }

      a {
        padding: 10px 40px;
        cursor: pointer;
        background: gray;

        &:hover {
          background: blue;
          color: white;
          font-size: 700;
        }

        .icon {
          margin-right: 5px;
          @include selector-modifier(-2 ':hover', 1 suffix '.zh') {
            color: red;
            background: green;
          }
          @include selector-modifier(-2 ':hover', 1 suffix '.en') {
            color: yellow;
            background: green;
          }
        }
      }
    }
  }
}

Let’s start with a disclaimer: This is clever code. In no way am I saying that this is bad code. I assume this code is doing exactly what it is intended to do.

Now, what if I asked you what exactly are those two examples meant to do? Base on a first glance, without spending a couple of minutes looking at the code, would you be able to guess?

Me neither. Because it is complicated.

Nesting makes code complicated

Both of the examples shown above use some selector functions (from Sass 3.4) to partially rewrite the current selector context (&).

So if I’m not mistaken, both examples involve more code in order to write less code, all of this in addition to some extra complexity. What about writing some simple code from the start?

This is a statement I already made before: Selector functions are not meant for everyday use. I believe Chris Eppstein and Natalie Weizenbaum explicitly said they were essentially adding this feature to help framework developers.

Note: if you find a legit use case for selector functions, that actually solves a problem, please be sure to show me, as I’d be interested in taking a look at that.

The Reference selector is ambiguous

The reference selector in Sass (&) can sometimes be quite ambiguous. Depending on the way it is used, it can yield a totally different CSS output. Here is a collection of simple examples.

/* SCSS */
.element {
  &:hover {
    color: red;
  }
}

/* CSS */
.element:hover {
  color: red;
}
/* SCSS */
.element {
  &hover {
    color: red;
  }
}

/* CSS */
.elementhover {
  color: red;
}
/* SCSS */
.element {
  & .hover {
    color: red;
  }
}

/* CSS */
.element .hover {
  color: red;
}
/* SCSS */
.element {
  &-hover {
    color: red;
  }
}

/* CSS */
.element-hover {
  color: red;
}
/* SCSS */
.element {
  &.hover {
    color: red;
  }
}

/* CSS */
.element.hover {
  color: red;
}
/* SCSS */
.element {
  .hover& {
    color: red;
  }
}

/* Syntax Error */
Invalid CSS after ".hover": expected "{", was "&"

"&" may only be used at the beginning of a compound selector.
/* SCSS */
.element {
  &:hover & {
    color: red;
  }
}

/* CSS */
.element:hover .element {
  color: red;
}
/* SCSS */
.element {
  &:hover {
    & {
      color: red;
    }
  }
}

/* CSS */
.element:hover {
  color: red;
}

And we are keeping things simple here with a single selector. Needless to say, it can get quite complicated when you multiply the references in a single rule.

The thing is, some operations work, some don’t (notice the error displayed in one of the examples above). Some generate a compound selector, some don’t. Depending on what’s being done and the Sass background of the next developer to use the code, it can become quite difficult to debug this kind of thing.

Unsearchable selectors

At this point I am mostly nitpicking. But there is something I don’t like that involves Sass code extensively using nesting to build BEM-like selectors:

.block {
  /* Some CSS declarations */

  &--modifier {
    /* Some CSS declarations for the modifier */
  }

  &__element {
    /* Some CSS for the element */

    &--modifier {
      /* Some CSS for the modifier of the element */
    }
  }
}

Before moving on to why I don’t like this, let me remind you what this code will compile to:

.block {
  /* Some CSS declarations */
}

.block--modifier {
  /* Some CSS declarations for the modifier */
}

.block__element {
  /* Some CSS for the element */
}

.block__element--modifier {
  /* Some CSS for the modifier of the element */
}

On one hand, this avoids repeating .block in each selector, which can be a pain when you have block names like .user-profile.

On the other hand, it creates new selectors out of nowhere that are thus impossible to search for. What if a developer wants to find the CSS from .block__element? Chances are high he’ll try to search it in the project from his IDE, finding nothing because this selector has never been authored as is.

Actually, it looks like I’m not the only one to hold this view. Kaelig, who was working at The Guardian at the time, tweeted this:

Also, I think it’s nicer to have the base name repeated. This way, it is crystal clear what’s going on.

I should note that source maps can help with this to some extent, but they don’t change the fact that the code base itself is still unsearchable.

When is it okay to use nesting?

As long as you and your team feel okay with the extra complexity it can involve, it is always okay to use selector nesting. If everybody’s fine with it, then that’s awesome!

If you ask me now, I feel like adding pseudo-classes and pseudo-elements are pretty much the only case where it’s fine. For instance:

.element {
  /* Some CSS declarations */

  &:hover,
  &:focus {
    /* More CSS declarations for hover/focus state */
  }

  &::before {
    /* Some CSS declarations for before pseudo-element */
  }
}

This is the best use case for selector nesting I can think of. Not only does it avoid repeating the same selector, but it also scopes everything from this element (states and virtual children) in the same CSS rule set. Also, & is not ambiguous: it means .element, no more, no less.

Another case where I do think nesting is interesting is when you want to apply some custom styles to a simple selector based on the upper context. For instance, when using Modernizr’s CSS hooks:

.element {
  /* Some CSS declarations */

  .no-csstransforms & {
    /* Some CSS declarations when CSS transforms are not supported */
  }
}

In this scenario, I feel like this is clear enough that .no-csstransforms & is intended to contextualize the current selector whenever CSS transforms are not supported. Although, this could be on the borderline of what I would consider acceptable use.

What are my recommendations?

Write simple CSS. Let’s take Micah’s example, which is slightly complicated, but not so complicated that it would be a pain to re-write.

This is the current code, obviously meant to style some tabs:

.tabs {
  overflow: hidden;

  .tab {
    background: red;

    &:hover {
      background: white;
    }

    .tab-link {
      color: white;

      @at-root #{selector-replace(&, '.tab', '.tab:hover')} {
        color: red;
      }
    }
  }
}

We could instead write it like this:

.tabs {
  overflow: hidden;
}

.tab {
  background: red;

  &:hover {
    background: white;
  }
}

.tab-link {
  color: white;

  .tab:hover & {
    color: red;
  }
}

I can’t think of a single reason for someone to prefer the first code snippet. Not only is it longer, but it’s also less explicit and makes use of Sass features that are not necessarily known by all developers.

Note that the CSS output is not exactly the same because we also simplified the code. Rather than having selectors as long as .tabs .tab .tab-link, we moved to easier things like .tab-link.

At this point, it’s not so much selector nesting but naming conventions and selector methodology. When using BEM, for instance, you name things after what they are rather than where they are, which often results in simple selectors (here simple means not compound), meaning there is no nesting involved.

According to CSS Guidelines from Harry Roberts:

It is important when writing CSS that we scope our selectors correctly, and that we’re selecting the right things for the right reasons […] Given the ever-changing nature of most UI projects, and the move to more component-based architectures, it is in our interests not to style things based on where they are, but on what they are.

A good rule of thumb when it comes to CSS selectors is the shorter the better. Not only is is better at any level (performance, simplicity, portability, intent, etc.), but it turns out it’s much easier not to screw things up with nesting when selectors are short.

Final thoughts

Whenever I say how I think selector nesting isn’t such a good idea, people always tell me “I never had an issue”, “we use it every day, no problem”, “it’s because you’re doing crazy things with it”. Of course there is nothing wrong with the feature.

Preprocessors don’t output bad code, bad developers do. And here, it’s not even about the output but about the input. Code becomes less and less readable when we add extra layers of complexity. Nesting selectors is one of those layers.

We were given nesting, so we abused its power. Then we were given selector functions, so we used those in order to fix the mess we made with nesting in the first place. This is wrong.

Use Sass, or any preprocessor for that matter, to simplify your code, not to make it more complex.

This article has been translated into French on La Cascade. Merci, Pierre!

Meet the author
CSS goblin. Sass hacker. Margin psycho. Author of Sass Guidelines, SassDoc, Sass Compatibility, Browserhacks and a lot of Sass stuff.
  • geoffyuen

    Great article and very well reasoned out, Hugo.

    > I can’t think of a single reason for someone to prefer the first code snippet.

    Just playing devil’s advocate but I believe most people nest classes to show hierarchy.

  • Test McTest

    amen brother! I’ve been unwinding some of our enthusiastic nesting which has become a nightmare to override or reuse. Interesting your take on the BEMification via &, that was on our list to look at but I’m rethinking the long term value (and likely maintenance pain) of it now.

  • Aaron

    I absolutely agree with you and have thought this could become a nightmare since it’s introduction. You’re correct in saying that “Preprocessors don’t output bad code, bad developers do.” And as Aunt May would say, “with great power comes great responsibility”

  • MattDiMu

    +1, it’s much better to write one or two lines more and have the code better readable and searchable. Btw, was this article a response to this one? http://www.sitepoint.com/introducing-ccss-component-css/

  • Tim Gummer

    Nice to have you on the side of un-spectactular, comprehendible code, Hugo. There was a period back there I had thought you were dragging us off into an obtuse, obsfucated oblivion of smart-arsedness. But this makes a lotta sense.

  • http://twitter.com/mattsince87 Matt Litherland

    This may sound like im bashing your article but for the record im not. I think the two most important things you said in this entire article are:

    “Preprocessors don’t output bad code, bad developers do.”

    “Use Sass, or any preprocessor for that matter, to simplify your code, not to make it more complex.”

    Nested selectors are for easily maintaining semantic front-end html. If you cant code maintainable CSS or SASS/SCSS to cater for this; then put simply… you’re not a very intelligent developer.

    • http://www.emjot.de Joachim Tillessen

      I agree with Matt here. Nested selectors are very useful if you want to avoid representational or otherwise overly specific class names in your markup. It gives you the ability to style your dom elements according to the semantic context they appear in.

    • Ernesti Kyöstilä

      Having done A LOT of object oriented web stuff I welcomed SASS just because of nesting. It is the very thing that makes a very complex CSS maintainable. I am not a designer, I just generate HTML and CSS from what the designer does and what data the backend vomits out :)

      The “Recommendations” is a very, very badly simplified example. We have a framework with dozens of web output modules. We have, for instance, data grids for different purposes and some of them have different styling principles. Now with those data grids we can have an umbrella class in which we can create the those small differences needed in various parts of the application and keep the whole thing maintainable.

      What if you have another class like “tabs”, say “fancytabs” and under it a “tab” as well? Now that you have isolated the “tab” from “tabs” you need to create another top-level class like “fancytab”. You would be exactly where you would be without SASS. With nesting things get much, much more streamlined when you do not need to invent new names for classes inside the parent container.

      .tabs {
      .tab {
      background-color: #ccc;
      background-image: none;
      }
      }

      .fanctytabs {
      .tab {
      background-color: #f00;
      background-image: none;
      }
      }

      I have to say I have only started using SASS a few weeks ago but nesting like the one above has saved an awful lot of head-scratching and time. For a programmer it is an obvious choice.

    • Tarabass

      This is exactly what I wanted to say. I think the biggest problem with preprocessor languages is that non-programmers are writing it most of the time and they have to change their way of thinking, which is not always a good thing. At first they found it very interesting, but it ends up with a big mess. Programmers know that they have to keep it simple, others don’t.

      For me nesting is just fine, and I’m having no problems with nesting. I always try to follow SMACSS rules, and am using nesting especially for exception-rules. Al buttons are blue (top-level), except the one on a popup for example (nested). Also I use nesting like in the tabs-example, to stay close to my component structure.

      To keep it short, preprocessors are great tools to keep your css organized. But at the end you should be able to do without (enough knowledge of css) and should have basic knowledge of program languages.

    • aaronf44

      Nailed it.

    • Matheus Victor

      This answer couldn’t be more true.

  • Josh Bambrick

    With BEM, I use selector nesting for modifiers but not elements of blocks. I think the concept of a modifier is similar to pseudo classes and pseudo selectors so I apply them both in the same way. I did start using selector nesting for elements but it made the code significantly less readable and maintainable so I had to swap back,

  • cozmo technology

    Simple 2 line code, over Complicated 1 line code — I prefer that even on a bad day, but its an art to make complicated code look simple & elegant. I believe your article is more about programming principles, and bad practices. Thus, it can be applied to programming in general. Either way, good article by demonstrating benefits of keeping it simple with the use of Sass.

  • Per

    I’m using LESS but it’s the same principle. Just add a single line comment above each selector, it takes about .5 seconds extra and makes is searchable.


    .block {
    ...

    // .block--modifier
    &--modifier {
    ...
    }

    // .block__element
    &__element {
    ...

    // .block__element--modifier
    &--modifier {
    ...
    }
    }
    }

    • http://hugogiraudel.com/ Hugo Giraudel

      Interesting idea. What’s the point of nesting at this point though?

      • Per

        It was just a reply to “… it creates new selectors out of nowhere that are thus impossible to search for.”.

        I think it’s a matter of personal choice. Myself I find this way of writing your BEM easy to understand. Sure, you’re nesting shit, but the output is not causing any specificity since it would be output like below.

        .block {
        ...
        }

        .block--modifier {
        ...
        }

        .block__element {
        ...
        }

        .block__element--modifier {
        ...
        }

        So for me the point is to keep your block and modifiers grouped in a nice way.

  • harishchouhan

    Nice article. I’m guilty of too much nesting only to confuse myself when looking at my own code after a year.

  • ultimatedelman

    Yeah, I’m gonna have to disagree with this article, although not in its entirety. I agree that nesting can be bad, sure, if you’re going too deep, your structure doesn’t make any sense, or you’re inconsistent. However, to say that nesting in 99% of cases is outright wrong is, well, outright wrong. Nesting is extremely helpful when you’re trying to scope styles (or “componentize”) and add one-offs. Using nesting for media queries is super helpful as well. I use the @content directive inside of a media query breakpoint so I can do something like:


    .element {
    //styles
    @include mobile-break {
    //styles
    }
    }

    with a media query partial that looks something like:


    @mixin mobile-break {
    @media only screen and (max-width: 360px) {
    @content;
    }
    }

    I’ve found this to be an incredible way to cohesively create responsive layouts so that you don’t have to go flipping back and forth between your styles and your media query file, and it is best achieved through nesting.

    tl;dr – nesting done wrong is bad, nesting done right is very, very good.

    • http://hugogiraudel.com/ Hugo Giraudel

      Don’t make me say what I didn’t. ;)

      I do warn against nesting selectors for wrong reasons, not against using nesting altogether.

  • James Kyle

    Reposting from a previous comment I made:

    This is not a matter of selector performance (although with larger projects the file will get ENORMOUS really quickly). I recently started a new job where an entire web app’s layout and design was contained in one large set of nested classes, mimicking the markup exactly (even though it’s a highly dynamic app with highly dynamic markup).

    This quickly becomes unmaintainable:

    1. In order to make any changes to the html, I had to be concerned with every single one of it’s children and how to update the CSS

    2. When looking at the generated CSS in the browser (slightly solved by source maps), you have to read the entire selector to try and figure out what it is selecting.

    3. Selectors can easily conflict, having an enormous tree structure can be dangerous.

    4. When you are writing longer and longer selectors, you are constantly upping the ante in terms of specificity. You’ll quickly find yourself writing even longer selectors to be more specific or even writing the dreaded !important to override something else.

    5. If you want to reuse styles from other places, copy and paste will happen (yes mixins are still just copy and paste), sure %placeholders are useful, but then your code is basically redirecting itself all over the place aaand it won’t always work as expected.

    6. Indentation isn’t a miracle worker, it’s still hard to read when you get past a few levels of indentation.

    7. Layout is doing something weird? Trying to diagnose it will frustrate you and you’ll start writing hardcoded ‘magic’ values, and hacking stuff to a visually working state (no, variables ain’t gonna save you bro).

    8. Okay now make that shit responsive.

    9. Change the positioning on something lower level, now that your code is hacked together with variables, placeholders, mixins, hardcoded variables, and stuff “you’ll refactor later”, now you have to update a ton of inner styles.

    10. Designer has redesigned X, Y, Z core parts of your app, time to start all over.

    11. (browser) has (selector) issue, when you (do something) (this style) (does something weird)… time to rethink your entire universe. (Yes this happens, I’ve run into issues like this in Chrome, Firefox, Android Browser, Mobile Safari, and every single Internet Explorer ever to exist)

    12. “Could you just move that to the other side?” (flips desk)

    13. Another team member makes a ‘quick change’ or adds ‘something small’, and now they can’t figure out why X, Y, and Z has broken. Now they are scared of your code, and now you’re the only one who can edit it. Plus your code will become a running joke (yes it will … no it will … stop arguing with me you will be made fun of … no screw you).

    14. Added another nested level higher up, now the diff is fucking enormous, and no one can see if that was the only change you made

    I’m really sure I could keep thinking of more, but I’m on the train and not too far from my stop so I’ll move on.

    Overall, it’s always better to think in terms of modularity, it may seem negligible being ‘only CSS’, but CSS gives you an incredible starting point for structuring your javascript as well.

    Take a look at stuff like BEM, OOCSS, and SMACSS. They might not tell you how to write Sass specifically, but universally will tell you to break things apart, they do so in different ways but if you pick one and stick with it you’ll be amazed.

    Well written CSS can really speed up your development, it can also slow it down drastically (fucking trust me on that shit, I know it all too well).

    Peace

  • bbmatt1

    Although you mentioned BEM, probably worth also expanding on the concept of modular CSS (and HTML). The less specific the targeting, the more versatile ‘blocks’ or ‘modules / components’ can be. This in turn means excessive nesting in Sass defeats the object of modular CSS.

  • https://medium.com/@amlinarev amlinarev

    I don’t really think there should be search related problems if the blocks are simple and separated into files. A block with three or four nested selectors shouldn’t be a problem. If you have a huge load of nested selectors you should probably reconsider refactoring your code into smaller, more manageable chunks. But keeping things DRY is really important for managing my code. Excellent article nevertheless.

  • Godwin

    In my mind, the obvious next step for SCSS development would be CSS code coverage tools to be run alongside unit or regression tests, much like the code coverage tools which have existed for some time for full scale dev languages. They could identify unused or unnecessarily complicated rules allowing developers to go back and improve the code as needed and even breaking continuous integration builds.

    Looks like the npm package css-coverage and the ruby gem deadweight may be paving the way for this level of testing, and although I have yet to use them, it looks like we will likely have a way to yet before preprocessed CSS can be fully analyzed in this way.

  • tkane2000

    I think you rewrote Micah’s code incorrectly. Should be like this:
    .tabs .tab {
    background: red;
    // …

    Which, btw, brings up an issue with specificity and long selectors.

  • markbrown4

    Nesting is great, but this is still an important reminder that it can be used for evil as well. Here’s my take, I find the second example with nesting much easier to consume than the third without nesting. In my mind I’m applying styles to different things in some context, it makes perfect sense and requires less code. https://gist.github.com/markbrown4/3c90bfd119544ddc90f1

  • Karen Menezes

    Thanks Hugo… I think with something as powerful as today’s preprocessors, we always need to take a step back and think: Will I be able to read this code if I look at it after 3 months? OR Will another developer be able to understand what’s going on? Readable code always wins over clever code, unless that clever code is doing something unique that couldn’t be done any other way…

  • Adam Shaylor

    I respect your credentials, Hugo, but this seems impossibly idealistic. To implement branding on any large scale, you really need modular code—classes that can be reused in many places but tweaked based on context. Throw multiple devs and a CSS framework into the mix and this whole article pretty much goes right out the window.

    Nesting or no nesting, SASS is a little hack on top of the bigger hack called CSS. We should really be talking about how poorly suited CSS to its job. It took 5 years to get from PostScript to Illustrator. CSS has been around for 17 years and we still have no acceptable graphical abstraction for it.

    • http://hugogiraudel.com/ Hugo Giraudel

      Nesting or no nesting, SASS is a little hack on top of the bigger hack called CSS. We should really be talking about how poorly suited CSS to its job.

      Amen to that my friend.

  • novak

    No offense but your article is like:

    If you wanna use a javascript function and you try these:


    var function{}(alert(1))

    or


    var function{}() -> alert(1)

    or
    function(
    alert(1)
    )

    You will see it’s bad and won’t work, so you don’t wanna use functions in javascript. See?

  • http://jamesmartindavies.co.uk/ James Martin-Davies

    Absolutely 100% spot-on, @mattsince87:disqus. I without fail use namespacing and nesting to create extremely maintainable code. I can change one single namespace string and my entire CSS structure will adapt that string, something I cannot express enough how useful that is when developing (also using @if statements to say truthy if I need to use them, or falsy if not). There are some weird quirks with SASS I don’t agree with (such as specificity issues with silent class extends), but nesting is 100% the best current pre-processing method, given you know how to write good, reusable and sustainable code. Plus, as a developer, you should be commenting your CSS. The tweet about BEM’d nesting is ludicrous — as any other professional developer who uses SASS plus a naming convention will tell you so. Don’t blame the tools, blame the developer. ;)

  • KonRud5

    Nice and useful article, in my own opinion the SaSS has its own advantages but the native CSS is much more readable and simple to use.

  • http://www.permanaj.net/ Permana Jayanta

    I use nested selector a lot in less. Until I start using sourcemaps features. It only point to top selector. not the child.

Recommended

Learn Coding Online
Learn Web Development

Start learning web development and design for free with SitePoint Premium!

Get the latest in Front-end, once a week, for free.