: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+)