:has()

The Parent Selector

CSS never had a way to select an element based on what's inside it, so ‘style this card differently if it contains an invalid input’ meant reaching for JavaScript. :has() closes that gap: it's a relational pseudo-class that matches an element if any of its descendants match the selector passed to it.

The CSS

.list {
  border: 1px solid var(--border);

  /* at least one child checkbox is checked */
  &:has(input:checked) {
    border-color: var(--accent);
  }

  /* no child is left unchecked, i.e. everything is done */
  &:not(:has(input:not(:checked))) {
    border-color: var(--success);
  }
}

.item:has(input:checked) {
  opacity: 0.55;

  & span {
    text-decoration: line-through;
  }
}

How it works

Check an item and its row styles itself via .item:has(input:checked), no click handler toggling a class. The list container goes even further: :has(input:checked) nudges its border toward the accent colour as soon as one box is ticked, and :not(:has(input:not(:checked))), read as “there is no child left unchecked”, flips it to success green once every item is done.

That last rule is the interesting part: composing :not() and :has() lets you express “all of these match” even though CSS has no direct every() selector. It reads backwards at first, but it’s a pattern worth knowing.

All evergreen browsers (Chrome/Edge 105+, Safari 15.4+, Firefox 121+)