:where()
and :is()
functionsEditor’s note: This deep dive into the CSS :where()
and :is()
functions was last updated on 4 January 2023 to update any outdated information, include sections on the :is()
function, and discuss browser compatibility.
The CSS :where()
function takes in a list of selectors as arguments and minifies them, allowing you to write less code and simultaneously style them all together.
In this tutorial, we’ll discuss the :where()
pseudo-class function and demonstrate how it can be used in production. We’ll review stacking, specificity, and forgiving in relation to the :where()
function. We’ll also look at some specific use cases and discuss its similarities and differences from the :is()
function. Let’s dive in!
Jump ahead
:where()
?:where()
function:where()
function:where()
function and forgiving:where()
:is()
function:where()
and :is()
functions:where()
?According to MDN, :where()
is a CSS functional pseudo-class selector that takes in a list of selectors as an argument and applies the given styles to any element from that list. :where()
is very useful for making a long selector list shorter.
In CSS, when multiple elements have the same style rules applied to them at the same time, we often end up writing a long list of selectors separated by commas.
Here’s an example in which we apply the same style to all <a>
tags found inside a header
, main
element, and footer
element:
header a:hover, main a:hover, footer a:hover { color: green; text-decoration: underline; }
There are only three elements that we are selecting in the above code snippet, but with a larger number of elements and selectors, the code will begin to look untidy and may become difficult to read and understand. This is where the :where()
pseudo-class function comes into play.
Here’s how the above example would look using the :where()
function:
:where(header, main, footer) a:hover { color: red; text-decoration: underline; }
Let’s take a closer look at how this code works.
When the browser gets to the code snippet, the code directs the browser to look for header
, main
, and footer
selectors and target all the anchor tags in those selectors. Then, when a user hovers over any of those anchor tags, the browser should apply the specified styles, which in this case are red
and underline
.
This pseudo-class function gives us the luxury of writing a long selector list in such a way that is shorter and more readily understandable.
:where()
functionWith the :where()
function, we can group elements in several ways and combinations. We can place the :where()
function at the beginning, middle, or end of the selector.
Here’s an example with multiple selectors and styles:
/* first list */ header a:hover, main a:hover, footer a:hover { color: green; text-decoration: underline; } /* second list */ article header > p, article footer > p{ color: gray; } /* third list */ .dark-theme button, .dark-theme a, .dim-theme button, .dim-theme a{ color: purple; }
Here’s the same code, rewritten with the :where()
function:
/* first list */ /* at the beginning */ :where(header, main, footer) a:hover { color: red; text-decoration: underline; } /* second list */ /* in the middle */ article :where(header, footer) > p { color: gray; } /* third list */ /* at the end */ .dark-theme :where(button, a) { color: purple; } .dim-theme :where(button, a) { color: purple; }
In the first list, we specify that the red
and underline
styles should be applied to the header
, main
, and footer
elements on hover
. In the second list, we specify that the article
, header
, and footer
elements should be styled with gray
.
We divided the third list into two :where()
functions for better clarity. In this list, we specify that the button
and a
element should be styled with the .dark-theme
, .dim-theme
, and purple
.
Now, we’ll even further reduce the third list functions, morphing them into one :where()
function:
/* stacked */ :where(.dark-theme, .dim-theme) :where(button, a) { color: purple; }
This strategy for reducing a complex selector list is referred to as stacking.
:where()
functionSpecificity is what browsers look at to determine what CSS property values or styles should apply to a particular element.
The specificity of the CSS :where()
function is always zero. Therefore, any element that is targeted with this function automatically gets a specificity of zero, as well. This gives us the power to easily nullify the style of any element we want while reducing its specificity to zero.
Here’s an example with HTML ordered lists:
<div> <h2>First list no class</h2> <ol> <li>List Item 1</li> <li>List Item 2</li> </ol> </div> <div> <h2>Second list with class</h2> <ol class="second-list"> <li>List Item 1</li> <li>List Item 2</li> </ol> </div> <div> <h2>Third list with class</h2> <ol class="third-list"> <li>List Item 1</li> <li>List Item 2</li> </ol> </div>
In the code snippet above, there are three ordered lists with two items in each list. The second and third lists have a given class
, whereas the first list does not.
Without any styling, we can see that each list is ordered numerically:
Now, let’s add some styling:
:where(ol[class]) { list-style-type: none; }
In the above snippet, we use the :where()
pseudo-class function to select all <ol>
tags that have a class
applied.
Below, we see that the second and third lists, which both have a class
, were targeted with the :where()
function and had their list-style-type
removed:
Now, let’s add some additional styling:
:where(ol[class]) { list-style-type: none; } .second-list { list-style-type: disc; }
Targeting only the second list using its class
name, we can see that it is now displayed with bullet points, while the third list still has no list-style
type:
You might be wondering, “Isn’t that how it’s supposed to be, seeing as the new styling is written below the :where()
function styling?” No, it’s not, and we’ll see that in a moment.
Let’s see what happens when we move the code we just added to the top of the code block and move the :where()
function portion to the bottom:
.second-list { list-style-type: disc; } :where(ol[class]) { list-style-type: none; }
Notice that the styling still doesn’t change:
Remember, the :where()
function has zero specificity. Regardless of whether the new code is placed before or after the :where()
function snippet, the specificity of the element targeted with :where()
will become zero, and its stylings will be nullified.
To illustrate this further, let’s add the second list’s stylings to the third list, following the :where()
function in the code:
.second-list { list-style-type: disc; } :where(ol[class]) { list-style-type: none; } .third-list{ list-style-type: disc; }
Both the second and third lists are displayed with bullet points, irrespective of code placement:
See the Pen
Using the CSS :where() function by Timonwa (@timonwa)
on CodePen.
See the Pen
Using the CSS :where() function by Timonwa (@timonwa)
on CodePen.
Now, let’s look at how the CSS :where()
function will react if one of the elements is targeted with an invalid selector.
:where()
function and forgivingCSS is generally considered to be non-forgiving with regard to selector lists. If a browser does not recognize just one selector in a list, the entire list of selectors will be considered invalid, and their styling will not be applied. However, this is not the case with the :where()
pseudo-class function.
If an element in a :where()
function is targeted with an invalid selector, that element will not get any styling. The rest of the elements will still get styled. The :where()
function will just skip over the invalid selector to the next (valid
) selector. This is why :where()
is known as a forgiving selector.
In the below example, :unsupported
is an invalid selector for many browsers. The below code above will be parsed correctly and will still match the :valid
selector, even in browsers that don’t support the :unsupported
selector, as shown below:
:where(:valid, :unsupported) { ... }
However, the following code will be ignored in browsers that don’t support the :unsupported
selector, even if they support the :valid
selector:
:valid, :unsupported { ... }
:where()
functionThe :where()
function can be a useful tool in some special use cases, but there are also some instances in which it should be avoided. Nearly all setbacks that occur when using the :where()
pseudo-class function come down to specificity. Because :where()
has zero specificity, we need to be very careful about where and when to use this function.
First, let’s look at a few use cases in which :where()
can be particularly helpful.
A CSS reset refers to loading a set of style rules prior to any other styles in order to clear the browser’s inbuilt styles. CSS resets are usually placed at the top or start of the CSS style sheet, so they load first. Developers often use them to remove the default stylings given by the browser to several elements initially before they start actually styling their elements and websites. CSS resets can also help remove inconsistencies between different browsers.
CSS resets are temporary stylings that would change later on in the styling process. However, depending on the simplicity or complexity of the selectors of an element or group of elements used in the CSS reset, it may be difficult to override the initial stylings later on in the code.
For example, let’s say we target all anchor tags on the website and style them in green. Then, we later decide to style all footer anchor tags in gray.
The new (gray color) style does not get applied due to the complexity of its selection in the CSS reset. The selector in the reset has a higher order of specificity than the selector used later in the code to target just the footer anchor tags, so the gray color style is not applied.
Now, if we add the :where()
pseudo-class function to the CSS reset, this automatically gives all elements in the reset a specificity of zero. This makes it easier for us to change the styles later on without having to worry about specificity conflicts.
The :where()
function can be useful if we want to remove or nullify the styles or reduce the specificity of an element or set of elements. The change will occur at the point that we place it in our code, almost like a mini reset.
Shorter code is easier to read and debug. A good rule of thumb is to examine any code that has more than two commas or three list items to see if it could be minified using the :where()
function. This is also a helpful strategy for combinations of two or more selectors; for example, section
> header
> p
> a
.
Now, let’s look at a use case in which the :where()
function should be avoided.
If it is important to ensure that the styling or specificity of an element or set of elements does not change at any point in the future, do not use the :where()
pseudo-class. The styling and specificity of any element targeted with this selector will be nullified.
:is()
functionThe CSS :is()
function works almost the same way as the :where()
function. You can use it to simplify complex selectors, and you can also place it at the beginning, middle, or end of the selector, just like the :where()
function.
It is also forgiving, just like the :where
function. So, when one of the selectors is invalid, that selector will be ignored by the browser, but the stylings of the valid selectors will be added to the selected elements.
:where()
and :is()
functionsThe difference between these two functions is that where the specificity of the :where()
function is always zero, the specificity of the :is()
function depends on the specificity of its most specific argument. For example, let’s look at a paragraph text in a header
element:
<header> <p>This is a paragraph text.</p> </header>
Then, let’s try to change the text color using four different selectors:
header p { color: blue; } :is(header, section) p { color: green; } p { color: blue; } :where(header, section) p { color: blue; }
The first selector gives the text a color of blue
. The second selector which uses :is()
has the same specificity as the first, but because it comes after the first one, it changes the text color from blue
to green
. The third selector, whose specificity is lower than both the first and second, has no effect on the text.
Lastly, the fourth one, which uses the :where()
function has no effect on the text because of its zero specificity:
See the Pen
Using CSS :is() function by Timonwa (@timonwa)
on CodePen.
See the Pen
Using CSS :is() function by Timonwa (@timonwa)
on CodePen.
All browsers, both on desktop and mobile, provide full support for the CSS :where
function, including support for its forgiving nature. So you do not have to worry about whether your styling will render properly in the browser.
The CSS :where()
pseudo-class function is very useful for improving CSS resets, removing styling at any point in our code, and making our code easier to read and debug.
In this article, we demonstrated how the :where()
function can be used in production and examined several use cases. We also saw its similarities to the CSS :is
function and the difference between them.
What are your thoughts on this CSS selector? Can you think of any other instances in which it would be useful? Let us know in the comments below.
As web frontends get increasingly complex, resource-greedy features demand more and more from the browser. If you’re interested in monitoring and tracking client-side CPU usage, memory usage, and more for all of your users in production, try LogRocket.
LogRocket is like a DVR for web and mobile apps, recording everything that happens in your web app, mobile app, or website. Instead of guessing why problems happen, you can aggregate and report on key frontend performance metrics, replay user sessions along with application state, log network requests, and automatically surface all errors.
Modernize how you debug web and mobile apps — start monitoring for free.
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowLearn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.
Bypass anti-bot measures in Node.js with curl-impersonate. Learn how it mimics browsers to overcome bot detection for web scraping.
Handle frontend data discrepancies with eventual consistency using WebSockets, Docker Compose, and practical code examples.
Efficient initializing is crucial to smooth-running websites. One way to optimize that process is through lazy initialization in Rust 1.80.
One Reply to "Deep dive into the CSS <code>:where()</code> and <code>:is()</code> functions"
Wow. This is awesome, Pelumi. I’m trying it out in my next project.