How to use the css specificity calculator
- Paste selectors, one per line. A line with commas is treated as a list, and each selector in it is scored on its own.
- Read the table for the three numbers of each selector: IDs, then classes, attributes and pseudo-classes, then types and pseudo-elements.
- The row with the highest score is highlighted. If several tie, the one that appears last in the stylesheet wins.
- Fix any selector the page flags, such as an unclosed bracket or a combinator with nothing before it.
Formula
a = number of ID selectors (#id)
b = number of classes (.class), attributes ([x]) and pseudo-classes (:hover)
c = number of types (div) and pseudo-elements (::before)
:is(), :not(), :has() count as their most specific argument; :where() counts as nothing
Scores compare from left to right as three separate numbers, not as a single base 10 number. :nth-child(An+B of S) counts as one pseudo-class plus the most specific selector S.
Selectors and their scores
ul li scores 0,0,2 and li.red.level scores 0,2,1, so the class selector wins even though it is shorter in total. #x34y scores 1,0,0 and beats any number of classes. #s12:not(p) scores 1,0,1 because :not counts as its argument p.
:is(em, #foo) scores 1,0,0, since the most specific argument is the ID, while :where(em, #foo) scores 0,0,0. .foo :is(.bar, #baz) scores 1,1,0. In the starting list, a[href^="https"]:not(.ext, #skip) scores 1,1,1 and is the highest, because the type and the attribute count 0,1,1 and :not adds the ID from its list.
How the cascade uses specificity
When two rules set the same property on the same element, the browser first compares importance and origin, then cascade layers, and then specificity. Only after that does it use the order in the stylesheet. So a more specific selector wins over a later but less specific one, which is why adding a class sometimes does nothing and adding an ID suddenly does.
Inline styles and !important rules are outside the three numbers. Keeping specificity low and flat makes a stylesheet easier to override. Selectors with :where() are useful for defaults because they add nothing.
Reading the result
The score is about the selector alone and not about the page, so the page cannot tell you which element matches. To test whether a selector matches anything in a piece of HTML, use the CSS Selector Tester. The CSS Formatter tidies a stylesheet and the CSS and HTML Minifier shrinks it. For color work in the same stylesheet, see the Color Contrast Checker.
Assumptions and limits
- Scores follow the Selectors Level 4 rules, including :is(), :not(), :has(), :where() and :nth-child() with an of-clause.
- The legacy pseudo-elements :before, :after, :first-line and :first-letter count as pseudo-elements.
- Up to 300 selectors are scored at a time. Comments and rule bodies in braces are ignored.
- Scoring does not check that a selector is meaningful for the elements it targets, and nothing is sent anywhere.
Frequently asked questions
Is 0,1,10 higher or lower than 0,2,0?
Lower. The middle number is compared first, so 2 classes beat 1 class however many types follow. The three numbers are never combined into a single value.
Does the universal selector add specificity?
No. The asterisk, combinators such as the space or the greater-than sign, and the :where() pseudo-class all add nothing. A namespace prefix does not add either.
How does :not() count?
It counts as the most specific selector in its list, and the :not itself adds nothing. So :not(.a, #b) counts as the ID, 1,0,0, because the ID is more specific than the class.
Why is a pseudo-element counted with the types?
The rules put types and pseudo-elements in the same, lowest category, because they select by element rather than by a class, attribute or state.