Native CSS Nesting: A Primer
UPDATE NOVEMBER 13, 2023: CSS Nesting is now available in the current version of all major browsers. I've updated the post to reflect this.
UPDATE April 9, 2023: CSS Nesting is now available in stable versions of Google Chrome and Microsoft Edge. Firefox support is still a question mark at this point. If you'd like to use CSS Nesting today, consider using the graceful degradation technique described at the end of this piece.
Pre-processors such as Sass and Less introduced nesting to CSS development. Nesting refers to the ability to group related CSS rules inside of a parent ruleset. It speeds the process of writing CSS because you don't have to re-type selectors. It can also improve the readability and maintainability of CSS by grouping related styles.
Thanks to its popularity and convenience, nesting is now a native feature of CSS. You get one of the benefits of using Sass or Less without the need for an external library and build process.
CSS Nestingisn't yet ready for prime-time, though. Support is currently limited to Google Chrome and Microsoft Edge (versions 112 and later), and Safari Technology Preview. is available in the latest versions of all major browsers.
I'm assuming that you're already familiar with Less or Sass/SCSS and CSS.
Nesting syntax basics
CSS Nesting syntax resembles that of Sass/SCSS and Less, but its grammar has some significant differences.
As with SCSS and Less, nested rulesets are contained within a parent declaration block, as demonstrated in example 1.
Earlier versions of the CSS Nesting specification required use of the &
nesting selector in order when nesting type selectors such as p
and img
(see example 2).
More recent versions of the specification have relaxed that rule. Using the nesting selector is now optional in many cases. It's useful to understand how it works.
One note of caution: as of November 13, 2023, only Firefox, Chrome Canary and Safari Technology Preview support the more recent spec. These changes will ship in near-future releases of Safari, Chrome, and Edge.However, CSS Nesting requires that nested selectors begin with a relative selector or the nesting selector (&
). Otherwise, the nested rule is ignored.
In example 1, the nested img
ruleset is invalid CSS. This syntax is perfectly fine for pre-processors. Less and Sass both transform that selector to .parent img
. This syntax does not work for native nesting.
Relative selectors
Type or element selectors are not relative selectors. That's why the img
ruleset from example 1 fails.
Relative selectors are a category of CSS selector. They include:
- id selectors such as #primary
;
- class selectors, e.g. .media__object
;
- attribute selectors, such as [alt]
or [rel="noopener"]
;
- pseudo-class selectors like :hover
, :focus
, or :disabled
;
- pseudo-elements such as ::before
and ::after
;
- and selectors that begin with a combinator, such as > img
or + p
.
To match type elements in a nested rule you can either:
* Use a combinator.
* Prefix it with the nesting selector.
* Use the :is()
pseudo-class.
Here's a rewritten version of the nested img
ruleset from example 1.
Changing img
to & img
or :is(img)
makes the ruleset valid.
Invalid nested rules and their parent rules
Although browsers ignore invalid nested rules, an invalid nested rule does not invalidate its parent ruleset. In this case, elements that match .parent
will still have red text. An invalid rule can, however, prevent a subsequent sibling rule from being applied. Sibling rulesets or declaration added after the img
ruleset would be ignored.
&
: The nesting selector
The &
nesting selector works a bit like a variable or placeholder for the selector of its parent rule. You can use the nesting selector anywhere in a selector list, and you can use it more than once.
One such case is when you're targeting a descendant element of the same type as the parent — think nested div
or ul
elements, as shown in example 3.
Note the use of & &
with a space — the descendant combinator — between each ampersand. It's the equivalent of typing ul ul
.
Browsers assume that a nested rule matches descendants of the parent selector unless you indicate otherwise. Consider the following example.
The above rules only match a
elements that are descendants of p
elements. It's equivalent to the following CSS.
Of course, descendant selectors aren't the only kind of selectors that exist in CSS. You can also use the &
selector to match elements that have a different relationship.
For example, use &
to incorporate the parent rule's selector as part of a descendant selector, as shown in example 6.
Or use &
to match child pseudo-elements and qualify pseudo-classes such as :hover
and :not()
.
In this case, it's necessary to use &
. Without it, our rule would match every pseudo-element descendant of .fancy-aside
, including .fancy-aside a::before
and .fancy-aside li::after
.
&
is not a concatenation operator
CSS Nesting is not a drop-in replacement for pre-processors. Sass/SCSS, for example, uses &
as a concatenation operator within nested rulesets (example 8).
When compiled, Sass converts SCSS to valid CSS (example 9).
CSS Nesting does not support this at all. You need to type the full .accordion__trigger
selector, whether or not you nest those rulesets.
You also can't use the nesting selector to represent pseudo-elements. The following CSS does not work.
Nesting requires that the resulting selector is a valid one.Note that pre-processors still compile this to CSS. In both cases, however, browsers will ignore the rule set. After all, .subhead::before:hover is not a valid selector.
Nesting multiple levels
The CSS Nesting specification does not specify a maximum nesting level. In the example that follows, rules are nested four levels deep.
Unlike pre-processors, nested native CSS does not result in larger CSS files. You may, however, choose to limit nesting depth to maximize the reusability and readability of your CSS.
Specificity
Nesting alone does not increase the specificity of a selector compared to its un-nested equivalent. In other words, the rulesets below have the same specificity.
The more you nest, however, the more specific your selectors become. Highly-specific selectors can make it difficult to reuse existing CSS to create new layouts and variations of components.
Order of appearance
Nested rules are always handled as though they occur after the parent rule, even if that's not how they're ordered in the source. Consider example 12.
It's the equivalent of the CSS shown in example 13.
For the sake of readability, group your parent rule's declarations together, at the top of the declaration block.
Nesting @
-rules
Yes, you can also nest at-rules within a ruleset. This includes conditional at-rules, such as @media
, and @supports
. It also includes the @container
and @layer
rules. The syntax is nearly identical to the way it's done in SCSS and Less.
The preceeding CSS equivalent to the CSS in example 15.
When parsed, the at-rule is applied as though it follows its parent rule. Specificity, cascade, and inheritance behave as you'd expect.
Testing for CSS Nesting support
You can use @supports
and its selector()
function to conditionally apply CSS in browsers that support nesting.
Since the fallback for nested CSS is to use un-nested CSS, though, this does not make much sense. You'd end up sending about twice as much CSS over the network.
One alternative is to use the CSS Object Model and the supports()
function to conditionally load a style sheet that contains nesting if the browser supports CSS Nesting, to load a Sass- or Less-compiled style sheet if the browser does not support CSS Nesting. of the style sheet if it does not.
Bear in mind, however, this technique doesn't yet work with Safari. As of this writing, Safari Technology Preview returns false
for CSS.supports('selector( & )')
.
Bear in mind, however, that some recently outdated browser versions may return a false negative.
Can I use it?
CSS Nesting is only available in the development / experimental versions of Chrome and Edge, as of version 112. and Safari Technology Preview also supports nesting. It's likely to ship in Safari 16.5. Its specification is still in flux. Behavior and syntax could change between now and when CSS Nesting ships in stable browser versions. Firefox’ development of this feature has not yet begun. Firefox which still has about 6% of desktop marketshare in the United States.
Yes. CSS Nesting is available in every major browser. To be on the safe side, continue to use the nesting selector (&
) with type selectors.
That said, it is It's also a great time to experiment with CSS Nesting. If Sass or Less is part of your workflow, prepare your .less
and .scss
files for a shift to native nesting. Eliminate instances where you've used &
as a concatenator. Move away from variables in favor of using CSS Custom Properties. Begin rewriting and removing mixins, extends, exports, and functions.
Avoid using CSS Nesting in public-facing, production sites for now, though. Your CSS will fail for most of your audience.
CSS has come a long way since its early days. Features like CSS nesting help us write it much more efficiently.