CSS Specificity Calculator: Know Exactly Which Rule Wins
'Why isn't my CSS applying?' is very often a specificity problem, not a syntax one — a more specific selector elsewhere in the stylesheet is winning the cascade. This calculator computes the exact (ID, class, element) specificity score for any selector, including the trickier cases like :not(), :is(), and :where(), and lets you compare two selectors head-to-head.
Compared left to right as a three-part tuple — not added into a single number.
Why Specificity Isn't a Single Number
It's tempting to think of specificity as 'IDs worth 100 points, classes worth 10, elements worth 1' and sum them, but that's not quite how it works — the three counts are compared independently, left to right, like a version number. A selector with 1 ID always beats a selector with 100 classes, because the ID count is compared first and any difference there decides the outcome immediately.
:not(), :is(), and :has() Don't Add Their Own Specificity
These functional pseudo-classes are specificity-neutral wrappers — they take on the specificity of whichever argument inside them is most specific, rather than contributing a fixed value themselves. :not(.a, #b, div) evaluates the specificity of .a, #b, and div, and uses whichever is highest (#b) as its contribution.
:where() Is the Zero-Specificity Escape Hatch
Unlike :is()/:not(), :where() was specifically designed to always contribute zero specificity no matter what's inside it. This makes it valuable for base/reset styles you want to be trivially overridable — a single class selector anywhere else will always beat a :where()-wrapped rule, by design.
Practical Examples
Why an ID Beats Many Classes
The classic specificity surprise.
- 1.#main .content p → (1, 1, 1)
- 2..content p.intro → (0, 2, 1)
- 3.First selector wins: 1 ID beats 2 classes
Comparing :not() Specificity
The pseudo-class takes its argument's specificity.
- 1.:not(.a, #b) → contributes (1, 0, 0)
- 2.Same as if you'd written #b directly
What Counts Toward Each Part
- IDs: #header
- Classes, attributes, pseudo-classes: .card, [type], :hover, :nth-child()
- Elements, pseudo-elements: div, ::before
- Zero specificity: *, >, +, ~, :where()
Good Use Cases
- Debugging why a more 'obvious' style isn't applying
- Comparing two competing selectors before refactoring CSS
- Understanding :not()/:is()/:where() specificity rules
- Writing intentionally low-specificity base styles with :where()
Frequently Asked Questions
How is CSS specificity calculated?
As a three-part value (IDs, classes/attributes/pseudo-classes, elements/pseudo-elements). Count ID selectors (#id) for the first part, class selectors, attribute selectors, and pseudo-classes (.class, [attr], :hover) for the second, and element/type selectors and pseudo-elements (div, ::before) for the third. Higher values in the leftmost part always win, regardless of how many of the lower parts the other selector has.
What's the specificity of :not(), :is(), and :has()?
These take on the specificity of their most specific argument — :not(.a, #b) contributes the specificity of #b (the more specific of the two), not both combined and not a fixed value of its own.
Why does :where() always contribute zero specificity?
It's specifically designed as a zero-specificity version of :is() — useful for writing default/reset styles that are trivially easy to override, since anything with any specificity at all will beat a :where()-wrapped selector.
Does inline style or !important show up in this calculator?
No — inline styles (style="...") and !important both override normal specificity comparison entirely, sitting outside the specificity system this calculator computes for selectors in a stylesheet.
What happens when two selectors have equal specificity?
The one that appears later in the CSS (or later in a later-loaded stylesheet) wins — this is the 'cascade' part of 'Cascading Style Sheets,' and it's exactly why property order matters when specificity is tied.
Why does a universal selector (*) contribute nothing?
The universal selector, and combinators like >, +, ~, and the descendant space, don't add to specificity at all — they affect which elements match, not how strongly the rule should apply relative to other matching rules.