CSS Nesting: Write Less, Mean More

Summary

For years, writing CSS meant repeating yourself constantly. You’d write a parent selector, then write it again for every child, then again for every modifier. Sass and Less existed largely to solve this one problem. Now, natively, CSS nesting is here – and it changes how you author styles at a fundamental level. What is […]

By Published Source: https://www.vikipandit.com/webdev/css-nesting/

For years, writing CSS meant repeating yourself constantly. You’d write a parent selector, then write it again for every child, then again for every modifier. Sass and Less existed largely to solve this one problem. Now, natively, CSS nesting is here – and it changes how you author styles at a fundamental level.

“Any fool can write code that a computer can understand. Good programmers write code that humans can understand.”
Martin Fowler

What is CSS Nesting?

CSS nesting lets you write child selectors inside their parent rule, mirroring the visual structure of your HTML. Instead of repeating context over and over, you declare it once and tuck everything that belongs to it inside.

Before nesting, you wrote this:

CSS

Without Nesting

.card { background: white; }
.card .card-title { font-size: 1.5rem; }
.card .card-body { padding: 1rem; }
.card:hover { box-shadow: 0 4px 12px hsl(0 0% 0% / 0.1); }
.card.is-featured { border-left: 4px solid royalblue; }

Five lines, and .card shows up in every single one, like a name tag you keep having to re-pin. With nesting, the same rules become:

CSS

CSS Nesting

.card {
  background: white;

  .card-title { font-size: 1.5rem; }
  .card-body { padding: 1rem; }

  &:hover { box-shadow: 0 4px 12px hsl(0 0% 0% / 0.1); }
  &.is-featured { border-left: 4px solid royalblue; }
}

The structure of your CSS now reflects the structure of your component.

The & Selector

The ampersand is the nesting selector. It refers to the parent selector in the current context. It is your primary tool for modifiers, pseudo-classes, pseudo-elements, and any rule that needs to stay tightly connected to its parent without introducing a descendant relationship.

CSS

The & Selector

.button {
  background: royalblue;
  color: white;

  &:hover { background: navy; }
  &:focus-visible { outline: 2px solid royalblue; outline-offset: 2px; }
  &:disabled { opacity: 0.5; cursor: not-allowed; }
  &::before { content: ""; display: block; }
  &.button--large { padding: 1rem 2rem; font-size: 1.125rem; }
  &.button--ghost { background: transparent; color: royalblue; border: 1px solid royalblue; }
}

Note the distinction: &:hover compiles to .button:hover, while .button:hover written as a nested rule without & would compile to .button .button:hover – targeting a .button that is a descendant of another .button. That is rarely what you want.

When you need a descendant, just drop the &:

CSS

Code Example

.nav {
  display: flex;

  a { color: inherit; text-decoration: none; }
  li { list-style: none; }
}

Media Queries and Container Queries, Nested

This is where nesting stops being a tidiness feature and starts being a structural one. You can nest at-rules directly inside the component they belong to, so a component’s responsive behavior no longer lives in some distant @media block at the bottom of the file, far from the thing it actually styles.

CSS

Code Example

.hero {
  font-size: 2rem;
  padding: 2rem;

  @media (min-width: 768px) {
    font-size: 3.5rem;
    padding: 4rem;
  }

  @media (min-width: 1200px) {
    font-size: 5rem;
    padding: 6rem;
  }
}

The same applies to container queries:

CSS

Code Example

.card {
  display: block;

  @container (min-width: 400px) {
    display: grid;
    grid-template-columns: auto 1fr;
  }
}

And to @supports:

CSS

Code Example

.layout {
  display: flex;

  @supports (display: grid) {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  }
}

Your component’s full behavior, base, responsive, enhanced, lives in one place. Delete the component, and you delete every rule that ever served it. No orphaned media query haunting line 4,000 of your stylesheet six months later.

Nesting Depth and the Specificity Trap

Now for the part where I talk you out of overdoing the thing I just sold you on.

Nesting is a tool, not a license. The most common mistake is treating it like a mirror of your HTML and burrowing as deep as the DOM goes, just because you can.

CSS

Code Example

/* This is a trap */
.page {
  .main {
    .section {
      .card {
        .card-header {
          .card-title {
            font-size: 1.25rem;
          }
        }
      }
    }
  }
}

This compiles to .page .main .section .card .card-header .card-title, a selector with specificity so high it becomes nearly impossible to override without !important. It also couples your CSS tightly to your HTML structure – change one level of nesting in your markup and the styles break.

A more practical guideline: nest for relationships that matter semantically, not for relationships that just happen to exist in the DOM. One or two levels deep is almost always enough.

CSS

Code Example

/* Better: component-level nesting only */
.card {
  background: white;

  .card-header { border-bottom: 1px solid hsl(0 0% 90%); }
  .card-title { font-size: 1.25rem; }

  &:hover { transform: translateY(-2px); }
  &.is-expanded .card-header { border-bottom: none; }
}

Practical Patterns

State and modifier classes are where nesting earns its keep daily. Every state an input can be in, focused, errored, errored-and-focused, disabled, sits together where you can actually reason about them.

CSS

Code Example

.input {
  border: 1px solid hsl(0 0% 80%);
  border-radius: 4px;
  padding: 0.5rem 0.75rem;

  &:focus { border-color: royalblue; outline: none; box-shadow: 0 0 0 3px hsl(225 73% 57% / 0.25); }
  &.has-error { border-color: crimson; }
  &.has-error:focus { box-shadow: 0 0 0 3px hsl(348 83% 47% / 0.25); }
  &:disabled { background: hsl(0 0% 96%); cursor: not-allowed; }
}

Variant-driven components read like a small menu of options rather than a scavenger hunt:

CSS

Code Example

.badge {
  display: inline-flex;
  padding: 0.2em 0.6em;
  border-radius: 99px;
  font-size: 0.75rem;

  &.badge--success { background: hsl(142 76% 90%); color: hsl(142 76% 25%); }
  &.badge--warning { background: hsl(38 92% 90%); color: hsl(38 92% 25%); }
  &.badge--danger { background: hsl(348 83% 90%); color: hsl(348 83% 25%); }
}

Parent-context targeting

Sometimes a component needs to behave differently depending on where it lives, like a teenager who is a completely different person at home versus at a friend’s house. The & placed at the end handles this:

CSS

Code Example

.icon {
  width: 1em;
  height: 1em;

  .button & { margin-inline-end: 0.4em; }
  .nav & { width: 1.25em; height: 1.25em; }
  .sidebar & { color: hsl(0 0% 60%); }
}

This compiles to .button .icon, .nav .icon, and .sidebar .icon respectively. The component knows about its contexts without those contexts knowing about the component internals.

Browser Support

As of mid-2026, CSS nesting is supported in every major browser, Chrome, Firefox, Safari, and Edge. Baseline 2024 marks it as widely available, which in browser-feature years is practically ancient and reassuringly boring. For the vast majority of production projects you can use it today, no preprocessor, no build step, no polyfill.

If you are still targeting genuinely old environments, the postcss-nesting plugin will compile your nested CSS down to flat rules at build time, so you get the nice authoring experience and the old browser gets what it can handle.

“Programs must be written for people to read, and only incidentally for machines to execute.”
Harold Abelson

The Real Shift

Nesting is not just a syntactic convenience. It is a change in how CSS communicates intent.

Flat CSS is a list of rules with no visible relationship between them. You have to hold the component model in your head while reading. Nested CSS is a document – you can read a .card block and understand the entire component: its base styles, its states, its variants, its responsive behavior, its pseudo-elements. Everything that belongs to .card lives inside .card.

That is what makes nesting genuinely significant. The structure of your code becomes evidence of your thinking. And code that reveals its own intent is code that holds up over time, across teams, and through the inevitable refactors that every living project goes through.

Write components, not rules. CSS nesting makes that possible in plain CSS.

Last updated: June 28, 2026