Selectors and the cascade

:has()

Widely availablesince Jun 2026

Replaces: Toggling a class on a parent from JavaScript. A parent can be styled by what it contains: a card with an image, a form with an invalid field.

See it working

The syntax

.option:has(input:checked) {
  border-color: var(--brand);
  background: var(--brand-subtle);
}
Edit the demo’s CSS

This is the stylesheet running the demo above. Change a value and watch it. Break it on purpose: delete the modern line and you are looking at the fallback.

For browsers without it

Without :has() the selected option keeps its default border and the radio button still shows which one is chosen, so nothing is lost but the emphasis. No script needed either way.

Before you ship it

It cannot be nested inside itself

:has() may not contain another :has(), and pseudo-elements are not allowed in its argument. Source

An unsupported :has() kills the whole rule

In a browser without it the entire selector block is thrown away, not just that one compound, unless the :has() sits inside a forgiving list such as :is() or :where(). Keep the base styles in a separate rule. Source

It reads a whole subtree

MDN is explicit that the engine has to walk the subtree under the matched element, so the narrower the argument, the cheaper the selector. Prefer a direct child (:has(> img)) to a descendant where you can. Source