:has() relational selector
Baseline 2023 (Chrome 105, Safari 15.4, Firefox 121); widely available.
Chrome 105
Edge 105
Safari 15.4
Firefox 121
Selects an element based on what it contains or what follows it, which CSS never could before ("a card that has an image", "a form field whose input is invalid", "the label before a checked checkbox"). It replaces a large class of JS state classes.
CSS
.fx-002 {
font-family: system-ui;
display: grid;
gap: 12px;
max-width: 360px
}
.fx-002 fieldset {
opacity: .4;
pointer-events: none;
transition: opacity .2s
}
.fx-002:has(input[type=checkbox]:checked) fieldset {
opacity: 1;
pointer-events: auto
}
.fx-002 fieldset:has(input:user-invalid) {
border-color: #c0392b
}
.fx-002 :is(a,button,input,select,textarea):focus-visible { outline: 2px solid currentColor; outline-offset: 2px; }
HTML
<form class="fx-002">
<label><input type="checkbox"> Enable shipping address</label>
<fieldset><legend>Shipping</legend><input placeholder="Street" required></fieldset>
</form>Fallback: Wrap in `@supports selector(:has(a))` if the non-:has state must differ; otherwise the fieldset simply stays enabled.
Prompt
Use `:has()` for parent- and sibling-aware styling instead of toggling classes with JavaScript: e.g. `form:has(input[type=checkbox]:checked) fieldset` to reveal a section, `.card:has(img)` for media variants, `label:has(+ input:focus-visible)` for focused labels. Keep the `:has()` argument shallow (one or two compound selectors) for performance and guard non-trivial layouts with `@supports selector(:has(a))`.