Should You Use Native CSS Nesting Instead of Sass?
Should You Use Native CSS Nesting Instead of Sass?
Yes for new projects, with one caveat that catches teams out. Native nesting is widely available and reads almost identically to Sass, but it deliberately refuses one Sass habit that most component naming conventions depend on. Know that before you migrate anything.
We have been writing nesting into new builds for a while now and the experience is better than we expected. What changed our minds was not the syntax. It was deleting a build step from projects that only had one because of nesting.
This piece compares the two honestly, including where native nesting is worse, and where it quietly changes how your stylesheet behaves.
What Is Native CSS Nesting, and When Did It Become Safe to Use?
It lets you write a rule inside another rule, so a component's styles live together instead of being spread across a flat list of selectors. The browser does the flattening. MDN lists the nesting selector as Baseline "Widely available," noting it has "been available across browsers since December 2023."
Widely available is the status that matters for production. In the Baseline model it means a feature is established across the major engines rather than newly shipped, so you are not the one finding the bugs. We wrote about reading those labels properly in our piece on Baseline web features.
What that status does not tell you is how the feature behaves at the edges, and nesting has real edges. The syntax looks like Sass, which is exactly why people assume it works like Sass and then spend an afternoon confused.
What Does the Ampersand Actually Do?
It makes the nested selector relative to the parent element rather than to the parent's children. MDN puts it plainly: the nesting selector "makes the nested child rule selectors relative to the parent element," and "without the & nesting selector, the child rule selector selects child elements."
That single sentence explains most nesting mistakes. Writing a class inside another class targets descendants, not the same element. If you want both classes on one element, the ampersand is mandatory.
MDN is explicit about when it is required: "when using compound selectors in nested CSS you have to use the & nesting selector," because "the browser will automatically add whitespace between selectors that do not use the & nesting selector." That automatic whitespace is the whole trap.
So a nested rule without an ampersand styles a child. With one, it styles the same element. In Sass the distinction exists too, but Sass users get away with sloppiness that native CSS silently reinterprets. Our habit now is to write the ampersand even where it is optional, purely as a readability signal.
What Can Sass Nesting Do That Native Nesting Cannot?
Build class names by joining strings. This is the caveat, and it is not a bug or a missing feature. It is a deliberate design decision, and it breaks the most popular way of writing component classes.
MDN states the restriction with a warning. In preprocessors "it is possible to use nesting to join strings to create new classes," but "this is not possible in CSS nesting: when a combinator is not used, the nested selector is treated as a type selector. Allowing concatenation would break this."
The consequence is concrete. If your naming convention nests an element suffix under a block class to generate the full class name, that pattern does not compile natively. You have to write the full class name out. There is no workaround, because allowing one would make bare element selectors ambiguous.
There is a related trick worth knowing. MDN shows that you can put a type selector before the ampersand, so a rule written as an element followed by the ampersand "parses as: element.my-class." Useful occasionally, and not a substitute for concatenation.
| Capability | Native CSS nesting | Sass nesting |
|---|---|---|
| Nest rules inside rules | Yes | Yes |
| Join strings to build a class name | Not possible by design | Yes |
| Needs a build step | No | Yes |
| Specificity of nested selectors | Weighted as if wrapped in :is() | Plain flattened selector |
| Debuggable in browser dev tools | Source is the shipped CSS | Needs source maps |
| Variables, mixins, loops | Custom properties only | Full language |
Does Nesting Change Your Specificity?
Yes, and this is the difference that will actually bite you during a migration. MDN states that with native nesting, "the child rule selectors have the same specificity weight as if they were within :is()." Sass output has no such wrapper.
Why that matters comes down to how that pseudo-class counts. MDN's page on it says ":is() counts towards the specificity of the overall selector (it takes the specificity of its most specific argument)," in contrast to the where pseudo-class, which "has a specificity value of 0."
So a nested rule takes the specificity of the most specific thing in its parent selector list. If your parent is a list containing one very specific selector, every nested rule inherits that weight, even for elements the specific selector never matched. In Sass, each flattened selector carried only its own weight.
The practical outcome is that a stylesheet converted mechanically from Sass to native nesting can produce different winners in a specificity contest. Nothing looks broken in the code. One override just stops applying.
If your project already uses cascade layers, this gets much easier to reason about, because layer order beats specificity across layers. Our guide to CSS cascade layers covers how we structure that.
Does Native Nesting Mean You Can Drop Your Build Step?
Only if nesting was the reason you had one. For plenty of marketing sites it genuinely was, and removing the preprocessor removes a dependency, a config file, and a class of source map problems.
Be honest about what else the preprocessor was doing. If you use mixins, loops, functions, or compile time maths, native CSS does not replace those. Custom properties cover variables well and cover nothing else. A project using Sass for a type scale generated in a loop is not a candidate.
The debugging gain is the part we did not anticipate. When the CSS in the browser is the CSS you wrote, dev tools point at the real line. That sounds minor until you spend an hour chasing a source map that regenerated wrong. Our piece on CSS frameworks versus vanilla CSS covers the wider version of this trade.
How Deep Should You Nest?
Two levels, three at the absolute limit. This was true in Sass and it is still true now. Nesting is a readability tool, and past a couple of levels it stops aiding readability and starts hiding what a selector actually matches.
Deep nesting also produces long descendant selectors that are hard to override deliberately and easy to override accidentally. With the specificity behaviour above in play, the cost of a deeply nested rule is less predictable than it used to be.
Our working rule is that if you need a third level, the inner thing probably wants its own class and its own top level rule. That change usually makes the stylesheet shorter, not longer.
What Does This Mean for a Webflow or Client-First Project?
Less than you might think, because the Designer produces flat classes and you rarely hand write the component CSS. Where it matters is custom code blocks, and there native nesting is now a safe default rather than something to avoid.
The naming convention point is the one to watch. Conventions that build class names from a block name plus a suffix are written out in full in Webflow anyway, so the concatenation restriction costs you nothing. You were already typing the whole class name.
For a custom code block that styles a small widget, nesting genuinely helps. One rule with its states and children inside it, no repeated parent selector, and no build step to add to a project that otherwise has none.
Which One Would We Pick on a New Build?
Native nesting, with cascade layers for structure and custom properties for tokens. We would reach for a preprocessor only when the project needs real programming in the stylesheet, and most marketing sites do not.
For an existing Sass codebase, we would not migrate for its own sake. The specificity change means a mechanical conversion is a real risk with no user visible benefit, and there is no deadline forcing it. Write new components natively and leave the old ones alone.
The one situation where we would push for a migration is a project whose only use of Sass is nesting and variables. There the preprocessor is pure overhead, and the conversion is small enough to review properly.
Where Should You Start?
With one new component, written natively, in a project that already ships. Not a migration and not a spike. Write the component, check it in dev tools, and see whether the flattened output is what you expected.
Then go looking for specificity surprises deliberately. Add an override you expect to win and confirm it does. That five minute experiment teaches the :is() behaviour better than any explanation, including this one.
If you are weighing whether to simplify a stylesheet or a build pipeline on an existing site, we are happy to look at it with you. You can find us at phoenix.studio.
Want a site that performs like this?
Tell us about your project. We will come back with a clear next step, no pressure.
This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.
Have a project like this?
Tell us where you want to go. We'll tell you how we'd get you there.