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