PureDevTools

CSS Nesting Reference & Generator

Generate native CSS nesting, explore the & selector, media queries inside rules, and common patterns with interactive examples

All processing happens in your browser. No data is sent to any server.

Nested Blocks

At-rule

)

Output

Generated blocks (3):

  • &:hover — hover state
  • &:focus-visible — keyboard focus
  • @media (min-width: 768px) — responsive breakpoint

Generated CSS

/* .card component */
.card {
  /* Base styles */

  &:hover {
    /* Hover state */
  }

  &:focus-visible {
    /* Keyboard focus */
  }

  @media (min-width: 768px) {
    /* Responsive styles at 768px */
  }
}

You’ve been writing .card h2, .card:hover, .card .badge, .card__title as separate rules — the same parent repeated 15 times in a component file. Sass solved this with nesting, but now you need a build step, source maps, and a preprocessor dependency. Native CSS nesting does the same thing with zero tooling: nest selectors inside a parent rule with &, and browsers compile it. Ship a .css file. No Sass, no PostCSS, no build step.

Why This Reference (Not the MDN Nesting Docs)

MDN covers nesting syntax across multiple pages without showing how it replaces common patterns (BEM, media query co-location, form validation states). This reference covers the & selector, pseudo-class/element nesting, @media inside rules, BEM with native nesting, implicit &, and all combinators — on one page with a generator. Everything runs in your browser; no data is sent anywhere.

What Is Native CSS Nesting?

Native CSS nesting lets you write child selectors, pseudo-classes, pseudo-elements, and at-rules directly inside a parent rule — without a preprocessor like Sass or Less. The & nesting selector represents the parent selector list, giving you the expressiveness of nested stylesheets in plain CSS.

/* Without nesting (traditional approach) */
.card { background: white; }
.card h2 { font-size: 1.5rem; }
.card:hover { box-shadow: 0 4px 12px rgb(0 0 0 / 0.1); }

/* With native CSS nesting */
.card {
  background: white;

  & h2 { font-size: 1.5rem; }
  &:hover { box-shadow: 0 4px 12px rgb(0 0 0 / 0.1); }
}

CSS Nesting is now baseline available in all major browsers (Chrome 112+, Firefox 117+, Safari 17.2+). No build step required for modern projects.

The & Nesting Selector

The & is the core of CSS nesting. It always represents the parent selector list — everything that comes before the current nested block.

.parent {
  /* & = ".parent" */
  &:hover { color: blue; }       /* → .parent:hover */
  &.active { font-weight: bold; } /* → .parent.active */
  & + .sibling { margin: 0; }    /* → .parent + .sibling */
}

Concatenation vs. Descendant

The key distinction: space before & means descendant; no space means concatenation.

.btn {
  &:hover { }       /* → .btn:hover (concatenated, no space) */
  &.loading { }     /* → .btn.loading (concatenated, no space) */
  & span { }        /* → .btn span (descendant, has space) */
  & > svg { }       /* → .btn > svg (child combinator, has space) */
}

Selector Reversal with &

Placing & at a non-leading position reverses the selector context:

.card {
  /* .dark-mode .card */
  .dark-mode & {
    background: #1f2937;
    color: #f9fafb;
  }

  /* :is(article) .card */
  :is(article) & {
    border-left: 4px solid #3b82f6;
  }
}

Basic Nesting

Any CSS selector can be nested. The & prefix (with space) creates a descendant relationship:

.form {
  display: flex;
  flex-direction: column;
  gap: 1rem;

  & label {
    font-weight: 500;
    margin-bottom: 0.25rem;
  }

  & input,
  & textarea {
    border: 1px solid #d1d5db;
    border-radius: 0.375rem;
    padding: 0.5rem 0.75rem;
  }

  & button[type="submit"] {
    align-self: flex-end;
  }
}

Pseudo-class Nesting

All pseudo-classes work with &. This is the most common pattern for interactive state management:

.link {
  color: #3b82f6;
  text-decoration: underline;

  &:hover { color: #1d4ed8; text-decoration: none; }
  &:visited { color: #7c3aed; }
  &:focus-visible { outline: 2px solid currentColor; outline-offset: 2px; }
  &:active { opacity: 0.8; }
}

Compound Pseudo-classes

.input {
  &:not(:placeholder-shown) { border-color: #10b981; }
  &:is(:focus, :focus-within) { box-shadow: 0 0 0 3px rgb(59 130 246 / 0.1); }
  &:has(+ .error-message) { border-color: #ef4444; }
  &:nth-child(even) { background: #f9fafb; }
}

Pseudo-element Nesting

All pseudo-elements work with &:::

.blockquote {
  &::before {
    content: '\201C';
    font-size: 3rem;
    color: #9ca3af;
    line-height: 0;
    vertical-align: -0.4em;
  }

  &::after {
    content: '\201D';
    font-size: 3rem;
    color: #9ca3af;
    line-height: 0;
    vertical-align: -0.4em;
  }
}

.input::placeholder { color: #9ca3af; font-style: italic; }

Media Queries Inside Rules

@media, @supports, and @container can be nested directly inside a rule — no & needed. This co-locates responsive styles with their component:

.grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1rem;

  @media (min-width: 640px) {
    grid-template-columns: repeat(2, 1fr);
  }

  @media (min-width: 1024px) {
    grid-template-columns: repeat(3, 1fr);
    gap: 1.5rem;
  }

  @supports (display: subgrid) {
    grid-template-rows: subgrid;
  }
}

Nesting Inside Nested @media

You can nest selectors inside a @media block that is itself inside a rule:

.card {
  @media (min-width: 768px) {
    & h2 {
      font-size: 2rem;
    }
  }
}
/* Compiles to: @media (min-width: 768px) { .card h2 { font-size: 2rem; } } */

Combinators

All CSS combinators work inside nested rules:

.list {
  & > li { list-style: none; }         /* .list > li  — direct child */
  & + .list { margin-top: 1rem; }       /* .list + .list — adjacent sibling */
  & ~ .footer { margin-top: auto; }     /* .list ~ .footer — general sibling */
}

BEM with Native Nesting

Native nesting is especially powerful for BEM conventions. The & concatenation eliminates repetition:

.card {
  /* Block */
  border-radius: 0.5rem;
  padding: 1.5rem;

  /* Elements */
  &__title { font-size: 1.25rem; font-weight: 600; }
  &__body { color: #4b5563; }
  &__footer { border-top: 1px solid #e5e7eb; padding-top: 1rem; }

  /* Modifiers */
  &--primary { border: 2px solid #3b82f6; }
  &--danger { border: 2px solid #ef4444; }

  /* Modifier + element combination */
  &--primary &__title { color: #1d4ed8; }
}

Implicit & (Chrome 120+, Firefox 117+, Safari 17.2+)

In modern browser versions, the & can be omitted when a nested selector starts with a valid CSS selector that is clearly not a combinatorless start (i.e., starts with ., #, [, *, a type selector, or a pseudo-class):

.card {
  /* Implicit & (no & needed in newest browsers) */
  h2 { font-size: 1.5rem; }
  .badge { display: inline-block; }
  :hover { box-shadow: 0 4px 12px rgb(0 0 0 / 0.1); }
}

Recommendation: Always use explicit & for maximum compatibility and readability.

Common Patterns

Dark Mode Toggle

.component {
  background: white;
  color: #1f2937;

  @media (prefers-color-scheme: dark) {
    background: #1f2937;
    color: #f9fafb;
  }

  /* Or with a data attribute toggle */
  [data-theme="dark"] & {
    background: #1f2937;
    color: #f9fafb;
  }
}

Container Query

.card {
  padding: 1rem;
  container-type: inline-size;

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

Form Validation States

.field {
  &:has(input:invalid:not(:placeholder-shown)) {
    & label { color: #ef4444; }
    & input { border-color: #ef4444; }
    & .hint { display: block; color: #ef4444; }
  }

  &:has(input:valid:not(:placeholder-shown)) {
    & input { border-color: #10b981; }
  }
}

Browser Support

Native CSS nesting is supported across all modern browsers:

BrowserBasic Nesting@media in RuleImplicit &
Chrome112+112+120+
Firefox117+117+117+
Safari17.2+17.2+17.2+
Edge112+112+120+
Opera98+98+106+

Global coverage: ~88% as of early 2026. For older browser support, use a PostCSS nesting plugin as a build-step fallback — the syntax is identical.

Checking for Support

/* @supports does not detect nesting directly,
   but you can use a has() check as a rough proxy */
@supports selector(&) {
  /* Nesting is supported */
}

Frequently Asked Questions

Is native CSS nesting the same as Sass/SCSS nesting?

Mostly yes, with minor differences. Native CSS requires & when the nested selector doesn’t start with a combinator (e.g., you must write & h2, not just h2), while Sass allows bare nested selectors. The @media nesting behavior is identical. Modern browsers also support implicit & (no & needed), closing most remaining gaps.

Does CSS nesting affect specificity?

No. Native CSS nesting compiles to the equivalent non-nested selectors, so specificity is determined by the compiled selector, not by how deeply it’s nested. .card &:hover has the same specificity as .card:hover.

Can I nest keyframes or font-face inside a rule?

No. @keyframes and @font-face must remain at the top level. Only @media, @supports, @container, and @layer can be nested inside rules (along with regular selector rules using &).

Does nesting work inside @layer?

Yes. Nesting works inside @layer blocks and within @media at-rules. You can combine all three:

@layer components {
  .card {
    & h2 { font-size: 1.5rem; }

    @media (min-width: 768px) {
      padding: 1.5rem;
    }
  }
}

What is the :is() + nesting pattern?

Combining :is() with & lets you apply styles when the parent element is in multiple different contexts:

.btn {
  /* Apply styles when .btn appears inside a form or a toolbar */
  :is(form, .toolbar) & {
    margin-inline-end: 0.5rem;
  }
}

Should I use native nesting or a preprocessor?

For new projects targeting modern browsers (Chrome 112+, Firefox 117+, Safari 17.2+), native CSS nesting is production-ready and requires no build step. For projects that must support older browsers, use PostCSS with postcss-nesting — your source CSS looks identical and the plugin un-nests for older environments.

Related Tools

More CSS Tools