CSS :where() Pseudo-Class

The CSS :where() functional pseudo-class matches elements that satisfy any selector in a comma-separated selector list. Unlike :is(), the :where() pseudo-class and its arguments always contribute zero specificity. This makes it useful for grouping selectors, simplifying stylesheets, and creating default styles that are easy to override.

:where() Syntax

:where(selector1, selector2, selector3) {
  property: value;
}

The :where() pseudo-class accepts a comma-separated list of selectors. An element matches when at least one selector in the list matches that element.

Basic Example

:where(h1, h2, h3) {
  color: #006699;
}

Applies the same text color to first-, second-, and third-level headings.

Class Selectors

:where(.notice, .tip, .warning) {
  padding: 12px;
}

Within a Container

.article :where(h2, h3) {
  margin-top: 20px;
}

Multiple Selector Types

:where(p, ul, ol, blockquote) {
  margin-bottom: 16px;
}

Functional Pseudo-Class

The parentheses are required because :where() takes selector arguments. Unlike a simple pseudo-class such as :hover, it cannot be written without them.

How :where() Works

The :where() pseudo-class provides a compact way to match multiple selectors while keeping its specificity contribution at zero.

Step 1 - Create HTML Elements

<h2>Section Heading</h2>
<h3>Subsection Heading</h3>
<p>Example paragraph.</p>

Step 2 - Group the Headings

:where(h2, h3) {
  color: #006699;
}

Step 3 - Match Either Heading

Both the <h2> and <h3> elements match because each appears in the selector list.

Step 4 - Override the Default

h3 {
  color: #b45309;
}

The h3 selector overrides the color for third-level headings because its specificity of 0-0-1 is higher than the zero specificity of :where(h2, h3).

Step 5 - Keep the Stylesheet Flexible

The grouped rule provides a shared default while more specific selectors can customize individual elements.

Selector Lists

The :where() pseudo-class accepts a selector list containing type selectors, classes, IDs, attributes, pseudo-classes, and more complex selectors.

Type Selectors

:where(h1, h2, h3) {
  line-height: 1.3;
}

Class Selectors

:where(.primary, .secondary, .tertiary) {
  border-radius: 4px;
}

ID Selectors

:where(#header, #footer) {
  padding: 20px;
}

Even an ID selector inside :where() contributes zero specificity.

Attribute Selectors

:where(input[required], select[required]) {
  border-width: 2px;
}

Pseudo-Classes

:where(a:hover, a:focus-visible) {
  text-decoration: underline;
}

Combining Selector Types

.form :where(input, select, textarea) {
  max-width: 100%;
  box-sizing: border-box;
}

The class selector outside :where() still contributes specificity. Only the pseudo-class and the selectors inside its parentheses contribute zero.

Zero Specificity

The defining feature of :where() is that it always contributes 0-0-0 specificity, regardless of the selectors inside it.

Type Selector Inside :where()

:where(p) {
  color: #006699;
}

Specificity: 0-0-0.

Class Selector Inside :where()

:where(.notice) {
  color: #006699;
}

Specificity: 0-0-0.

ID Selector Inside :where()

:where(#important) {
  color: #006699;
}

Specificity: 0-0-0.

Selector Outside :where()

.article :where(p, li) {
  color: #333;
}

Specificity: 0-1-0, contributed by the .article class.

Easy Overrides

:where(.notice) {
  color: #006699;
}
.notice {
  color: #b45309;
}

The second rule wins because .notice has higher specificity, even if the rules were written in the opposite order, assuming other cascade factors are equal.

:where() vs. :is()

The :where() and :is() pseudo-classes have similar matching behavior but different specificity rules.

Feature :where() :is()
Accepts selector listsYesYes
Matches any listed selectorYesYes
Uses forgiving selector listsYesYes
Specificity contributionAlways 0-0-0Highest specificity among its arguments
Useful for easily overridden defaultsYesLess suitable when arguments are highly specific

Using :where()

:where(h2, .heading, #title) {
  color: #006699;
}

Specificity: 0-0-0.

Using :is()

:is(h2, .heading, #title) {
  color: #006699;
}

Specificity: 1-0-0, because the ID selector is the most specific argument.

Choosing Between Them

Use :where() when the goal is low-specificity styling. Use :is() when grouping selectors without discarding their specificity is appropriate.

Forgiving Selector Lists

The :where() pseudo-class uses a forgiving selector list. If one selector in the list is invalid or unsupported, other valid selectors can still be used.

Example

:where(h2, :unsupported-example, h3) {
  color: #006699;
}

Assuming the middle selector is not recognized, the browser can discard that argument while retaining the valid h2 and h3 selectors.

Ordinary Selector List

h2, :unsupported-example, h3 {
  color: #006699;
}

In an ordinary comma-separated selector list, an invalid selector can invalidate the entire rule.

Important Limitation

Forgiving selector lists do not guarantee support for every new CSS feature. A browser must recognize :where() itself before it can process its arguments.

Unsupported Arguments

Invalid arguments are discarded rather than treated as matches. If no usable argument remains, the selector does not match any elements.

Complex Selectors

The :where() pseudo-class can contain complex selectors with combinators, making it useful for grouping related element patterns.

Descendant Selectors

:where(article h2, section h2) {
  margin-top: 24px;
}

Child Selectors

:where(nav > ul, footer > ul) {
  list-style: none;
  padding: 0;
}

Attribute and Class Combinations

:where(input[required], .required-field) {
  border-width: 2px;
}

Combining with :not()

:where(button, input[type="submit"]):not(:disabled) {
  cursor: pointer;
}

The :where() portion contributes zero specificity. The :not(:disabled) portion contributes the specificity of its argument, 0-1-0.

Combining with :has()

:where(.form-group):has(input:focus) {
  background-color: #f0f7ff;
}

The :has() argument contributes specificity even though the :where() portion does not.

Complexity and Readability

Although complex selectors are allowed, keep selector lists readable and avoid unnecessary nesting.

Nesting and Combining Selectors

The :where() pseudo-class can be used with other selectors, pseudo-classes, and CSS nesting.

With a Class Selector

.content :where(h2, h3, h4) {
  line-height: 1.3;
}

With a Pseudo-Class

:where(a, button):focus-visible {
  outline: 2px solid #006699;
}

With a Descendant Selector

:where(article, aside) p {
  margin-bottom: 16px;
}

The p selector outside :where() contributes 0-0-1 specificity.

With CSS Nesting

.article {
  :where(h2, h3) {
    color: #006699;
  }
}

The nested rule targets headings inside .article. The outer class contributes specificity, while :where() contributes zero.

Nested :where()

:where(.content :where(h2, h3)) {
  margin-top: 20px;
}

Nesting one :where() inside another is permitted, although it is usually unnecessary.

Practical Uses

The :where() pseudo-class is especially useful for reusable styles, component defaults, and CSS resets.

Heading Defaults

:where(h1, h2, h3, h4, h5, h6) {
  line-height: 1.3;
}

Form Controls

:where(input, select, textarea, button) {
  font: inherit;
}

Navigation Lists

:where(nav ul, nav ol) {
  margin: 0;
  padding: 0;
  list-style: none;
}

Component Defaults

:where(.card, .panel, .notice) {
  padding: 16px;
  border-radius: 6px;
}

Low-Specificity Reset

:where(h1, h2, h3, p, ul, ol) {
  margin-block: 0;
}

Easy Customization

:where(.button) {
  background-color: #006699;
  color: #fff;
}
.button {
  background-color: #b45309;
}

The ordinary class selector overrides the default background because it has higher specificity.

:where() Specificity

The :where() pseudo-class and everything inside its parentheses contribute zero specificity. Selectors outside the parentheses continue to contribute normally.

Specificity Examples

Selector Specificity
:where(p)0-0-0
:where(.notice)0-0-0
:where(#main)0-0-0
:where(h2, .heading, #title)0-0-0
.article :where(h2, h3)0-1-0
article :where(h2, h3)0-0-1
:where(.article) h20-0-1
:where(button):hover0-1-0
:where(.button):not(:disabled)0-1-0
:where(.card):has(img)0-0-1

Specificity Comparison

:where(.notice) {
  color: #006699;
}
.notice {
  color: #b45309;
}

The second rule wins because .notice has a specificity of 0-1-0, while :where(.notice) has 0-0-0.

Cascade Considerations

Specificity is only one part of the CSS cascade. Cascade origin, importance, cascade layers, and other factors can affect which declaration wins.

Limitations

The :where() pseudo-class simplifies selector grouping, but it does not replace every other selector technique.

Always Zero Specificity

You cannot make the contents of :where() contribute specificity. If that behavior is needed, use another selector or consider :is().

Does Not Override Higher Specificity

A low-specificity rule can be overridden by a more specific declaration when other cascade factors are equal.

Does Not Change HTML Structure

The selector matches existing elements. It does not create, remove, or rearrange elements.

Does Not Automatically Reduce File Size

Grouping selectors can reduce repetition, but the effect on stylesheet size depends on the code being replaced.

Browser Support

Older browsers that do not recognize :where() will ignore rules containing the unsupported pseudo-class.

Pseudo-Elements

Pseudo-elements such as ::before and ::after cannot be included as arguments inside :where(). They can be placed outside the functional pseudo-class when appropriate.

Not a Replacement for Cascade Layers

The :where() pseudo-class controls specificity contribution, while CSS cascade layers organize precedence between groups of declarations.

Best Practices

  • Use :where() when grouping selectors without increasing specificity.
  • Use it for default styles that should be easy to override.
  • Keep selector lists short enough to remain readable.
  • Remember that ID and class selectors inside :where() still contribute zero specificity.
  • Use :is() when the highest specificity of the selector arguments should be retained.
  • Place selectors outside :where() when they need to contribute specificity.
  • Use forgiving selector lists carefully and test newer selectors in supported browsers.
  • Do not assume that source order overrides a selector with higher specificity.
  • Combine :where() with cascade layers when a stylesheet needs additional control over rule precedence.
  • Test styles in older browsers if compatibility with browsers lacking :where() is required.

:where() Example

This example demonstrates how :where() provides shared heading styles without increasing specificity. Both headings receive a default blue color, but the ordinary h3 selector overrides the color of the second heading.

HTML

<div class="where-demo">
  <h2>Section Heading</h2>
  <h3>Subsection Heading</h3>
  <p>The headings share a default style, but the h3 color is overridden.</p>
</div>

CSS

.where-demo :where(h2, h3) {
  color: #006699;
  font-family: Arial, sans-serif;
  margin-bottom: 10px;
}
.where-demo h3 {
  color: #b45309;
}
.where-demo p {
  color: #333;
}

Browser Result

Play in Editor

Note: The :where() portion contributes zero specificity, but the .where-demo class outside it contributes 0-1-0. The .where-demo h3 selector has higher specificity and overrides the heading color.

Browser Support

Baseline: Widely available. The :where() pseudo-class is supported in all major modern browsers, including Chrome, Edge, Firefox, Safari, and Opera. Older browser versions may not recognize the selector.

For current compatibility information, visit Can I Use? for :where() support.

Specifications

The :where() functional pseudo-class is defined in CSS Selectors Level 4. The specification establishes its selector-list matching behavior and zero-specificity rule.

CSS Selectors Level 4 - :where()