🚀 LEVEL UP TO SENIOR:Unlock 500+ Advanced Practical Challenges & Exercises.
🎓 COURSERA PARTNER:Earn professional Google, Meta, and IBM certificates to supercharge your resume.
CSS MASTER CLASS /// VISUAL ENGINEERING /// LAYOUT DESIGN /// ANIMATION LAB /// CSS MASTER CLASS /// VISUAL ENGINEERING ///

:is() and :where(): Grouping Selectors With Opposite Specificity Philosophies

Learn how :is() and :where() group selector lists to reduce repetition, why :is() inherits the specificity of its strongest argument while :where() always contributes zero, and when each is the right choice.

Total XP: 0|💻 css XP: 0

Skill Matrix

UNLOCK NODES BY LEARNING NEW TAGS.

:is() and :where()

Selector grouping, divergent specificity.


🚀 LEVEL UP TO SENIOR:Unlock 500+ Advanced Practical Challenges & Exercises.
🎓 COURSERA PARTNER:Earn professional Google, Meta, and IBM certificates to supercharge your resume.

Both :is() and :where() solve the same repetition problem, matching identical elements for identical selector lists. The single axis they diverge on — specificity — is exactly the axis that decides which one belongs in your stylesheet.

1Identical Matching, Different Purpose

Both functions accept the same kind of argument — a comma-separated selector list — and match any element satisfying any one of the listed selectors. :is(h1, h2, h3) and :where(h1, h2, h3) match exactly the same set of headings; there is no scenario where one matches an element the other doesn't.

This means the choice between them is never about correctness of matching — it's entirely about how much specificity weight you want the resulting rule to carry, which has real downstream consequences for how easily future rules can override it.

:is(h1, h2, h3) { font-weight: 700; }
:where(h1, h2, h3) { font-weight: 700; }
/* Identical matching. Different specificity. */
localhost:3000
✓ Same Elements MatchedBoth rules apply to every h1, h2, and h3 — the only difference is how hard future rules have to work to override them.

2The One Difference That Matters

:is() computes its specificity the same way a normal selector list would — it takes on the specificity of its single most specific argument. :is(.nav, #header) a therefore has the specificity of an ID selector, since #header outweighs .nav.

:where(), introduced specifically to solve this, always contributes exactly zero specificity, no matter what's inside it — :where(#header) still counts as zero. This isn't a bug or edge case; it's the entire reason :where() exists as a distinct feature from :is().

:is(.nav, #header) a { } /* Specificity: ID-level */
:where(.nav, #header) a { } /* Specificity: zero */
localhost:3000
:is(.nav, #header) a → (1,0,1)
:where(.nav, #header) a → (0,0,1)

3A Practical Decision Rule

Default to :where() any time you're writing base styles, resets, or design-system defaults meant to be freely overridden by consuming code — its zero specificity guarantees that even the simplest later class selector wins without a fight. Reach for :is() when you're grouping selectors purely to avoid repetition in your own, self-contained styles, where you're not particularly concerned about external overrides and want the specificity to behave the way it normally would.

A useful mental shortcut: :where() says 'match these, but don't fight for priority'. :is() says 'match these, and take the priority you'd normally get'.

/* Design-system base: freely overridable */
:where(.button, .badge, .chip) { font-family: inherit; }
localhost:3000
✓ Designed To Be OverriddenA single consuming project rule like .button { font-family: Georgia; } wins instantly — no specificity escalation needed.

4Step-by-Step Breakdown

Two Selectors, One Job, One Critical Difference. :is() and :where() both let you group a list of selectors to avoid repeating a shared context — :is(h1, h2, h3) a instead of h1 a, h2 a, h3 a. They match exactly the same elements. The single difference that decides which one you should reach for is specificity.

Grouping Selectors To Eliminate Repetition. Both functions accept a comma-separated selector list and match any element that matches any selector in that list, letting you factor out a shared ancestor or descendant instead of writing near-duplicate rules for every combination.

Selector Grouping. What do :is(header, footer, nav) a and writing header a, footer a, nav a separately have in common?

  • They always produce identical specificity
  • They match exactly the same set of elements
  • Nothing — they're unrelated features

:is() Takes The Highest Specificity Of Its Arguments. :is() behaves like the selector list was 'the strongest one wins' for specificity purposes: :is(#header, .nav) a carries the specificity of an ID selector, even though only one of its two arguments is an ID. This can produce surprisingly high-specificity rules from what looks like a convenience grouping.

:is() Specificity. What is the effective specificity contribution of :is(#header, .nav)?

  • The average of an ID and a class selector
  • That of an ID selector — the highest-specificity argument in the list
  • Zero — :is() never contributes specificity

:where() Always Contributes Zero Specificity. :where() matches identically to :is() but always contributes zero specificity, regardless of what's inside it — even :where(#header) counts as specificity zero. This makes it the ideal tool for base styles or design-system defaults meant to be trivially overridden by literally any other rule.

Choosing :where(). Why would a component library author prefer :where() over :is() for their default base styles?

  • Because :where() is faster to parse than :is()
  • Because :where()'s zero specificity guarantees consumers can override the defaults with even a single low-specificity class
  • There's no meaningful reason — they're interchangeable

Grouping Selectors Mastered. You now know when to reach for each: :is() when you want the grouped selector's specificity to reflect its strongest argument, and :where() when you deliberately want zero specificity so the rule stays maximally easy to override — exactly the property a design system's base styles need.

Group Selectors With :is(). :is() lets you group multiple selectors into one, applying the same declarations to all of them.

Level Up 🚀

Advanced cheat sheets, SEO tricks, and interview prep for this topic.

Browser Support

ChromeSupported

Fully supported.

FirefoxSupported

Fully supported.

SafariSupported

Fully supported.

EdgeSupported

Fully supported.

Accessibility (A11y)

1Zero-Specificity Base Styles Reduce The Risk Of Accidentally Unoverridable Accessibility Fixes

If a design system's default styles use :where(), a consumer's targeted focus-state or contrast fix is guaranteed to apply without needing !important or a specificity war, keeping accessibility patches simple to ship.

2Grouped Selectors Can Simplify Auditing Focus Styles Across Many Interactive Elements At Once

Using :is(button, a, input, select):focus-visible to define one consistent focus treatment reduces the chance any single interactive element type is accidentally missed during an accessibility pass.

SEO Implications

  • 1

    Selector Grouping Reduces Total Stylesheet Size Through Less Repetition

    Replacing many near-duplicate selectors with a single :is() or :where() group can measurably shrink a compiled stylesheet, directly reducing the CSS payload every page load has to fetch and parse.

  • 2

    :where()-Based Component Libraries Ship Fewer Specificity-Escalation Overrides Downstream

    When a component library's defaults use :where(), consuming projects need less counter-specificity CSS to override them, keeping the final shipped stylesheet leaner across the whole site.

Best Practices

Default Component-Library And Reset Styles To :where()

This guarantees consumers can override any default with the simplest possible selector, which is exactly the ergonomics a shared base layer should offer.

Use :is() Freely Within Your Own Application Styles To Reduce Repetition

Since you control both sides of the specificity relationship in your own codebase, :is()'s normal specificity behavior is rarely a problem and the repetition savings are worth it.

Frequent Bugs

THE BUG

A design-system base style can't be overridden by a consuming project no matter how the selector is written.

THE FIX

The base style used :is() with a high-specificity argument (like an ID) instead of :where(), which always contributes zero specificity and would have avoided the conflict entirely.

THE BUG

A grouped selector using :is() unexpectedly outranks a rule that should have won by source order.

THE FIX

Check whether one of :is()'s arguments has unexpectedly high specificity (like an ID) — that becomes the whole group's specificity, not an average or the lowest argument.

Real-World Examples

A Freely Overridable Component Library Reset

A shared component library resets typography defaults using :where() so every consuming application can override them with ordinary, low-specificity class selectors.

:where(.btn, .badge, .chip) {
  font-family: inherit;
  line-height: 1.2;
}

Interview Prep

?Frequently Asked Questions

Pascual Vila

Pascual Vila

Frontend Instructor // Code Syllabus

Common Pitfalls & Errors

The Error //

Using :is() for a design-system default that needs to stay overridable

/* Wrong: can escalate specificity unexpectedly */ :is(.btn, #legacy-btn) { } /* Correct: always zero specificity */ :where(.btn, #legacy-btn) { }

The Solution //

Switch to :where() so the base style always contributes zero specificity, regardless of its arguments.

The Error //

Assuming :is() and :where() differ in which elements they match

/* Both match the exact same elements */ :is(h1, h2, h3) { } :where(h1, h2, h3) { }

The Solution //

Remember they match identically — only their specificity contribution differs.

Lesson Glossary

[01]:is()

Groups a selector list, taking the specificity of its strongest argument.

Code Preview
:is(h1, h2, h3)

[02]:where()

Groups a selector list with guaranteed zero specificity.

Code Preview
:where(h1, h2, h3)

[03]Selector List

A comma-separated group of selectors treated as alternatives.

Code Preview
a, b, c

[04]Specificity Escalation

A rule unintentionally gaining high specificity via a grouped argument.

Code Preview
:is(#id, .class)

Continue Learning