CSS PSEUDO CLASSES: :has(), :is() and :where()

CSS PSEUDO CLASSES: :has(), :is() and :where()

What’s a pseudo-class?

As a reminder, a CSS pseudo-class is a keyword added to a selector that specifies a special state of the selected element(s).

What is :has() ?

It is a part of CSS selector 4 called relational pseudo-class. It represents elements whose relative scope selector matches when evaluated absolute

  • Matching <a> elements that directly contain an <img>

The following selector matches only <a> elements that directly contain an <img> child:

a:has(> img)
  • Matching <h1> elements that are followed by a <p>

The following selector matches <h1> elements only if they have a <p> element directly following them:


For example:

header:has(h1) {
    background-color: blue;}

The mentioned example applies a blue background color to the header element with <h1> sub elements.

Up to now, we can only style down, from parent to child but never back up the tree. We can see here however that :has() sort of shake things up a bit. In fact, it is like CSS speak for “it lets us change the parent element <header> if it has a child or another element <h1> that follows it.”

What is :is() and :where()?

:is() and :where() are both pseudo-class functions that can help shorten and stop repetition in creating your selectors. They both take an array of arguments of selectors (ids, classes, tags, etc…) and selects any element that can be selected in that list.

The :is()  (formerly :matches(), formerly :any()) pseudo-class checks whether the element at its position in the outer selector matches any of the selectors in its selector list. It’s useful syntactic sugar that allows you to avoid writing out all the combinations manually as separate selectors. The effect is similar to nesting in Sass and most other CSS pre-processors.

For example, if we want to color adjust any <b> tags found inside a heading element, you could write:

h1 > b, h2 > b, h3 > b, h4 > b, h5 > b, h6 > b {
  color: hotpink;

Instead, we could use :is() and improve legibility while avoiding a long selector:

 :is(h1,h2,h3,h4,h5,h6) > b {
  color: hotpink;}

:where() can pretty much do the same. As an example, :where() can help us turn something like this:

.btn span > a:hover,
#header span > a:hover,
#footer span > a:hover {

into something like this

:where(.btn, #header, #footer) span > a:hover { 



Selector grouping 

Anything that :is() can do regarding grouping, so can :where(). This includes being used anywhere in the selector. Here’s a few more examples:

/* at the beginning */
:where(h1,h2,h3,h4,h5,h6) > b {
  color: hotpink;
/* in the middle */
article :is(header,footer) > p {
  color: gray;
/* at the end */
.dark-theme :where(button,a) {
  color: rebeccapurple;
/* multiple */
:is(.dark-theme, .dim-theme) :where(button,a) {
  color: rebeccapurple;

Each of the above selector examples demonstrates the flexibility of these two functional pseudo-classes.

So far, :is() and :where() are syntactically interchangeable.

What is the difference between :is() and :where()?

:where() and :is() both look and function identically but there is one difference to remember between them, that they have different specificities. :where() is simple and always has a specificity of 0 while :is() has the specificity of the strongest/most specific selector.

So quickly, what is CSS specificity?

There are four levels of the specificity hierarchy in CSS. Each of these levels or categories have a different score. We can calculate the specificity of a selector by adding up all the scores together.

Whichever selector has the greatest number will have their styles applied to that element.

Specificity hierarchy scores

  • IDs — Specificity score of 100
  • Inline styles — Specificity score of 1000
  • elements & pseudo-classes — Specificity score of 1
  • Classes, pseudo-classes & attributes — Specificity score of 10

For example:

button.btn {
  color: red;
}.btn {
  color: green;

.btn = 10

button.btn = 1 + 10 = 11

If we put the .btn class on a <button> tag the text would be red because the button.btn selector scores higher than the .btn selector.

How about their browser compatibility?

A quick tour by caniuse.com would give us the following results:




Although :has() isn’t supported in browsers right now (for performance reasons), it is part of the CSS Selectors Level 4 specification which is the same specification that has the extremely useful :not() pseudo-class. Unlike :has():not does have pretty decent browser support.

Stephan Celestin


Laisser un commentaire