Using the CSS :has Pseudo‑Class

The :has CSS parent selector is a relatively new and less well‑known upcoming feature of CSS4 that allows you to select a parent element based on whether it has a specific child or children. Parent selectors as a concept are themselves fairly new (and not for want or need within the development community!).
Using :has, we can resolve a number of common front‑end scenarios, such as when you want to style a container based on the contents of one of its child elements. However, it is important to understand the limitations and ideal use cases of this selector before using it in your own projects.
At publication in May 2022, :has() was defined in the Selectors Level 4 working draft, but stable support was narrow. Safari 15.4 had shipped it in March. Chrome and Edge had not yet enabled it by default, and Firefox support arrived later. A fallback was therefore necessary for sites whose browser range extended beyond Safari 15.4.
Chrome and Edge were Chromium browsers using Blink by then, not WebKit. EdgeHTML had already been retired from the stable Edge channel, so it was not part of the support picture for this article. Chrome 105 and Edge 105 added :has() later in 2022, followed by Firefox 121 in December 2023. Check a current compatibility source before using it without a fallback.
Using :has
With the more stark compatibility issues out of the way, let's talk about actually using this relational pseudo‑class.
Usage should be very familiar; simply include the :has function followed by the child selector you want to match. For example:
div:has(p) { margin-bottom: 1rem;}What this will do is match any div with a p child, and set the bottom margin to 1rem. This falls back to more generic CSS selectors, so in the example above, both of these parent divs will match:
<!-- This div will match the selector above --><div> <p>Lorem ipsum dollar</p></div><!-- This div will also match the selector above --><div> <article> <p>Lorem ipsum dollar</p> </article></div><!-- This div will also match the selector above --><div> <!-- ..and so will this one --> <div> <p>Lorem ipsum dollar</p> </div></div>You can of course use more complex selectors like child selectors (>). For example:
div:has(> p) { margin-bottom: 1rem;}This will only match divs that have an immediate p child:
<!-- This div will match the selector above --><div> <p>Lorem ipsum dollar</p></div><!-- This div will not match the selector above --><div> <article> <p>Lorem ipsum dollar</p> </article></div>Any normal combination of CSS selector will work within :has, which makes it incredibly powerful.
What you can also do is chain multiple :has selectors together (in the same way you can with pseudo‑classes), which can create more explicit matches. For example, you could select a div element that has both a p element and a img element as children like this:
div:has(p):has(img) { padding: 10rem;}A selector list inside one :has() expresses a different condition. The following matches a div that has a p or an img (or both), whereas the chained form above requires both conditions:
div:has(p, img) { padding: 10rem;}The Wrap‑Up
To summarise, if you aren't put off by the current level of browser support, then :has is a very powerful feature that can help you to create more complex and dynamic styles for your website, and write more elegant CSS.
It is, however, important to understand its limitations and use it in appropriate scenarios. :has() is a relational selector, and a very broad match can give the browser more style invalidation and selector work when the DOM changes. Engines include optimisations for it, so keep the selector as narrow as the component allows and measure the actual page instead of assuming every use will be expensive.
Postscript
June 2026: :has() is no longer just an upcoming selector. It is usable in modern browser support matrices, but the advice to use it deliberately still stands. I would now frame the risk less as "can browsers do this?" and more as "does this selector keep the component state readable and cheap enough for the page it sits on?"