CSS nesting lets you write selectors inside other selectors, keeping related styles together and reducing repetition. For years this required Sass, Less, or PostCSS. Now native CSS nesting works in all modern browsers without any build step.
Browser support
Native CSS nesting is fully supported in:
| Browser | Version |
|---|---|
| Chrome | 112+ (March 2023) |
| Edge | 112+ (March 2023) |
| Safari | 16.5+ (May 2023) |
| Firefox | 117+ (August 2023) |
For older browsers, use a PostCSS plugin like postcss-nesting to compile nested CSS to flat selectors during build.
Basic nesting syntax
Wrap child selectors inside a parent:
.card {
padding: 1rem;
background: white;
border-radius: 8px;
.title {
font-size: 1.25rem;
font-weight: 600;
}
.content {
color: #666;
line-height: 1.6;
}
}
This compiles (conceptually) to:
.card { padding: 1rem; background: white; border-radius: 8px; }
.card .title { font-size: 1.25rem; font-weight: 600; }
.card .content { color: #666; line-height: 1.6; }
The nested selectors inherit the parent context automatically.
The & selector
The & symbol explicitly references the parent selector. It's required for:
Pseudo-classes and pseudo-elements
.button {
background: blue;
color: white;
&:hover {
background: darkblue;
}
&:focus-visible {
outline: 2px solid orange;
}
&::after {
content: '→';
margin-left: 0.5rem;
}
}
Compound selectors
.nav-link {
color: gray;
&.active {
color: blue;
font-weight: bold;
}
&[aria-current="page"] {
border-bottom: 2px solid blue;
}
}
Combinators
.list {
& + .list {
margin-top: 1rem;
}
& > li {
padding: 0.5rem;
}
& ~ .footer {
border-top: 1px solid #eee;
}
}
When & is optional
For descendant selectors (space), the & is optional in the latest spec:
/* These are equivalent */
.card {
.title { font-size: 1.25rem; }
}
.card {
& .title { font-size: 1.25rem; }
}
Use explicit & for clarity or when the meaning might be ambiguous.
Nesting at-rules
You can nest media queries and other at-rules inside selectors:
.sidebar {
width: 300px;
@media (max-width: 768px) {
width: 100%;
position: fixed;
}
}
This outputs:
.sidebar { width: 300px; }
@media (max-width: 768px) {
.sidebar { width: 100%; position: fixed; }
}
You can also nest container queries:
.card {
padding: 1rem;
@container (min-width: 400px) {
padding: 2rem;
display: grid;
}
}
Deep nesting
You can nest multiple levels:
.nav {
display: flex;
.menu {
display: flex;
gap: 1rem;
.item {
padding: 0.5rem 1rem;
&:hover {
background: #f0f0f0;
}
.icon {
margin-right: 0.5rem;
}
}
}
}
Avoid nesting too deep. Three to four levels is usually the maximum for maintainability. Deep nesting creates high-specificity selectors that are hard to override.
Differences from Sass
Native CSS nesting is similar to Sass but has some differences:
| Feature | Sass | Native CSS |
|---|---|---|
| Variables | $color: blue; |
--color: blue; (custom properties) |
| Mixins | @mixin, @include |
Not supported |
| Functions | darken(), lighten() |
color-mix(), oklch() |
| Extend | @extend |
Not supported |
| Parent reference | & anywhere |
& with some restrictions |
| Imports | @import, @use |
Native @import or build tools |
For features like mixins, you'll need to use other CSS techniques or keep using Sass.
Migrating from Sass
Most Sass nesting patterns work in native CSS:
/* Sass */
.button {
&:hover { background: darkblue; }
&.primary { background: blue; }
.icon { margin-right: 0.5rem; }
}
/* Native CSS - identical */
.button {
&:hover { background: darkblue; }
&.primary { background: blue; }
.icon { margin-right: 0.5rem; }
}
Watch for edge cases:
/* Sass - works */
.parent {
.child & { color: red; } /* .child .parent */
}
/* Native CSS - not supported */
/* Use flat selectors instead */
.child .parent { color: red; }
Common patterns
Component variants
.button {
padding: 0.5rem 1rem;
border: none;
border-radius: 4px;
cursor: pointer;
&.primary {
background: blue;
color: white;
}
&.secondary {
background: gray;
color: white;
}
&.ghost {
background: transparent;
border: 1px solid currentColor;
}
&:disabled {
opacity: 0.5;
cursor: not-allowed;
}
}
Responsive components
.hero {
padding: 2rem;
text-align: center;
.title {
font-size: 2rem;
}
.subtitle {
font-size: 1rem;
color: #666;
}
@media (min-width: 768px) {
padding: 4rem;
.title {
font-size: 3rem;
}
.subtitle {
font-size: 1.25rem;
}
}
}
State-based styling
.input {
border: 1px solid #ccc;
padding: 0.5rem;
&:focus {
border-color: blue;
outline: none;
}
&:invalid {
border-color: red;
}
&:disabled {
background: #f5f5f5;
cursor: not-allowed;
}
}
Combining with other modern CSS
Native nesting works well with other modern CSS features:
.card {
container-type: inline-size;
.content {
display: block;
@container (min-width: 400px) {
display: grid;
grid-template-columns: 1fr 2fr;
}
}
&:has(.image) {
.content {
padding-left: 1rem;
}
}
}
Using with Tailwind v4
Tailwind CSS v4 uses native CSS and Lightning CSS. You can use nesting in your CSS files:
@layer components {
.card {
@apply bg-white rounded-lg shadow;
.title {
@apply text-xl font-bold;
}
&:hover {
@apply shadow-lg;
}
}
}
Performance considerations
Native CSS nesting has no performance cost. Browsers parse nested CSS directly. Unlike Sass, there's no compilation step at build time. The browser's CSS parser handles nesting as efficiently as flat selectors.
Deeply nested selectors do have higher specificity, which can affect cascade performance in very large stylesheets. Keep nesting shallow for maintainability and performance.
Fallbacks for older browsers
If you need to support older browsers, use PostCSS with postcss-nesting:
npm install postcss postcss-nesting
Configure in postcss.config.js:
module.exports = {
plugins: [
require('postcss-nesting'),
],
};
The plugin compiles nested CSS to flat selectors during build, so older browsers receive compatible CSS.
Summary
Native CSS nesting eliminates the need for preprocessors in many projects. Use & for pseudo-classes, compound selectors, and combinators. Nest media queries and container queries inside selectors for component-scoped responsive design. Keep nesting shallow (3-4 levels max) for maintainability. For older browsers, PostCSS can compile nested CSS to flat selectors.
