skip to content

In native CSS nesting, what does the & nesting selector refer to, and what does a nested rule that omits & match?

level: juniorimportance: should knowfreq 55%

answer

  1. ampersand means the enclosing selector
  2. whitespace is a combinator
  3. relative selector, like inside :has()
  4. browser prepends an implied descendant &
  5. &:hover versus & :hover differ

basics

~20 s

& stands for the enclosing rule's selector list. A nested rule written without & gets an implied descendant combinator, so .card { p { ... } } styles paragraphs inside .card, exactly like .card p.

solid answer

~50 s

Native CSS nesting lets a style rule contain other style rules. Inside them, `&` is the *nesting selector*: it stands for the enclosing rule's whole selector list, so `.card { &:hover { ... } }` means `.card:hover`. If a nested selector does not contain `&`, it is treated as a **relative** selector and the browser absolutizes it by prepending `&` plus a descendant combinator — `.card { p { ... } }` behaves like `.card p`, and `.card { > p { ... } }` behaves like `.card > p`. Position matters: `&:hover` is a compound (the element itself), while `& :hover` has a space and matches hovered descendants. `&` may also appear later in the selector, as in `.theme-dark & { ... }`, which is how you write a contextual override. Nesting has been supported across current Chrome, Safari, and Firefox since late 2023.

code

css · 15 lines
css
.card {
  padding: 1rem;

  /* compound: the card itself */
  &:hover { border-color: currentColor; }

  /* descendant: anything hovered inside the card */
  & :hover { outline: 1px dashed; }

  /* no &: relative selector, implied descendant */
  p { margin-block: .5rem; }

  /* & later in the selector: contextual override */
  .theme-dark & { background: #111; }
}

go deeper

for a junior

Know that & means the enclosing rule's selector and that a nested rule without & is a descendant rule. Be able to flatten .card { p { } } to .card p out loud.

for a middle

Explain absolutization: a nested selector is relative, so the browser inserts & plus a descendant combinator, or & before a leading combinator. Show that you know .theme-dark & is legal and useful.

for a senior

Show judgment about depth — every nesting level adds a descendant combinator to the flattened selector, making the result harder to override. Be ready to say when you would flatten a rule out instead.

for a principal

Own the question of whether nesting belongs in a shared stylesheet at all: it changes how readable the emitted selectors are, interacts with tooling that greps for selector text, and needs a house rule on maximum depth.

## What native nesting is Nesting lets one style rule contain another, so related rules live together instead of repeating a long ancestor selector. Preprocessors like Sass offered this for years; CSS itself gained it in the CSS Nesting specification, supported in current Chrome, Safari, and Firefox since late 2023. Unlike the Sass version, native nesting is resolved by the browser at parse time — there is no build step flattening it first. ```css .card { padding: 1rem; & .title { font-weight: 700; } &:hover { border-color: currentColor; } } ``` ## The nesting selector & `&` is a real selector, not a text macro. It matches whatever the enclosing rule's selector list matches. In the example above, `& .title` means "a `.title` inside something matched by `.card`", and `&:hover` is a compound selector meaning "the `.card` element itself, while hovered". Because `&` is an ordinary simple selector, it can appear anywhere a simple selector can: ```css .btn { .theme-dark & { background: #000; } /* .theme-dark .btn */ & + & { margin-inline-start: .5rem; } /* .btn + .btn */ } ``` That second pattern — `&` written after some other selector — is the standard way to express "this component, but only inside that context", and it has no equivalent if you think of `&` as merely "the parent prefix". ## Relative selectors: the implied descendant A nested selector that never mentions `&` is a **relative selector**, the same kind of selector you pass to `:has()`. The browser *absolutizes* it: if it starts with a combinator, `&` is inserted before that combinator; otherwise a descendant combinator plus `&` is prepended. ```css .menu { li { list-style: none; } /* == & li == .menu li */ > li { padding: .5rem; } /* == & > li == .menu > li */ + .menu { margin-top: 2rem; } /* == & + .menu */ } ``` Early implementations required a nested selector to begin with a symbol such as `&`, `.`, `#` or `:`, which is why older articles insist you must write `& span`. The specification was relaxed and current browsers parse a bare type selector like `span` correctly, so `& ` is now optional in that position — though writing it explicitly still reads more clearly to people used to the old rule. ## The space is meaningful The single most common beginner mistake is treating `&:hover` and `& :hover` as the same thing. They are not: ```css .card { &:hover { /* .card:hover — the card itself */ } & :hover { /* .card :hover — any hovered descendant */ } } ``` The same applies to `&.is-active` (the element that has both classes) versus `& .is-active` (a descendant with that class). Whitespace is the descendant combinator, so removing it makes a compound selector. ## What a nested rule is equivalent to Conceptually, each nested rule desugars to a top-level rule whose selector is the absolutized nested selector with `&` replaced by the parent selector list. The one wrinkle is that the parent list behaves like `:is(...)` when the browser computes specificity — `#hero, .card { & p { } }` matches the same elements as `#hero p, .card p`, but scores like `:is(#hero, .card) p`. That difference matters enough that it is worth reasoning about separately from "what does this match". Nesting can go several levels deep, and each level re-scopes `&` to the rule directly around it: ```css .card { .title { & span { text-decoration: underline; } /* .card .title span */ } } ``` Deep nesting is legal but produces long descendant selectors that are hard to override — the flattened result is exactly as specific as if you had typed it out by hand. ## Practical notes Nesting works inside conditional at-rules and, in the other direction, at-rules such as `@media` can be nested inside a style rule. A nested rule only styles elements; it cannot introduce declarations for something the parent does not contain, and it does not create any kind of scoping boundary — `.card { p { } }` still matches paragraphs nested arbitrarily deep inside a card unless you use the child combinator.

  • What is the difference between `.card { p { } }` and `.card { > p { } }`?
    The first is absolutized to `.card p`, matching paragraphs at any depth inside the card. The second starts with a combinator, so `&` is inserted before it, giving `.card > p` — only paragraphs that are direct children. Starting a nested selector with a combinator is the shorthand for "child/sibling of the enclosing rule".
  • Can `&` appear more than once in a single nested selector?
    Yes. `& + &` is valid and expands to "an element matched by the parent selector immediately following another one", which is a common way to space repeated components. Each occurrence resolves to the same parent selector list, and each one contributes its specificity, so repeating `&` makes the rule more specific.
  • Does nesting a rule create any kind of style scoping?
    No. Nesting is purely a syntactic shorthand for writing longer selectors; the flattened selector still matches descendants at any depth and still competes in the normal cascade. Real scoping requires a separate mechanism such as `@scope` or shadow DOM — a nested rule cannot stop matching outside the component.

& is a pronoun that refers to the rule wrapped around it, not a find-and-replace of the parent's text.

saying these in an interview costs you the question

  • Thinking &:hover and & :hover mean the same thing
  • Believing nesting scopes styles to the component
  • Assuming a nested selector must always start with &
  • Reading & as a text prefix rather than a selector
  • Expecting nesting to lower specificity

context