& / nested selectors

CSS Nesting

Sass and Less popularised writing selectors inside selectors. CSS does it natively now!

The CSS

Copied
.card {
  border: 1px solid var(--border);
  transition: transform 0.15s, border-color 0.15s;

  &:hover {
    transform: translateY(-2px);
    border-color: var(--text-secondary);
  }

  &.featured {
    border-color: var(--accent);
  }

  &[data-variant="neutral"] {
    --accent: var(--text-secondary);
  }

  &[data-variant="success"] {
    --accent: var(--success);
  }

  & button {
    background: var(--accent);

    &:hover:not(:disabled) { opacity: 0.85; }
    &:disabled { opacity: 0.4; cursor: not-allowed; }
  }
}

How it works

Any rule can contain another rule, and it’s the browser doing the work: no compiler, no source maps. A nested selector that starts with a type selector needs an explicit &, since CSS can’t otherwise tell a nested rule from a declaration, but class, pseudo-class and attribute selectors nest directly.

& stands in for the parent selector, same as it does in Sass, so &:hover, &.featured and &[data-variant="success"] all read as “this element, when…”. At-rules nest too, so a component’s media query lives next to its styles instead of in a block at the bottom of the file.

Widely supported

Chrome / Edge112+Safari16.5+Firefox117+
:has() · tic-tac-toe

turns, wins and reset, all CSS

@property · counter()
hover me
cos() · sin()

trigonometry, in CSS

:has() quantity query

switch guests on