Steven Poulton

@CalinLeafshade

Designing better CSS with a single step

CSS preprocessors like SASS and Less gave us a whole slew of new syntax designed to reduce computer-reproducible bloat within our style sheets.

We got variables for our colours and dimensions, we got loops for our grid systems and complicated typographic boiler plates and we got selector nesting for syntactic encapsulation of our components.

One of these things is probably encouraging you to design bad systems.

CSS is a funny language because the number of choices available to a developer at the micro level is usually 1. There’s only one way to make a div float left and there’s only one way to make the text colour a wonderful shade of magenta (with a lime green background).

So therefore you should direct your brain power at the macro level, namely designing solid design systems. The problematic new syntax I mentioned above that’s insidiously impeding that is selector nesting and you should ditch it. I say insidiously because it just looks so damn clean. It looks like the right thing to do.

First, let’s look at what I mean by selector nesting:

.comment {
  .comment__header { // Semantic nesting
color: black;
}
  &__body { // Syntactic nesting
  }
}

There are two kinds of nesting above. One is worse than the other but they both have problems.

Semantic Nesting

Semantic nesting changes the meaning of the CSS that is generated. In the example above, the second selector states:

“Select all elements with class comment_header that are children of an element with class comment”

That seems fairly reasonable at first glance but it has a couple of negative side-effects:

It has unnecessarily raised the specificity of the selector

Nesting a selector in CSS increases what’s called its specificity. This means that it will override any selectors with a lower specificity value. (If two selectors have the same specificity, the last declared one takes precedence.)

Selector specificity needs to be what Harry Roberts calls “well managed” at all times. This broadly means that our specificity should remain as consistent as possible, rising only when necessary to override previous selectors. This keeps our CSS predictable and avoids !important-hell. Here we are increasing the specificity of the selector for no real benefit. It’s true that we gain the encapsulation but a good naming convention (BEM for instance) will provide that.

For instance, imagine I wanted to make a particular comment have a highlighted header using the following utility class:

.highlight {
color: red;
font-weight: 600;
}

If I apply my highlight class to the comment__header element the color will remain black because the comment__header class has a higher specificity.

Alternatively I could make a new class at the same specificity as the header like so:

.comment .comment__header--is-highlighted {
color: red;
font-weight: 600;
}

That would certainly solve the issue at hand but it brings me nicely onto my next point.

It discourages you from thinking in terms of composable systems

Nesting your selectors gives you an illusory sense of encapsulation by tying the CSS to the markup. It makes you think of whatever component you start with as the “top” component where everything else is contextually connected to its parent when it often isn’t.

Returning to the example CSS from above, do you really think that comment headers are sufficiently unique to require their own class or do you think we could build them from other design elements in our toolkit?

In reality, a comment header probably looks very similar to other kinds of headers in your app but because you’ve nested the selectors, almost without thinking, you’ve attached contextual baggage to the component that just isn’t there.

If you apply the Single Responsibility Principle to your CSS selectors, you start to notice them becoming much more composable and removing those selectors from useless contexts makes that so much easier to conceptualize.

One final, related point and I’ll let you get back to your gulping and grunting:

It hides the intent of the rules in the selector

Often by tying our CSS so closely to our markup we make statements about the selected elements that we don’t really mean. Permit me a frivolous example:

.whitehouse {
.president {
color: orange;
}
}

This CSS makes the categorical statement that the president is orange when and only when he is inside The White House. In reality the president is orange in many different contexts and so our CSS is lying by omission.

Ok, so when can I semantically nest selectors? Never?

Selectors should only be nested when they are part of a composed system, overriding previously stated functionality and with clear and limited intent.

.button {
background-color: green;
color: magenta;
padding: 8px 12px;
border-radius: 5px;
}
.button-bar {
.button {
padding: 4px 8px;
}
}

Here we have a composition of two different components. The button component and button-bar component. The button is entirely stand-alone and can be used in lots of different contexts but when it’s inside a button-bar, the padding needs to be reduced to make it fit nicely.

Here we have a clear change of context, a need to override previous styles and a clearly defined intent for the nested rules.

Syntactic Nesting

Above, I demonstrated semantic and syntactic nesting. Where semantic nesting changes the meaning of the CSS, syntactic nesting is purely syntactic sugar for a non-nested css selector.

.class {
&--active { }
}
// Compiles to:
.class {}
.class--active {}

As you can see, this results in two classes with the same specificity but all the “softer” issues I mentioned above still apply in terms of the way you conceptualize your components.

In addition, it also makes your code unsearchable which can be a drag if you’re into that kind of thing.

Worst selector I’ve ever seen? Behold:

.home #content main .entry-content ol li p span, .page-template-page-home #content main .entry-content ol li p span, .contact-page #content main .form ul li p a span 

The horror.

If you liked this article please remember to click the💚 symbol below to make sure more people get to read it!

Steven Poulton is a web developer and technical architect living in Manchester, England. In his spare time he likes to make indie music, make indie games and play with his indie cats.

More by Steven Poulton

Topics of interest

More Related Stories