CSS Nesting
Native CSS nesting — & parent selector, nested rules and media queries, specificity behavior, and when not to nest five levels deep.
- css
- nesting
Browsers now understand native CSS nesting — rules inside rules without a preprocessor. The & parent selector joins nested selectors to the outer one, matching the mental model many teams already have from Sass, with a few important differences.
Docs: MDN CSS nesting, CSS Nesting Module.
Basic form
.card {
padding: 1rem;
border: 1px solid var(--border);
& .card__title {
margin: 0;
font-size: 1.125rem;
}
& .card__title {
/* equivalent flattened: .card .card__title */
}
&:hover {
border-color: var(--brand);
}
&.is-selected {
box-shadow: 0 0 0 2px var(--brand);
}
}
/* Also valid in modern nesting: start nested type selectors carefully */
.card {
h2 {
/* parses as & h2 → .card h2 in supporting engines with nesting */
font-size: 1.125rem;
}
}
Engine rules evolved: if a nested selector wouldn’t parse safely, prefix with &. Prefer explicit & for team clarity.
Nested media and supports
.toolbar {
display: flex;
gap: 0.5rem;
@media (min-width: 48rem) {
gap: 1rem;
& .toolbar__label {
display: inline;
}
}
@supports (backdrop-filter: blur(4px)) {
& {
backdrop-filter: blur(8px);
}
}
}
Keeps responsive variants next to the component — the main readability win.
Specificity
Nested selectors compute like their flattened equivalents. Nesting is not a specificity shield.
.card {
& .title {
/* specificity of .card .title */
color: blue;
}
}
&:is(.a, .b) follows :is() specificity rules (most specific argument wins).
Composition patterns
.btn {
--_bg: var(--brand);
background: var(--_bg);
color: white;
&--primary {
/* .btn--primary only if your naming expects concatenation */
}
}
Warning: &--primary becomes .btn--primary (string concatenation with &). & .btn--primary is a descendant. Know which you want.
.btn {
&.btn--primary {
--_bg: var(--brand);
}
&.btn--danger {
--_bg: var(--danger);
}
}
How deep is too deep?
/* Smell */
.page {
& .layout {
& .sidebar {
& .nav {
& .link {
& span {
color: red;
}
}
}
}
}
}
Flatten to BEM-style single classes or shallow nests (component + state + one media). Deep nests recreate the specificity and reuse problems preprocessors caused.
Interview out-loud
“Native nesting lets me write nested rules and media queries inside a component block using & for the parent. Flattened specificity matches the final selector list. I nest shallowly for hover, modifiers, and breakpoints, and I avoid deep descendant pyramids. It’s not Sass: some interpolation and parent tricks don’t exist.”
Footguns
- Expecting full Sass feature set (
@mixin,@extend, maps). - Accidental concatenation (
&--x) vs descendant. - Nesting until selectors are un-overridable.
- Shipping nested CSS to targets without support without a transpiler — check baseline.
- Mixing modules, layers, and deep nests without a stylelint max-depth rule.
Nesting + layers + modules
Native nesting works inside @layer blocks and CSS modules. With CSS modules, nested selectors still compile to unique class names for the local class; global tags inside nests can widen unexpectedly.
/* modules example */
.card {
padding: 1rem;
&:global(.is-dragging) {
opacity: 0.8;
}
}
Adopt a team max nesting depth (2–3) in stylelint. Nesting is a readability tool, not a replacement for good class names. If the flattened selector would look embarrassing in DevTools, rewrite it flat.
Migration from Sass
Replace @include with CSS mixins via custom properties or shared utility classes. Replace Sass maps with design tokens on :root. Keep Nesting; drop Sass-only selector juggling. Compile step can stay for older browsers until your baseline includes native nesting.
Related
Further reading
Related guides
- Accent Color and Form ControlsUse accent-color to theme native checkboxes, radios, ranges, and progress — what it paints, what it skips, and when to custom-style instead.
- Auto-fit vs Auto-fillauto-fit collapses empty tracks; auto-fill keeps them. Use both with minmax for responsive card grids without media-query soup.
- BEM Naming MethodologyBlock, Element, Modifier naming for CSS — how it limits specificity wars, what to name, and when utility or CSS modules beat strict BEM.
- blend-mode Basicsmix-blend-mode vs background-blend-mode — how pixels combine, isolation, and the stacking/readability traps in product UI.
- box-sizing border-boxWhy border-box makes width include padding and border, the global reset pattern, and the layout bugs content-box still causes.