Avatar of Chris Coyier
Chris Coyier on (Updated on )

DataStax Astra — Open, multi-cloud stack for modern apps

The :where() pseudo selector in CSS is functionally identical to the :is() psuedo selector in that it takes a comma-separated list of selectors to match against, except that where :is() takes the most specific among them as the specificity of that whole part, the specificity of :where() is always zero (0).

For example:

main :where(h1, h2, h3) {
  color: orange;

Matches as if you wrote that selector like this:

main h1, main h2, main h3 {
  color: orange;

…except the specificity of the first example above is (0, 0, 0, 1) for the single element selector (main) and doesn’t include the specificity of anything in the :where() part. Whereas, in the second example, the specificity is (0, 0, 0, 2) because there are two element selectors.

Deciding on specificity handling

It’s a tough call deciding if you want the specificity of :is() or the zero-specificity of :where(), although I might argue that keeping specificity low in general leads to fewer headaches than working with elements that have a higher specificity.

Imagine a classic layout:

<header class="header"></header>
<main class="main"></header>
<aside class="aside"></aside>
<footer class="footer"></footer>

If I want to select the links in the header and footer, I could…

:is(.header, .footer) a {
  color: red;

That selector is (0, 0, 1, 0), meaning if I were to write:

header a {
  color: orange;

That selector is less specific at (0, 0, 0, 2), and will not override the other. But if I wrote:

:where(.header, .footer) a {
  color: red;

That is only (0, 0, 0, 1), so my override will work.

Perhaps a less-contrived example is selecting a header by ID without having to resort to ID-level specificity.

h3:where(#specific-header) {
  outline: 1px solid yellow;

There I’ve selected a header with a specific ID. IDs have very high specificity — (0, 1, 0, 0) — which is hard to override, but here I’ve managed to select just that header, but not use any super high specificity to do it.

Forgiving selectors

Each selector within the comma-separated list within :where(x, y, z) is forgiving in that it is ignored if it is invalid. That is important because it doesn’t wipe out the entire selector like invalid selectors normally do. For more information, see how it works with the :is() selector since it is identical.

:where(h1, totally:fake) {
  /* will still select h1 */

Browser support

:where() is supported across all the major browsers. No Internet Explorer though.

More information