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 lists | Yes | Yes |
| Matches any listed selector | Yes | Yes |
| Uses forgiving selector lists | Yes | Yes |
| Specificity contribution | Always 0-0-0 | Highest specificity among its arguments |
| Useful for easily overridden defaults | Yes | Less 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) h2 | 0-0-1 |
:where(button):hover | 0-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
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.
