Tip of the week #011: Nesting in SCSS

In SCSS it's possible to nest your CSS. It speeds up your workflow, but it also invites you to overdo it. Here's my two cents when it comes to when and how you should nest your code.

Let's look at this simple card component:

Language: html
<article class="card">
  <div class="inner">
    <h2 class="title">Nesting in SCSS</h2>
  </div>
</article>
Language: scss
.card {
  background-color: #FFF;
  border-radius: 1rem;
  padding: 1.5rem;
}

.inner {
  background-color: #EEE;
  border-radius: .5rem;
}

.title {
  font-size: 1.5rem;
  font-weight: 600;
}

This component has very generic class names, like .inner and .title. A problem arises when you have another component with the same class name.

Language: html
<article class="card">
  <div class="inner">
    <h2 class="title">Nesting in SCSS</h2>
  </div>
</article>

<div class="news-section">
  <h2 class="title">Recent news</h2>
</div>

If I don't want "Recent news" to share the same style of .title I must either change the class name to something else, or I can style .title to have a different specificity depending on where it's used.

Language: scss
.title {
  font-weight: 600;
}

.card {
  background-color: #FFF;
  border-radius: 1rem;
  padding: 1.5rem;

  .title {
    font-size: 1.5rem;
  }
}

.news-section {
  .title {
    font-size: 2rem;
  }
}

This works. The compilated version of this code, the code that the browser will read, becomes this:

Language: css
.title {
  font-weight: 600;
}

.card {
  background-color: #FFF;
  border-radius: 1rem;
  padding: 1.5rem;
}

.card .title {
  font-size: 1.5rem;
}

.news-section .title {
  font-size: 2rem;
}

However, naming classes and creating different specificities like this is not a good approach, in my opinion. I think the nature of SCSS nesting invites you to overdo it because it feels good to look at nested selectors that mirrors the nesting in the HTML:

Language: scss
/* Mirrors the HTML */
.card {
  background-color: #FFF;
  border-radius: 1rem;
  padding: 1.5rem;
  
  .inner {
    background-color: #EEE;
    border-radius: .5rem;
    
    .title {
      font-size: 1.5rem;
      font-weight: 600;
    }
  }
}

The .title class here will be compiled as the selector .card .inner .title, three levels deep, as seen here:

Language: css
.card {
  background-color: #FFF;
  border-radius: 1rem;
  padding: 1.5rem;
}

.card .inner {
  background-color: #FFF;
  border-radius: .5rem;
}

.card .inner .title {
  font-size: 1.5rem;
  font-weight: 600;
}

Two levels deep (.card .title) would probably suffice.

My rule of thumb is therefore to limit the nesting to two levels.

Using BEM

BEM is a naming convention that in my opinion is the best way to create scalable, maintainable stylesheets. I won't go into too much details about the convention itself (you can read more about it here), but nesting in SCSS helps speed up the BEM naming process. Here's an example:

Language: html
<article class="card">
  <div class="card__inner">
    <h2 class="card__title">Nesting in SCSS</h2>
  </div>
</article>
Language: scss
.card {
  background-color: #FFF;
  border-radius: 1rem;
  padding: 1.5rem;
  
  &__inner {
    background-color: #EEE;
    border-radius: .5rem;
  }

  &__title {
    font-size: 1.5rem;
    font-weight: 600;
  }
}

The & in SCSS creates class names based on the parent selector, like this:

Language: css
.card {
  background-color: #FFF;
  border-radius: 1rem;
  padding: 1.5rem;
}

.card__inner {
  background-color: #EEE;
  border-radius: .5rem;
}

.card__title {
  font-size: 1.5rem;
  font-weight: 600;
}

In conclusion

Nesting in SCSS is a nice way to create higher specificity, but it invites you to mirror the HTML structure, resulting in long, overcomplicated CSS selectors.

Combining BEM with nesting is all in all a good way to write CSS (if you do it correctly).