Native CSS Nesting
By Flavio Copes
Native CSS nesting works without Sass. Learn basic nesting, the ampersand selector, nested media queries, specificity gotchas, and when to drop preprocessors.
Native CSS nesting lets you group related selectors without Sass or Less.
The browser understands the nested rules directly. There is no compilation step, generated CSS file, or source map involved.
Nesting does not replace the cascade. It only changes how you organize selectors. The same specificity, inheritance, and source-order rules still apply.
Start with a card
Here is a flat set of component styles:
.card {
padding: 1rem;
border: 1px solid #ddd;
}
.card h2 {
margin: 0;
font-size: 1.25rem;
}
.card a {
color: royalblue;
}
Move the descendant selectors inside .card:
.card {
padding: 1rem;
border: 1px solid #ddd;
h2 {
margin: 0;
font-size: 1.25rem;
}
a {
color: royalblue;
}
}
The result is still .card h2 and .card a. The nesting makes the relationship visible in the stylesheet.
This works with any valid nested selector:
.article {
> header {
border-bottom: 1px solid #ddd;
}
h2 + p {
margin-top: 0;
}
[aria-current="page"] {
font-weight: bold;
}
}
The > and + combinators keep their normal meaning. Nesting does not make every selector a direct child.
Use & for the parent selector
The & symbol represents the selector of the outer rule.
Use it for pseudo-classes:
.button {
background: royalblue;
color: white;
&:hover {
background: navy;
}
&:focus-visible {
outline: 3px solid orange;
outline-offset: 3px;
}
}
The nested selectors become .button:hover and .button:focus-visible.
You can also combine a class with the parent:
.button {
&.danger {
background: firebrick;
}
&[aria-pressed="true"] {
box-shadow: inset 0 0 0 2px white;
}
}
These become .button.danger and .button[aria-pressed="true"].
Put the parent in a different position
The & can appear later in the selector.
For example, change a card when its parent has a dark theme:
.card {
background: white;
color: #222;
.dark-theme & {
background: #222;
color: white;
}
}
This produces .dark-theme .card.
This pattern is useful for theme containers, layout contexts, and feature classes. Use it carefully. If a component depends on many distant ancestors, the nesting can hide that dependency.
Native nesting is not Sass nesting
Do not copy every Sass pattern into native CSS.
Sass often uses & to build a new class name:
.card {
&__title {
font-weight: bold;
}
}
That creates .card__title in Sass. Native CSS does not use & as text substitution, so this concatenation is invalid.
Write the class explicitly:
.card {
.card__title {
font-weight: bold;
}
}
Or use a simpler class name that makes sense inside the component.
Nest media and container queries
At-rules can live beside the styles they modify.
.sidebar {
width: 100%;
@media (min-width: 768px) {
width: 18rem;
}
}
The @media block applies to .sidebar. You do not repeat the selector.
The same pattern works with container queries:
.product-card {
display: grid;
gap: 1rem;
@container (min-width: 32rem) {
grid-template-columns: 10rem 1fr;
}
}
Keeping responsive changes near the base rule makes small components easier to understand. Read CSS media queries if you need a refresher on responsive conditions.
Be careful with selector lists
Native nesting uses the specificity behavior of :is() for a selector list.
Consider this rule:
.card, #featured {
.title {
color: royalblue;
}
}
It behaves like this:
:is(.card, #featured) .title {
color: royalblue;
}
The most specific selector inside :is() determines the specificity. Because the list includes #featured, the nested title rule gets ID-level specificity even when .card matches.
Avoid mixing IDs and classes in the same outer selector list. Separate them when you want predictable overrides:
.card {
.title {
color: royalblue;
}
}
#featured {
.title {
color: royalblue;
}
}
Do not nest everything
Deep nesting produces long selectors and tight coupling.
.page {
.content {
.article {
.card {
a {
color: royalblue;
}
}
}
}
}
That selector is difficult to reuse and difficult to override. Give the element a meaningful class and keep the nesting shallow:
.article-card {
a {
color: royalblue;
}
}
My rule is one or two levels for most component CSS. Nest when it reveals a relationship, not because the syntax allows it.
When you can remove Sass
Native nesting covers one large reason projects adopted preprocessors. CSS variables cover another.
You might remove Sass when your stylesheets use it only for:
- nested selectors
- color and spacing variables
- splitting files that your build tool already bundles
Keep the preprocessor when the project depends on loops, custom functions, generated utility classes, or a large mixin system. Removing it is useful only when it removes real complexity.
CSS nesting works in current browsers. Check your own browser support requirements before deleting an existing build step.
My advice is to use native nesting in new component styles. Leave stable flat CSS alone until you have another reason to edit it.
Related posts about css: