CSS Specificity, Computed: What :is(), :not() and :where() Actually Add
When two rules set the same property on the same element, the cascade compares origin and importance first, then specificity, then source order. Specificity is a triple, compared left to right. The specificity calculator computes it for a list of selectors; this page is the rulebook it implements (Selectors Level 4).
The three columns
| Column | Counts | Example | Score |
|---|---|---|---|
| ids | #id | #nav | 1,0,0 |
| classes | .class, [attr], :pseudo-class | a.btn:hover[href] | 0,3,1 |
| types | div, ::pseudo-element | ul li::before | 0,0,3 |
*, combinators (>, +, ~, space) and the :where() function add nothing. The columns do not carry: #a (1,0,0) beats a selector of eleven classes (0,11,0) because the first column is compared first. The old story that 256 classes overflow into an id was true of a 1990s byte-per-column implementation and is not true of current engines.
The functions that change the arithmetic
| Selector | Score | Rule |
|---|---|---|
a:not(.x, #y) | 1,0,1 | :not() is replaced by its most specific argument, not the sum. |
:is(h1, .title) span | 0,1,1 | Same rule for :is(). The h1 branch is ignored because .title is more specific. |
:where(#app) p | 0,0,1 | :where() is always zero, whatever is inside it. |
li:nth-child(2n+1 of .a, #b) | 1,1,1 | One pseudo-class, plus the most specific selector after of. |
a:before | 0,0,2 | The legacy single-colon form is still a pseudo-element. |
The consequence of the :is() rule: the score of a list is the score of its heaviest member, even when the element matched the light one. :is(h1, #hero) span costs 1,0,1 on every h1 span too.
Why :where() exists
A reset or a component library that ships :where(.btn) instead of .btn has specificity 0,0,0 for that part, so any rule you write afterwards wins without a specificity fight. It is the supported way to write a default that is meant to be overridden.
What is outside the score
- Inline
style=""outranks any selector. !importantis a separate layer: an important declaration beats all normal ones, and between two important declarations specificity decides again.@layer: for normal declarations a later layer beats an earlier one regardless of specificity, and unlayered styles beat all layers. Specificity only matters inside one layer.- Nesting (
&): a nested selector's score is computed from the rule it expands to, so the calculator flags&instead of guessing.
Reading the calculator's output
Selectors are ranked most-specific first; on a tie the rule written later is listed first, because it wins. Paste a whole stylesheet and the tool extracts every selector, including ones inside @media.