What Is the Difference Between CSS :is() and :where()?
Both the :is() and :where() pseudo-class
functions allow developers to group multiple selectors into a single,
compact rule and benefit from forgiving selector lists. While their
syntax, browser support, and grouping mechanisms are identical, their
single functional difference lies entirely in
specificity: :where() always carries a
specificity score of zero regardless of its contents, whereas
:is() takes on the specificity of the most specific
selector in its argument list.
Understanding Specificity in CSS
CSS specificity determines which style declaration takes precedence when multiple rules target the same element. It is commonly calculated as a three-part score:
- ID selectors (
#id) have the highest weight. - Class, attribute, and pseudo-class selectors
(
.class,[type="text"],:hover) carry medium weight. - Type (tag) selectors and pseudo-elements
(
h1,p,::before) carry the lowest weight.
When using standard nested or grouped selectors, every element in the chain adds to the overall specificity score.
How :is() Handles
Specificity
The :is() pseudo-class dynamically adopts the
specificity of the most specific selector provided inside its
parentheses. Even if an element matches a simpler selector within the
list, the entire rule is scored against the highest-weighted
argument.
/* Specificity is determined by #main-nav (1, 0, 0), not .menu (0, 1, 0) or header (0, 0, 1) */
:is(header, .menu, #main-nav) a {
color: blue;
}In the example above, targeting header a will evaluate
with an ID-level specificity of (1, 0, 1) because
#main-nav is inside the selector list. This can make
subsequent overrides harder to apply unless equally specific selectors
are used.
How :where() Handles
Specificity
The :where() pseudo-class strips all specificity from
its arguments, always evaluating to (0, 0, 0). The only
specificity applied to the overall rule comes from whatever selectors
exist outside of :where().
/* Specificity is (0, 0, 1) solely from the 'a' tag */
:where(header, .menu, #main-nav) a {
color: blue;
}Even though #main-nav is listed inside
:where(), the rule can be overridden by any single class,
tag, or utility selector later in the stylesheet:
/* Overrides the rule above because a class has specificity (0, 1, 0) */
a.nav-link {
color: red;
}Side-by-Side Comparison
| Feature | :is() |
:where() |
|---|---|---|
| Specificity Calculation | Specificity of the most specific argument | Always zero (0, 0, 0) |
| Forgiving Selector List | Yes (invalid selectors are ignored) | Yes (invalid selectors are ignored) |
| Primary Use Case | Reducing repetition without reducing specificity | CSS resets, base component defaults, theme layers |
| Ease of Overriding | Harder (depends on argument specificity) | Very easy (any standard selector overrides it) |
When to Use :is() vs.
:where()
Use :where() when writing foundational CSS, such as CSS
resets, third-party component libraries, or base typography sheets.
Because :where() creates zero-specificity rules, end users
and consumers can easily override styles with simple utility classes
without relying on !important or overly complex selector
chains.
Use :is() when grouping selectors to clean up complex
stylesheets where the existing specificity structure must remain intact
to prevent unintentional style leaks across different application
states.