CSS Nesting: How the Ampersand Selector Works

Native CSS nesting introduces the ampersand (&) symbol as a dedicated nesting selector to explicitly reference the parent selector within a rule block. This article explains how the browser resolves the & selector, how to apply pseudo-classes, compound selectors, and contextual styling, as well as the critical differences between native CSS nesting and preprocessor syntax like Sass.

Representing the Parent Selector

In native CSS, the & selector explicitly resolves to the parent selector rule list wrapped in an :is() pseudo-class. When placed at the start of an inner rule, it directly targets child elements or states of the parent.

.card {
  padding: 1rem;

  & p {
    color: #333;
  }
}

This compiles conceptually to :is(.card) p. While modern CSS allows implicit nesting for element tags without the & symbol, explicitly using & clarifies intent and avoids parsing ambiguities.

Adding Pseudo-Classes, Pseudo-Elements, and Modifiers

The primary use case for & is targeting states and modifiers without whitespace between the selector and the ampersand.

  • Pseudo-classes and pseudo-elements:

    .button {
      background: blue;
    
      &:hover {
        background: darkblue;
      }
    
      &::after {
        content: "";
      }
    }
  • Compound class selectors:

    .button {
      &.is-active {
        border: 2px solid green;
      }
    }

Placing the & directly against :hover or .is-active ensures the output targets the parent itself under those conditions (.button:hover and .button.is-active), rather than looking for a descendant element.

Contextual and Ancestor Reordering

The & selector does not have to appear first in a nested declaration. Placing & later in the selector rule changes the context based on an ancestor:

.card {
  color: black;

  .dark-theme & {
    color: white;
  }
}

This compiles to :is(.dark-theme) :is(.card), applying the styles to .card only when it is inside .dark-theme.

No Identifier Concatenation (The BEM Limitation)

Unlike preprocessors such as Sass or Less, native CSS cannot use the ampersand for string concatenation. The following BEM pattern is invalid in native CSS:

/* This does NOT work in native CSS */
.block {
  &__element {
    color: red;
  }
}

Because CSS parses selectors as dynamic object tokens rather than plain strings, & represents the entire parsed parent selector, not a string fragment. To target elements using BEM, full selector names must be declared.

Specificity and the :is() Behavior

Because the & selector resolves using :is(), it adopts the highest specificity of any selector within the parent's selector list.

.nav, #main-nav {
  & a {
    color: blue;
  }
}

In this scenario, & a expands to :is(.nav, #main-nav) a. Consequently, the link inside .nav will inherit the high specificity of the #main-nav ID selector, even if the element currently matches only the .nav class.