:has() The Parent Selector
Style an element based on what's inside it or what comes after it. The holy grail of CSS selectors.
Selecting a parent based on its child
For over 20 years, CSS could only style an element based on its parents (e.g., .card img styles the image inside a card). There was no way to style the .card itself if it happened to contain an image.
The :has() pseudo-class solves this. It acts as a relational selector, letting you apply styles to an element if the selector passed inside the :has() parenthesis matches.
It is commonly called the 'parent selector', but it's even more powerful than that—it can look ahead at siblings too.
Replacing JavaScript class toggling
Before :has(), if you had a form group and wanted the label to turn red when the input inside it was invalid, you had to write JavaScript to listen for validation events and attach an .is-invalid class to the parent div.
With :has(), you can simply write .form-group:has(input:invalid) { color: red; }. The browser handles the state automatically, perfectly in sync, with zero JavaScript.
This drastically reduces frontend logic and keeps styling concerns entirely inside your CSS.
Descendants and Siblings
To style a parent based on a child, just pass the child selector into :has(). article:has(video) selects any article containing a video.
To style an element based on its state, combine pseudo-classes. label:has(input:checked) styles a label differently if its internal checkbox is checked.
You can also use combinators to look at siblings. h2:has(+ p) selects an h2 only if it is immediately followed by a <p> tag, letting you adjust margins dynamically.
Try it
Interact with the child elements below and watch how the parent container restyles itself instantly using purely CSS.
The :has() Relational Selector
Delete the @ symbol to trigger :invalid
Check yourself
Pick an answer to lock it in, then read why. Getting one wrong is part of how it sticks.
Remember this
- The
:has()pseudo-class lets you select an element based on its descendants or subsequent siblings. - It eliminates the need for JavaScript when toggling parent classes based on child states (like
:checkedor:invalid). - You can use combinators inside
:has(), such as:has(+ p)to look at the very next sibling. - Avoid using
:has()with universal selectors (*) to maintain high rendering performance.
Done with this concept?
Mark it complete to track your progress. No login needed.