:has() relational selector

Baseline 2023 (Chrome 105, Safari 15.4, Firefox 121); widely available.

Chrome 105Edge 105Safari 15.4Firefox 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.

Shipping
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.

Source: developer.mozilla.org/en-US/docs/Web/CSS/:has

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))`.