Skip to main content
CSS Tip

Why :is(::before,::after) doesn't work?

The functional pseudo-class :is() is ideal for shortening selectors. Take the following example:

.container button.large,
.container button.small
{
/* CSS */
}

We can write it using :is() like below:

.container button:is(.large,.small) {
/* CSS */
}

Following the same logic, you may be tempted to write the following selector:

.container button:is(::before,::after) {
/* CSS */
}

You select both pseudo-elements of button with one rule. Unfortunately, it won't work because pseudo-elements aren't valid within :is().

What's the Reason?

Since :is() is often presented as a way to shorten selectors, many people think that the browser will "expand" the selector before doing the evaluation.

So The following:

button:is(.large,.small) {

}

will first get transformed into:

button.large,
button.small
{

}

Then we select the elements.

While the logic appears to work for that particular case (and many others), it doesn't behave that way.

The :is() part is a pseudo-class that adds a condition to the selection, just like other selectors (such as class selectors, ID selectors, etc.) do. The key is to know how to read the selector, and you will understand why pseudo-elements aren't allowed:

Do you see the issue with the last selector? A button cannot match the selectors because a button cannot be a pseudo-element. The :is() pseudo-class is adding a condition to the selector. It's not selecting what is inside it. You cannot read it as "select ::before and ::after of the button element". You should read it as "select the button element that matches the selectors inside :is()". It won't work because a button cannot be a pseudo-element.

What about :is(::before,::after){} ?

Even if we don't have a selector before the :is(), the same logic apply because :is(::before,::after) is equivalent to *:is(::before,::after) which means "select any element that match the selectors inside :is()". It won't work because an element cannot be a pseudo-element.

You will probably find this a bit confusing and counter-intuitive, but never forget that pseudo-elements aren't allowed inside :is(). The same rule applies to the :not() and :where() pseudo-class.

What about button:is(:active,:hover,:focus){} ?

That selector is valid because it does make sense if we read it correctly:


More CSS Tips