Using the CSS :has Pseudo‑Class

Hero image for Using the CSS :has Pseudo‑Class. Image by  .
Hero image for 'Using the CSS :has Pseudo‑Class.' Image by .

The :has CSS parent selector is a relatively new and less wellknown 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 frontend 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.

The biggest limitation here is going to be that the :has selector is currently only part of the working draft for CSS4 selectors, so as you might imagine, browser support is fairly limited and syntax/implementation are still likely to change as the specs mature. The Can I Use reads fairly bleakly at present; essentially unless your entire userbase is on very recent versions of WebKitbased browsers (Chrome, Safari, or Edge), then you're going to have to find an alternative solution, especially if any of them use Firefox...

Microsoft Edge is a surprise amongst those browsers that are compatible, but perhaps less of a surprise given that EdgeHTML is supposed to be fully compatible with the Webkit Layout Engine.


Using :has

With the more stark compatibility issues out of the way, let's talk about actually using this relational pseudoclass.

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 pseudoclasses), 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;}

You can also commaseparate selectors, so the same can be achieved like this:

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 only in appropriate scenarios. At the moment, :has is very performanceintensive (as it works backwards against the traditional CSS cascade), so you might be better off avoiding it on larger or more complex projects.


Categories:

  1. CSS
  2. Front‑End Development
  3. Sass