CSS Nesting: Native Browser Support
Native CSS nesting is here. Learn the syntax, the ampersand rules, how it compares to Sass, common gotchas with pseudo-classes and media queries, and how to adopt it without breaking older browsers.
What you'll learn
- ✓How native CSS nesting works without a preprocessor
- ✓When the ampersand is required and when it is implicit
- ✓How nested media queries and pseudo-classes behave
- ✓Differences from Sass and Less
- ✓How to adopt nesting safely in real projects
Prerequisites
- •Basic CSS knowledge
What and Why
CSS nesting lets you write child rules inside parent rules, the same way Sass and Less have for years. The difference is that browsers now do it natively. No build step, no preprocessor, no extra source map. You ship the file you wrote.
The why is locality. Component styles tend to live together: a button, its hover state, its disabled state, its variants. Writing them as separate selectors works, but you repeat the parent selector every time and your eye has to jump around. Nesting keeps related rules in one block, which makes refactors safer and intent clearer.
Mental Model
Think of a nested rule as syntactic sugar for a flattened selector that joins the outer and inner. By default, the inner selector is interpreted as a descendant. When you need the inner selector to attach to the parent itself, or to a sibling, use the ampersand & to refer to the parent.
Three rules cover ninety percent of cases:
- Plain selector inside a rule means descendant.
&is replaced by the full parent selector.- Pseudo-classes and pseudo-elements always need
&because they have no descendant interpretation.
Hands-on Example
.button {
background: navy;
color: white;
padding: 0.5rem 1rem;
/* descendant: a .icon inside .button */
.icon {
margin-right: 0.5rem;
}
/* attached to the parent itself */
&:hover {
background: darkblue;
}
/* variant class on the same element */
&.primary {
background: crimson;
}
/* media query nested inside */
@media (min-width: 640px) {
padding: 0.75rem 1.5rem;
}
}
.button { ... } .button { ... }
.icon { ... } => .button .icon { ... }
&:hover { ... } .button:hover { ... }
&.primary { ... } .button.primary { ... }
@media (...) { ... } @media (...) { .button { ... } }
The flattened form is exactly what the browser computes. If you ever wonder what a nested block actually selects, mentally substitute the parent for & and concatenate.
Common Pitfalls
The biggest trap is forgetting & before a pseudo-class. .card { :hover { ... } } does not style the card on hover. It styles any hovered descendant of the card. You almost always meant &:hover.
Another trap is type selectors as the first token of a nested rule. Some early implementations required wrapping div { ... } in & div because a nested rule starting with a letter could be ambiguous. Modern engines accept the bare form, but if you support older releases, prefix with & to stay safe.
Nesting can also hide specificity creep. A four-level nest reads like a tree, but produces a long descendant selector that is hard to override later. Two levels is comfortable; four is a code smell.
Finally, nesting is not free for tooling. Make sure your linter, formatter, and CSS-in-JS bridge understand the syntax before you commit a wholesale refactor.
Best Practices
Use nesting for variants and states that genuinely belong to a component: hover, focus, disabled, modifier classes, media queries. Avoid nesting unrelated layout rules just because they happen to live in the same file.
Cap your nesting depth. A rough rule: if you cannot read the flattened selector in your head, you have nested too far. Extract a class instead.
Prefer & explicitly for pseudo-classes and combinators. The extra character makes the intent unambiguous and matches how preprocessors trained your muscle memory.
Check @supports selector(&) if you must support older browsers, and ship a flat fallback file via a build step. For evergreen targets, no fallback is needed.
Wrap-up
Native CSS nesting brings a preprocessor superpower into the browser. The mental model is small: descendant by default, & for self and combinators. Keep depth modest, prefer explicit &, and your stylesheets will be both shorter and easier to refactor.
Related articles
- HTML & CSS CSS Cascade Layers and Specificity: A Calmer Mental Model
Stop fighting !important. Learn how the CSS cascade, specificity, and the new @layer rule combine to give you predictable, maintainable styles.
- HTML & CSS CSS Container Queries Explained with Real Examples
Container queries let components style themselves based on their parent's size, not the viewport. Learn the syntax, the units, and when to use them.
- HTML & CSS CSS Grid vs Flexbox: When to Use Each (with Examples)
Stop flipping a coin between Grid and Flexbox. Learn the mental model that picks the right layout tool every time, with practical CSS examples.
- HTML & CSS CSS :has() Pseudo-Class Tutorial
Learn how the CSS :has() pseudo-class enables true parent selectors, conditional styling, and sibling-aware rules. Includes a mental model, examples, pitfalls, and best practices for production use.