Modern Selectors: :has, :is, :where & :not

Four functional pseudo-classes have transformed what selectors can do. :has() finally lets CSS style an element based on what it contains — the long-awaited "parent selector". :is(), :where() and :not() make complex selectors short and keep specificity under control.

:has() — the parent selector

Try it yourself
Result

A:has(B) matches A elements that contain a B. Until recently, CSS could only select downwards; now a parent can react to its children.

:has() with form state

Try it yourself
Result

Styling a whole field wrapper or option card when its input is focused, invalid or checked used to need JavaScript. Now it's one selector.

:has() looking sideways

Try it yourself
Result

:has() accepts relative selectors: h3:has(+ p) is "an h3 immediately followed by a p", and :has(> li:nth-child(4)) is "has at least four direct li children" — a way to style by quantity.

Use with care: :has() on body or :root (like body:has(dialog[open])) is powerful for page-wide states, but very broad :has() selectors can make style recalculation slower on huge pages.

:is() — shorter selector lists

Try it yourself
Result

Without :is() that rule would need six comma-separated selectors. :is() takes the specificity of its most specific argument, and if one selector in the list is invalid, the rest still work (unlike a plain comma list, where one invalid selector breaks the whole rule).

:where() — zero specificity

Try it yourself
Result

:where() works exactly like :is() but always has a specificity of zero. It's perfect for base styles, resets and library CSS that users should be able to override with simple selectors.

:not() — exclusions

Try it yourself
Result

:not() accepts a list of selectors and excludes anything matching any of them. "Every item except the last" is a classic use, avoiding separate rules to undo a style.

Specificity summary

SelectorSpecificity
:is(#id, .class)The most specific argument — here (1,0,0)
:not(.a), :has(.a)Same as their most specific argument
:where(anything)Always (0,0,0)

Try it yourself

Try it yourself
Result