Recent chats
ASKGEEZ Q&A GUIDETechnology & devices

How do :is(), :where(), and :not() affect selector specificity?

Quick answer

:is() and :not() take the specificity of their most specific argument; the functions themselves add no separate pseudo-class weight. :where() and every argument inside it contribute zero specificity. Use :where() when you want a precise target that stays easier to override. Specificity is considered only after cascade origin and importance, and within the relevant layer. [S1]

LET’S WORK THROUGH IT

The question, the details, the next step.

Question

What is the practical difference between :is(), :where(), and :not() for specificity?

Geez

Here’s the comparison:

  • :is() takes the specificity of its most specific argument. The function itself adds no separate pseudo-class weight. [S1]
  • :not() follows the same rule: its most specific argument determines its contribution, with no separate pseudo-class weight for the function. [S1]
  • :where() contributes zero specificity, including the specificity of all its arguments. [S1]

That makes :where() useful when a selector needs to describe a particular target but should remain easier to override. With :is() or :not(), a high-specificity argument can give the function a higher contribution than you intended. [S1]

Specificity is not the first cascade decision. It is compared after origin and importance, and within the relevant layer. [S1] Lowering specificity can help with conflicts only when those earlier cascade conditions do not decide the outcome.

Follow-up question

Can you show specificity calculations without making me guess what the functions add?

Geez

Yes. Let W(x) stand for the specificity of selector argument x. This notation shows how each function contributes without assigning a numeric score:

  1. :is(.card, #promo) → W(#promo), because #promo is the most specific argument in that list.
  2. :not(.muted, #promo) → W(#promo) for the same reason.
  3. :where(.card, #promo) → zero, including the arguments.

These follow the functions’ specificity rules. [S1] For a selector with other parts, include those parts too. For example, in :where(.results, .sidebar) .card, the :where() portion contributes zero; the separate .card portion still contributes its own specificity. [S1]

This comparison shows what each function contributes. It does not assign numeric tuples to the arguments.

Follow-up question

How could I use that difference to make a stylesheet easier to override?

Geez

Suppose you want to style a card inside either a results area or a sidebar. You could write :where(.results, .sidebar) .card { … }. The :where() portion contributes zero specificity, including its arguments, so grouping those locations adds no specificity burden. The separate .card selector still contributes. [S1]

By contrast, :is(.results, #featured) .card { … } takes the specificity of its most specific :is() argument. [S1] A more specific argument can therefore make the grouped selector harder to override on specificity than you intended. And :not() is not automatically low-specificity just because it expresses an exclusion: its strongest argument determines its contribution too. [S1]

These patterns are not a promise that a particular declaration will win. Origin, importance, and layer are considered before specificity. [S1] Check those conditions when an override does not behave as expected.

Follow-up question

What’s a quick way to choose and review one of these functions in my own CSS?

Geez

First decide what the selector needs to express, then choose accordingly: use :where() when its arguments should contribute no specificity; use :is() to group alternatives while accepting the strongest argument’s specificity; use :not() for exclusions while applying that same strongest-argument rule. [S1]

For a review, list the arguments inside each :is() or :not() and identify the most specific one. For :where(), record zero for both the function and its arguments. Then check whether cascade origin, importance, or the relevant layer explains the conflict before changing specificity. [S1]

This gives you a focused way to reason about an override: identify the function’s contribution, account for the rest of the selector, and consider the earlier cascade conditions.

A practical checklist

  • List the arguments inside each :is(), :where(), or :not().

  • For :is() and :not(), identify the most specific argument; it determines the function’s contribution. [S1]

  • For :where(), count the function and all its arguments as zero specificity. [S1]

  • Remember that selector parts outside these functions still contribute their own specificity.

  • Before changing a selector, check cascade origin, importance, and the relevant layer. [S1]

  • Prefer :where() when you want grouped targets without added specificity from the grouping.

A few more useful answers

Does `:not()` always have low specificity because it excludes something?

No. Its most specific argument determines its contribution, and the function itself adds no separate pseudo-class weight. [S1]

Does `:where()` make every declaration using it win?

No. It contributes zero specificity, but specificity is considered after cascade origin and importance, and within the relevant layer. [S1]

Do these examples give numeric specificity tuples?

No. They show how each function contributes: :is() and :not() take the most specific argument’s specificity, while :where() and its arguments contribute zero. [S1]

Sources

References used for this guide.

  1. [S1] Specificity — MDN
    developer.mozilla.org · Checked

YOUR QUESTION. YOUR DETAILS.

Let’s work through your situation.

Get a tailored answer or help with your next task. Starts at $1, with a quote before you pay.

Ask your own question
← Back to Geez at Work