CSS Nesting Cheatsheet: Interactive Visual Guide
October 28, 2026 | Cheatsheet | CSS
By Syed · Frontend Engineer & Creator of UITweak
CSS nesting lets you write child rules inside their parent rule, instead of repeating the parent selector on every line. It is the feature most developers knew from Sass, now built into the browser itself, with no build step and no preprocessor. A card component that used to need four separate rules can now be written as one block, and the result in the browser is exactly the same.
Nested CSS vs flat CSS, live
Switch between the two versions below. The preview card does not change, because nesting changes how CSS is written, not what it does. Hover the card to see the nested hover rule work.
Nested card
Hover this card. The border turns indigo, from one nested rule.
.nest-demo-card {
background: #ffffff;
border: 1px solid #e2e8f0;
border-radius: 16px;
padding: 24px;
h2 {
margin: 0 0 8px;
font-size: 20px;
color: #0f172a;
}
p {
margin: 0;
color: #475569;
}
&:hover {
border-color: #6366f1;
}
}Both versions style the card identically. Nesting only changes how the code is written, not what it does.
How nested rules work
A nested rule is a normal rule written inside another rule. The browser reads the inner selector relative to the outer one. A plain selector nested inside a parent means descendant: an h2 nested inside .card matches exactly what .card h2 matches in flat CSS. You do not need any special symbol for that case.
.card {
background: #ffffff;
border-radius: 16px;
padding: 24px;
h2 {
margin: 0 0 8px;
font-size: 20px;
}
p {
margin: 0;
color: #475569;
}
}The nesting selector, written as &, stands for the parent selector itself. You need it whenever the nested selector does not start with a plain descendant: pseudo-classes, pseudo-elements, and combinators all need it. Inside .card, &:hover means .card:hover, and &::before means .card::before. Without the &, a leading :hover would be read as a descendant selector, which is not what you want.
.card {
border: 1px solid #e2e8f0;
&:hover {
border-color: #6366f1;
}
&.featured {
border-width: 2px;
}
}The & can also sit in the middle of a selector. Inside .card, writing .dark-theme & matches .dark-theme .card, which is how you write theme overrides without leaving the component block. And you can chain it: & & is valid, though it raises specificity, so it is rarely the right call.
Nesting at-rules: media and supports
At-rules like @media and @supports can be nested inside a selector, which keeps responsive tweaks next to the component they belong to instead of in a separate media query block at the bottom of the file. The media query applies only to the rules nested inside it, and the parent selector still applies.
.card {
padding: 24px;
@media (max-width: 600px) {
padding: 16px;
h2 {
font-size: 18px;
}
}
}The reverse works too: you can nest selectors inside a top-level @media block exactly as before. Nesting does not replace media queries, it just gives you a second place to put them, and the readable place is usually inside the component.
Try the nesting builder
Pick a color. The nested CSS updates with it, and the preview shows the result. Notice how the heading and paragraph rules stay inside the card rule, so the whole component travels as one block.
Builder card
Pick a color below. The nested rules update with it.
.nest-builder-card {
background: #6366f1;
border-radius: 16px;
padding: 24px;
h2 {
margin: 0 0 8px;
font-size: 20px;
color: #ffffff;
}
p {
margin: 0;
opacity: 0.85;
color: #ffffff;
}
}CSS nesting vs Sass
If you used Sass nesting, native nesting will feel familiar, but the two are not identical. Native nesting runs directly in the browser, so there is no compilation step, no build tool, and no waiting for a watcher. The price is that Sass-only features do not exist here: there are no mixins, no @extend, no custom functions, and no partial imports. CSS custom properties cover variables, and real CSS does the rest.
One more difference matters when you convert old Sass code. Sass historically required the & before nested selectors in many positions, while modern native nesting allows a plain descendant selector without it. Code copied from Sass usually still works, because an explicit & is always allowed, but new code can skip it for simple descendants.
/* Sass-only: will not work as native CSS */
@mixin card-shadow {
box-shadow: 0 4px 12px rgb(0 0 0 / 0.1);
}
/* Native CSS: works in the browser as written */
.card {
border-radius: 16px;
&:hover {
box-shadow: 0 4px 12px rgb(0 0 0 / 0.1);
}
}CSS nesting browser support
Native CSS nesting ships in Chrome 112 and later, Safari 16.5 and later, and Firefox 117 and later. Every current browser version supports it, so for most projects it is safe to use directly. One version nuance: nested class selectors worked from those first versions, while a bare tag selector like a nested h2 needs the relaxed syntax, which arrived in Chrome 120, Safari 17.2, and Firefox 117.
The fallback behavior is graceful: a browser that does not understand nesting skips the nested rules but still applies the outer rule's own declarations. So the parent's own styles keep working in old browsers, and only the nested parts are lost. That makes nesting safe for enhancements, though styles a page critically needs should not live only inside a nested block when older browsers matter. To test support in CSS itself, use @supports (selector(&)), which is true only where nesting works.
@supports (selector(&)) {
.card {
h2 {
font-size: 20px;
}
}
}Common nesting mistakes
Nesting too deep. Three levels is usually plenty. Five levels of nesting produce a long, high-specificity selector that is hard to override later, which defeats the readability nesting was supposed to give you.
Forgetting the & on pseudo-classes. A bare :hover nested inside .card is read as a descendant selector, matching a hovered element inside the card, not the card itself. Pseudo-classes always need the &.
Treating nesting as scoping. Nesting shortens selectors, it does not scope them. A nested h2 rule still matches every h2 inside every .card on the page. True scoping is a separate feature, and nesting alone will not isolate a component from the rest of the page.
Specificity surprises. A nested selector has the specificity of the full combined selector, so .card containing h2 has the specificity of .card h2, not of a bare h2. Deep nesting quietly builds specificity that later overrides have to beat.
Nesting examples: recipes
Navigation with hover and active states. The link rules, their hover state, and the active state all live inside the nav block, so the component reads top to bottom in one place.
.nav {
display: flex;
gap: 16px;
a {
color: #334155;
text-decoration: none;
&:hover {
color: #4f46e5;
}
&.active {
font-weight: 700;
}
}
}Form field with focus and error states. The input, its focus ring, and its error variant nest inside the field wrapper, which keeps state styles next to the base styles they modify.
.field {
display: grid;
gap: 4px;
input {
border: 1px solid #cbd5e1;
border-radius: 8px;
padding: 8px 12px;
&:focus {
outline: 2px solid #6366f1;
}
}
&.error input {
border-color: #ef4444;
}
}