CSS nesting is here - front end developers represented as hungry chicks in a nest.
Calendar   4 July, 2024 //

Native CSS nesting is here. Is it time to ditch SCSS?

#Frontend
#HTML, CSS & Javascript
Darren Fisher, Creative Director at Pivale Drupal agency - a bearded man with dark hair and glasses.

Written by

Darren Fisher

Creative Director

Share Arrow down

CSS nesting has finally arrived in native CSS and we're so excited. Could 2024 be the year that we finally ditch the requirement for SCSS in our frontend workflows?

What is CSS nesting?

CSS nesting is how we frontend developers describe the ability to nest CSS selectors inside of other ones. For example you might be familiar with writing CSS like this:

.parent {
  color: black;
}

.parent a {
  color: blue;
}

But if you've ever used SCSS you'll have likely come across nesting that looks like this:

.parent {
  color: black;
  
  a {
    color: blue;
  }
}

An example of where this might be more practical is when using BEM CSS naming such as the following:

<div class="parent">
  <a class="parent__link" href="/">Some link</a>
</div>
.parent {
  color: black;
  
  &__link {
    color: blue;
  }
}

In SCSS the & (ampersand) character would always refer to the parent selector. In the above example the parent selector is .parent so the nested selector would become .parent__link when compiled to native CSS. Because native CSS previously hadn't supported nesting we relied on CSS preprocessors such as SCSS to write neater frontend code and compile to ugly native CSS that the browsers could actually render.

The "magic" & parent selector would continue nesting further down as well which is also useful when using BEM naming methodology. Let's consider the following example where a BEM modifier is used:

<div class="parent">
  <a class="parent__link" href="/">Some link</a>
  <a class="parent__link--secondary" href="/">Some link</a>
</div>
.parent {
  color: black;
  
  &__link {
    color: blue;
    
    &--secondary {
      color: dimgray;
    }
  }
}

The first & targets the immediate parent selector which would compile to .parent but the second & targets it's own immediate parent selector which would compile to .parent__link.

This means that the CSS output of this after preprocessing would look like this:

.parent {
  color: black;
}

.parent__link {
  color: blue;
}

.parent__link--secondary {
  color: dimgray;
}

CSS nesting allows us to visually keep logical blocks of styling together and be able to determine which selectors are the children of others.

Differences between CSS nesting and SCSS nesting

If you're familiar with SCSS nesting then this probably sounds very exciting but before you dive in and start using CSS nesting there are a few key differences between the two which I'll cover here.

To & or not to & - that is the question

In SCSS the & character was a reference to the direct parent selector but with native nesting it's not quite that simple! In native CSS nesting the & is the "nesting selector" and this differs from simply being a compilation reference to the direct parent. 

One key difference is that the & character is required when nesting selectors. Let's take the example from before:

.parent {
  color: black;
  
  a {
    color: blue;
  }
}

This will NOT WORK in native CSS nesting. Instead you'd need to do the following:

.parent {
  color: black;
  
  & a {
    color: blue;
  }
}

This is because the & selector is a live variable symbol in native CSS nesting and therefore needs to be referenced in CSS code in order for the browser to understand that you are nesting an element selector whereas in SCSS the preprocessor understood the nested syntax and compiled with the parent selector automatically prefixed. But what if the nested element isn't an element selector? Well... that leads me on to:

Native CSS nesting must start with a symbol

When using native CSS nesting you must start the nested selector with one of the following symbols:

So let's take the example above again but this time instead of targeting the <a> element let's add a class to it like this <a class="link"... and refactor the code we used above:

.parent {
  color: black;

  .link {
    color: blue;
  }
}

This WILL WORK and is perfectly valid native CSS syntax. So in short always use the & nesting selector when targeting an HTML element directly or always use another selector such as a class! To quote Kevin Powell "I think the right way to think about it is just that you always need to have a symbol" when nesting in native CSS. But as Kevin goes on to point out in the same YouTube video if this is too much cognitive load you can always just include the & nesting selector and it will still work as demonstrated in the following example:

.parent {
  color: black;
  
  & .link {
    color: blue;
  }
}

Who said CSS was complicated?!

So how does native CSS nesting work with BEM?

Here's the bad news! Native CSS nesting doesn't play nicely with the BEM naming convention.

As I previously alluded to the & nesting selector in native CSS is a live object and not a simple string representation of the parent selector and that means that we can't do string concatenation for BEM selectors like what we might be used to with SCSS nesting. Don't try it. It will break!

The fact that this isn't possible is a major blocker right now for our Drupal agency team and we have everything crossed that this becomes possible in the near future.

Similarities between CSS nesting and SCSS nesting

Creating compound CSS classes

We can create compound selectors by omitting the space between the & nesting selector and any compound class though. This is demonstrated in the following example:

<button class="btn blue">A blue button</button>
<button class="btn red">A red button</button>
.btn {
  &.blue {
    background-color: blue;
  }
  
  &.red {
    background-color: red;
  }
}

Targeting pseudo classes

Pseudo classes can be targeted using native CSS nesting just the same as we have been used to with SCSS:

.btn {
  :hover,
  :focus {
    text-decoration: underline;
  }
  
  :visited {
   color: purple;
  }
}

Infinite nesting

Firstly, let me say I don't like this and I hope you don't either AND you really shouldn't do it... but... you can carry on nesting until the cows come home if you're the sort of psychopath that likes writing frontend code like this:

.parent {
  .child {
    & ul {
      & li {
        & a {
          :not(:visited) {
            :hover {
              translate: 0 -3px;
            }
          }
        }
      }
    }
  }
}

Using the & as a suffix to target a parent

One of the coolest features of SCSS nesting that I was almost certain wouldn't make it in to the spec for native CSS nesting is the ability to suffix the & character in order to create parent selector conditional rules. Let's create an example where we want to create some conditional colours for a particular element if the parent has a modifier class of some sort:

<div class="box">...</div>
<div class="box dark">...</div>
.box {
  background-color: #eee;
  
  & p {
    color: #111;
    
    .dark & {
      color: #eee;
    }
  }
  
  .dark& {
    background-color: #111;
  }
}

Notice that because the .dark modifier is on the div element with a class of 'box' the & is used as a suffix without a space when nested in the .box class directly but when the .dark modifier class is nested inside of the p selector a space is required before the & suffix in order for the browser to understand we're referring to the parent .box and not a .dark modifier on the p selector itself.

Learn CSS they said. It will be fun they said!

Nested media queries

One thing I absolutely HATED was when I had to write media queries in native CSS. Media queries had to live at the root of the stylesheet because CSS nesting wasn't a thing and that meant that any conditional styling for a particular selector had to live in a completely separate block and this was easily the fastest way for a stylesheet to get out of hand. But now that we have native CSS nesting we can do it just like we do it in SCSS:

.card {
  .heading {
    font-size: 1.1rem;
    
    @media (min-width: 780px) {
      font-size: 1.3rem;
    }
  }
}

Browser support

Data on support for the css-nesting feature across the major browsers from caniuse.com

So is now the time to ditch SCSS in favour of native CSS nesting?

In my opinion, no! We're so close but without the ability to do string concatenation for simplified BEM naming convention selectors it feels like we're still going to need SCSS in order to keep our components name-spaced and accompanying stylesheets easy to write, read, and maintain. I'm keeping my ear to the ground on this issue and will pop back to update this article if and when the state of play changes.

If you're not using BEM naming convention or something similar then I'd probably say the answer is yes. We used to choose SCSS over CSS because it gave us nesting and variables primarily. SCSS variables have since been superseded by powerful native CSS custom properties and CSS nesting is finally here after years of front end developers demanding it.

I personally think the mixins and inheritance functionality that SCSS provides can lead to over-bloated CSS stylesheets and have avoided using these for the longest time. The ability to write imports has been useful in the past but it seems like web components and smaller CSS files per component might be the new standard on the web although the jury is still out on this one in terms of performance.

One thing that SCSS gives you which is still missing in native CSS is custom functions which is a powerful piece of functionality that we utilise too little and often, and perhaps will continue to under-utilise in order to make it easier to migrate away in the future.

Here's the thing... there's no real reason to stop using SCSS. It doesn't create any issues if you use it properly and it rarely breaks. It isn't required at runtime so doesn't create performance issues and that's why we'll be sticking with it for the time being!

Darren Fisher, Creative Director at Pivale Drupal agency - a bearded man with dark hair and glasses.

Written by

Darren Fisher

Creative Director

Darren is our creative director, responsible for our design and frontend development team as well as managing the majority of our website and multisite builds. Darren is a graduate of the University for the Creative Arts, achieving a bachelor's degree in Digital Screen Arts.

The Pivale team from left to right - Pri Scarabelli, Julie Manning, Barry Fisher, Darren Fisher, and Daniel Johnson.

Who are Pivale?

Let's talk