We can use :has, finally!

A dive into the most anticipated CSS feature

For years, CSS developers have wished for the ability to select an element based on its content. Although CSS provides selectors that can select elements based on a lot of characteristics, until recently there was no way to select an element based on what it contained.

Fortunately, the introduction of :has() changed this. This new feature is a game-changer, as it allows you to select an element based on its content.

In this post, we’ll dive into one of the most anticipated CSS features: the :has pseudo-class. And it turns out to be much more than “just” a parent selector.

The Syntax

The :has pseudo-class is used to select elements based on their contents. It is applied to the element we want to apply rules to, and we pass it to the selector of the element it should contain:

// Here we target any element with the 
// class `post` containing an `h1`
.post:has(h1){
	background-color: teal;
}

Using :has as a parent selector

Using :has as a parent selector can simplify many situations. Here are some examples that come to mind:

  • On some pages of your app, you may want to change the global font size or background color of the body element. Previously, before the :has pseudo-class was introduced, we would have needed the backend to toggle some HTML class based on the page type. However, with a parent selector, this is now trivial:

    body:has(.container.legal-mentions) {
    	font-size: 80%;
    }
    
  • On our list of blog posts, we want posts to change not have margin if the post contains an image:

    .post:has(img){
    	margin-left:0;
    }
    

This is extremely powerful in itself, but we can do even more when using combinators.

Going further with Combinators

Combinators combine other selectors in a way that gives them a useful relationship to each other and the location of content in the document.
MDN

We can use the child combinator > inside has to ensure that we are selecting a direct child element. For example, to select a div element that has a hr element as a direct child, we can use the selector div:has(>hr).

We can use the adjacent sibling combinator + to target an element followed by another element. For example, to select a title followed by a subtitle, we can use title:has(+.subtitle).

Combining with other Pseudo-classes

Changing the style of a container when hovering over a child element sounds pretty cool, doesn't it?

We can achieve this by combining has with hover. For example, if we want a container to have a border whenever one of its links is hovered, we would use the following code:

.container:has(a:hover){
	border: 2px solid pink;
}

Browser support

As of June 2023, the :has pseudo-class is missing only in Firefox. However, it is behind a flag, so it should be supported soon!

Screenshot of caniuse for the has feature

Screenshot of caniuse for the has feature

Conclusion

The :has pseudo-class is a powerful addition to the CSS selector arsenal. It allows you to select elements based on their contents, which can simplify many situations and make your code more maintainable. By using combinators, you can further refine your selections and achieve even more advanced effects.

While it is still missing in Firefox, it is expected to be supported soon. As always, make sure to test your code in all major browsers before deploying it to production.

Thanks for reading, and happy coding!

Learn how to use combinators and other pseudo-classes to achieve even more advanced effects.

I'm Tom Quinonero, I write about design systems and CSS, Follow me on twitter for more tips and resources 🤙