Most popular arguments against nested CSS

Let’s list the most popular reasons for avoiding nested CSS, and my response to these reasons.

1. Claim: Nesting creates high specificity, which is harder to override

This is true, locally scoped CSS is harder to override from the outside. But here is an idea: you should avoid overriding your CSS anyway. What if overriding something in your code indicates that you should have structured it some other way? What if making overriding easy just sweeps this fact under the carpet?

Here are 2 situations when you need to override your CSS you can stumble upon (when you use high specificity selectors in particular), and why both situations are fine:

If you are overriding a small amount of css rules, — then high specificity shouldn’t be a significant issue for you, just throw in 3 !important s in your css, that won’t make it worse. If you are overriding a lot of css rules, — then something went wrong!

- If you are overriding your own css, — it means your css was NOT SPECIFIC ENOUGH at some point. It means you are overriding something that should have been scoped under a more specific rule.

- If you are overriding (a lot of) css of some library you use, — it means that that library should have provided you with some means of not including their css at all. This can be achieved by, for example, passing your own classNames for particular components of said library, if you don’t need default styles for those particular components.

Avoiding high specificity in CSS is akin to avoiding locally scoped variables. It’s true that we can’t reuse our .foobar somewhere higher up the scope without explicitly moving it up the scope. But it means that our namespace is not littered with unused variables, and it means that we won’t have the need for overrides if we structure our CSS well. Don’t override, refactor:

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. 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.

2. Claim: CSS resulting from deep nesting harms performance

There are two concerns regarding the performance of deeply-nested CSS —

1. Files resulting from deep nesting turn out bigger, and

2. Css rules in the form of header > ul > li > a slow down CSS rendering.

The first concern is based on the fact that this scss, for example:

header > .container {

background: red;

> a.logo {

border-radius: 5px;

}

> nav {

color: blue;

}

}

will expand into this css:

header > .container { background: red; }

header > .container > a.logo { border-radius: 5px; }

header > .container > nav { color: blue; }

which has a lot of repetition in it, and it means that the .css file we’ll be sending over the wire will be significantly larger than if we were to avoid nesting.

However, if you care about performance, all of your css files are probably already getting gzipped. Compression in gzip is achieved primarily via the matching and replacement of duplicate strings.

We should not worry about it more than we worry about file size when we use, for example, BEM methodology, with its 26-symbol classnames ( .uppy-dashboardItem-preview ).

The second concern is based on the fact that the fastest selectors to find for CSSDom are id-based, and class-based. The difference between the nested selector (e.g. .box > .title ) and class selector (e.g. .box--title ) is 0.8337ms for 50000 elements (which is a very significant amount of elements per page). That’s 0.0008th of a second. For 50000 elements.

Overall, about performance, — I believe performance is frequently scapegoated when we want to prove something in programming.

We use ‘performance’ as an argument, because it’s something measurable, something objective, it makes our argument sound substantial.

I hope I dispelled some worries about performance above, but I come at it from a place of, that, as Matthew Jones said, — performance doesn’t matter unless you can prove that it does.

3. Claim: Tight coupling between your CSS and HTML is bad

Another concern I stumbled upon is ‘Any change you make to your markup will need to be reflected into your Sass and vice versa.’ (http://thesassway.com/beginner/the-inception-rule).

My answer to that is — yes, you will have to change your CSS if you change your markup. Many changes to your HTML markup will need to be reflected in your SCSS.

A classic example of when deeply nested CSS requires updates that flat classname-based CSS wouldn’t, is when, in our HTML, we move a child of some parent element out of that parent.

For example, we moved ul out of nav . Then we’d go from:

header {

background: red;

> a.logo {

border-radius: 5px;

}

> nav {

color: blue;

> ul{

list-style-type: none;

}

}

}

to:

header {

background: red;

> a.logo {

border-radius: 5px;

}

> nav {

color: blue;

}

> ul{

list-style-type: none;

}

}

Why would we want to perform this extra step, right? Isn’t it a sign of good code, - when we don’t need to change something just because we changed something else?

Not always. We would want this extra step, because in the real world this is not the css you’d have on your site.

In the real world, your CSS is filled with { display: flex; } , with { width: 80%; } , and with { float: left; } , you got the gist. It doesn’t matter what positioning method you use, — you want to know what elements surround you. What is the display value of your siblings, whether they are block-level or inline, what widths and heights they take up, and what position value their parent has.

When I refactor SCSS code (otherwise amazing code!) to use strict nesting, I frequently discover unneeded CSS rules, dangling selectors that don’t exist in HTML anymore (is that the loose coupling we wanted?), and excessive HTML tags. All of this could be more easily avoided, if the coupling between HTML and CSS was tighter rather than looser.

Nesting can give us an amazing overview of our HTML as Jason Zimdar noticed, e.g.:

Keep in mind that in the real world this will not be <html> , or <body> . The highest parent selector will be a local unique className defined for a <section> , usually it will be autogenerated for you (in the standard React+Webpack setup at least, see CSS modules).

HTML and CSS are already tightly coupled, there is no way around it, and it’s better when this coupling is visually apparent in your CSS files.

4. Claim: Readability - do we really want our selectors indented by 10 tabs?