Using the where(), is(), and has() CSS pseudo classes

With the has() pseudo classing rolling out to browsers I've seen more talking about pseudo classes and ways to utilize them. To help myself better understand them I want to go over a few of the more recent ones, where(), is(), and has().

2022 new CSS hotness series

This is article is a part of a series I'm going to be doing talking about some of the cool new things that have come to CSS in the last couple of years.

  1. Fluid type and sizing using clamp(), min(), max()
  2. where(), is(), has() (You are here)
  3. :focus-visible
  4. aspect-ratio
  5. Container Queries

where() and is()

Where() and is() are both pseudo classes that function almost identically. Both where() and is() take a list of selectors and then apply classes to the those selectors or to children under them.

/* How we used to do this */
.post p,
.content p,
footer p
{
color blue
}

/* How it can be writtern now */
:where(.post, .content, footer) p {
color: blue;
}

The difference in the two comes down to specificity. where() has 0 specificity and is() has the same specificity as the most specific item in the selector list.

:where() example

:where()'s lack of specificity makes it a prime candidate for reset styles. You can see in this example that .article p overwrites the base :where() styles.

Note if you declare the :where block after declaring footer p the where will take specificity. Since element selectors have 0 specificity.

:is() usage

:is() on the other hand can be a bit more tricky to override. In this example you will notice that .article p overwrites the base :where() styles, but footer p does not. That is because the :is() block takes the specificity level of the most specific selector in the selector list.

In this case the most specific selector in the list is .article p. It is the more specific than footer p in the list because class selectors have more specificity than element selectors. Then it is also more specific than .main p since it comes second in the list.

has()

For a long time people have been asking for a way to select an elements parent in CSS. For quite a while doing so would involve using JavaScript to find the element in question and then select its parentNode. Now you can (mostly) do this instead use has() in CSS.

To use has() you select an element/class and then check to see if a child/class/condition exists within it. In its simplest form you can for example check a section tag to see if a h2 is inside of it and if so make that background a different color than other section tags.

section {
background: red;
}

section:has(h2) {
background: blue;
}

It's also possible to make more complex selectors by using other pseudo class. In this codepen example, I'm watching for when an a tag is hovered within a section and then am changing the background and text color.

This example doesn't currently work in Firefox.

It's also possible to get even more crazy and do something such as Dave Rupert's solution to styling dangling elements.

Compatibility

A downside for has() is that it is still pretty new and only now starting to roll out into browsers. Currently only Chrome desktop and Safari (Desktop and iOS) support it without flags.

Firefox Chrome Edge Safari iOS Chrome Android Firefox Android
None 105-106 None 15.4+ 15.4+ None None

Full compatibility charts

MDN | Can I Use