CSS Specificity Calculator
Calculate the CSS specificity of one or more selectors as an (A, B, C) triple — ID, class/attribute/pseudo-class, and type/pseudo-element counts — to debug why a style rule isn't winning.
Input
Output
| Selector | ID | Class | Type | Specificity |
|---|---|---|---|---|
| No data yet | ||||
Guides
What is CSS specificity?
When two CSS rules target the same element and set the same property, the browser needs a tiebreaker to decide which one wins. That tiebreaker is specificity — a score computed from the selector itself, not from where the rule appears in the stylesheet (that only matters as a final tiebreaker, when specificity is equal). A more specific selector always beats a less specific one, no matter which rule was written last.
The (A, B, C) system
Specificity is expressed as three numbers, compared left to right like a version number — any difference in A outweighs any difference in B, which outweighs any difference in C:
- A — ID selectors. Each
#idin the selector adds one. - B — classes, attributes, and pseudo-classes. Each
.class,[attr], or:hover-style pseudo-class adds one. - C — types and pseudo-elements. Each tag name (
div,a) and each pseudo-element (::before,::after, and the legacy single-colon forms:before/:after/:first-line/:first-letter) adds one.
The universal selector (*) and combinators ( , >, +, ~) always contribute zero — they narrow what's matched, not how specific the match is. So #header scores (1, 0, 0), .nav a:hover scores (0, 2, 1) (two class-ish selectors, one type), and .btn.btn-primary — two classes on the same element — scores (0, 2, 0), beating a single-class rule even though it's the "same kind" of selector.
The :not() / :is() / :has() gotcha
Modern CSS scores :not(), :is(), and :has() by the specificity of their most specific argument, not as a flat pseudo-class. This calculator uses the simpler, pre-Selectors-4 approximation instead: each of these (and any other functional pseudo-class, like :nth-child()) counts as a single pseudo-class — a flat +1 to B, regardless of what's inside the parentheses. It's a documented scope cut, not a bug: it keeps the parser simple and is accurate for the common case (a single, unnested argument), but will undercount a :not() wrapping something with an ID inside it. :where() always scores zero, per spec, no matter its contents.
How to use it
Paste one CSS selector per line — as simple as .btn or as compound as #main .content > p::before:hover. Each line gets its own row with its ID, class, and type counts plus the combined (A, B, C) score. Results update as you type.
FAQ
Why does div > p::before outrank #main... or does it? It doesn't — an ID (A) always outweighs any number of type selectors or pseudo-elements (C), by definition of how the three numbers compare.
Does the order of rules matter if specificity is equal? Yes — when two rules have identical specificity, the one that appears later in the cascade (or was loaded later) wins. !important overrides specificity entirely, and inline style="" attributes outrank any selector-based rule.
Does this handle selector lists like .a, .b? Treat each comma-separated group as its own line for a per-selector breakdown — a selector list applies its rule to each selector independently, and each can have a different specificity.
Privacy
Parsing and scoring happen entirely in your browser. Selectors you paste in are never uploaded or stored.