CSS Nesting and the Cascade

You might have noticed that Safari Technology Preview 179 includes an update to CSS Nesting that adds support for the new “relaxed parsing behavior”.

What does this mean? It means you no longer have to worry about whether or not each nested selector starts with a symbol. It means that now nested CSS like this will work just fine:

article {
  h1 { 
    font-size: 1.8rem;
  }
  p {
    font-size: 1.2rem;  
  }
}

(If you didn’t realize there was a previous limitation and are curious about what it was, you can read about it in Try out CSS Nesting today, from Feb 2023. But also, you can ignore this limitation since it’s going away soon.)

This is fantastic news. After many months of debates over how CSS Nesting could work, we ended up with the best possible solution. In the end, browser engineers figured out how to make the parsing engine handle nested type selectors (element selectors).

How is browser support? In late August 2023, Firefox 117 shipped support for Nesting using the relaxed parsing behavior from the beginning. Safari 16.5 shipped the original version of Nesting in May 2023, and Safari Technology Preview 179 brought the update to the relaxed parsing behavior in September 2023. Chrome is tracking their coming update in this issue.

By the way, any code written with an & will continue to work. In fact, & is an important tool for CSS like this:

ul {
  padding-left: 1em;
  article & {
    padding-left: 0;
  }
}

Which is the equivalent of:

ul {
  padding-left: 1em;
}
article ul {
  padding-left: 0;
}

The & gives you a way to say “this is where I want the nested selector to go”. It still does the same job, it’s simply no longer required before an element selector.

Another question

There is one more thing about CSS Nesting that’s still up for debate. We still have time to make the change if we do so very soon.

Let us ask you a question. If you wrote this nested CSS, which color would you want the article text to be?

article {
  color: blue;
  @supports (text-shadow: 0 0) {
    color: red;
  }
  color: yellow;
}

Do you want it to result in this unnested equivalent, Option 1, where color: red wins?

article {
  color: blue;
  color: yellow; 
}
@supports (text-shadow: 0 0) {
  article {
      color: red;
  }
}

Or do you want it be computed to be this equivalent, Option 2, where color: yellow wins?

article {
  color: blue;
}
@supports (text-shadow: 0 0) {
  article {
      color: red;
  }
}
article {
  color: yellow; 
}

Currently, the Nesting specification says Option 1 is correct, and so that’s how it’s implemented in browsers. This is how all of the preprocessors work: Less (demo), Sass (demo), Stylus (demo), PostCSS (demo), and more. Perhaps matching over fifteen years of third-party tooling is the best way to go.

But many people find this to be an unexpected gotcha, since it seemingly reorders styles. It makes something that’s earlier in the cascade override something that’s later in the cascade. These folks expect Option 2 to be how it works, where the fundamentals of the cascade stay fully intact — when two declarations have the same specificity, the later one always wins.

We ran a survey to find out what you want. The results were remarkable consistent across the first 48 hours.

Option 1: 38%
Option 2: 62%

Your input helps the CSS Working Group make a final decision on how code like this should work. It’s still not fully clear if Option 2 is possible, but before embarking on a deeper effort to find out, it helps to know what web developers want.

Thanks for participating!