:has() — The Parent Selector
Style an element based on what it contains or what follows it — the selector we waited 20 years for.
For two decades the answer to “can CSS select a parent?” was a flat no. :has() finally changed that, and it's now supported in every modern browser. It matches an element if it contains something — or is related to something — that matches the inner selector.
Read it as “which has”
/* A card that contains an image looks different */
.card:has(img) { padding-top: 0; }
/* A label whose checkbox is checked */
label:has(input:checked) { font-weight: 700; }
/* A form group that contains an invalid field */
.field:has(:invalid) { border-color: crimson; }Because it combines with the other combinators, you get things that used to require JavaScript. Want a figure to react when its caption is present? figure:has(figcaption). Want to style a whole row when any checkbox in it is checked? Done, no listeners.
The quantity query trick
Combine :has() with :nth-child to react to how many children exist — a genuinely new capability.
Example
When to use it
- A developer uses :has(img) on a card to add extra padding only when the card contains an image, without a separate modifier class.
- A designer uses form:has(input:invalid) to highlight the entire form container red when any input fails validation.
- A developer uses .nav:has(.nav-link.active) to style the nav wrapper itself differently when any child link is the active page.
More examples
:has() for card with image variant
Uses :has(img) to remove top padding on cards that start with an image, letting the image flush to the card's rounded corners.
.card { padding: 24px; }
/* Extra top padding only when card has an img child */
.card:has(img) {
padding-top: 0;
}
.card img {
width: 100%;
border-radius: 8px 8px 0 0;
}Form highlight on any invalid input
Highlights the form border and disables the submit button any time a child input fails browser validation.
form:has(input:invalid) {
border-color: #c0392b;
}
form:has(input:invalid) .submit-btn {
opacity: 0.5;
pointer-events: none;
}:has() as next-sibling selector
Styles both the label and border of a field group when the contained input is focused — the first CSS parent selector.
/* Style a label when its following input is focused */
.field:has(input:focus) label {
color: #2965f1;
font-weight: 600;
}
.field:has(input:focus) input {
border-color: #2965f1;
outline: none;
}
Discussion