The :has() selector
CSS gets a parent selector.
For two decades CSS could only descend. :has() finally lets a parent style itself based on what's inside it β or anywhere downstream.
Style a parent based on its children
.card:has(img) β a card that contains an image. form:has(:invalid) β a form with at least one invalid input. .row:has(input:checked) β the parent row of a checked checkbox.
It's not just direct children
:has() looks at any descendant by default. Combine with > for direct children only: .card:has(> img).
The "previous sibling" trick
CSS still has no :previous-sibling selector β but .a:has(+ .b) selects every .a that has a .b immediately after it.
Asking "are we there yet?"
The two examples above react to invalid state. The inverse is just as useful: :has(:valid):not(:has(:invalid)) lights up only when every required field is satisfied β a positive "ready to submit" affordance. And because :has() accepts structural pseudo-classes, you can count siblings (:has(> :nth-child(6))) to switch layout once a collection crosses a quantity threshold β the long-wished-for "quantity query".
Try it 4 examples
Highlight the row containing a checked box
HTML+CSS+JSintroOnly the "Slice ginger" row renders highlighted β amber border, amber text, struck through β because its box is checked and li:has(input:checked) reaches back up from the input to style the whole li. Toggle any other box and that row lights up too, proving the parent reacts to a descendant's state with no JavaScript.