CSS Specificity Calculator
Every CSS rule you write carries a specificity weight - a score the browser uses to decide which style wins when two rules target the same element. That score is expressed as three numbers: (ID, Class, Element). A higher number in an earlier column always wins, regardless of how large the later columns are. So (1,0,0) beats (0,99,99) every time.
Paste any selector below to instantly see its breakdown. Each part of the selector - IDs, classes, attributes, pseudo-classes, element types - is colour-coded and annotated so you can see exactly what's contributing to the total. Use the compare panel to rank multiple selectors against each other and find out which one the browser will give priority to.
CSS Specificity Calculator
!important with higher specificity).
Token Breakdown
Compare Selectors
How specificity scoring works
The browser scores every selector using three independent counters, written as (A, B, C).
- A counts ID selectors (
#id). - B counts class selectors (
.class), attribute selectors ([type="text"]), and pseudo-classes (:hover,:focus, etc.). - C counts element type selectors (
div,a) and pseudo-elements (::before,::after).
Comparison always starts from the left. A selector with (1,0,0) beats anything with (0,x,x) regardless of x. If A is equal, move to B; if B is also equal, compare C. Only when all three match does source order in the stylesheet decide the winner.
| Selector type | Contributes to | Example |
|---|---|---|
| #id | A (+1,0,0) | #header |
| .class | B (+0,1,0) | .nav-link |
| [attr] | B (+0,1,0) | [type="submit"] |
| :pseudo-class | B (+0,1,0) | :hover, :nth-child(2) |
| element | C (+0,0,1) | div, a |
| ::pseudo-element | C (+0,0,1) | ::before, ::placeholder |
| * (universal) | Nothing (0,0,0) | * |
| Combinators | Nothing (0,0,0) | >, +, ~, space |
Common gotchas and edge cases
Several modern pseudo-classes have specificity behaviour that surprises even experienced developers:
:is(#id, .class) scores (1,0,0), not (0,1,0).:not(#id) contributes (1,0,0).!important rules conflict, specificity is used as a tiebreaker between them.!important.S adds its own specificity in newer browsers. This tool scores the pseudo-class itself at (0,1,0).How this tool parses selectors
The parser is a hand-written tokeniser that walks the selector character by character, recognising IDs, classes, attribute selectors, element types, pseudo-classes, and pseudo-elements. It tracks parenthesis depth to correctly handle nested function selectors like :is(), :not(), :has(), and :where().
For :is(), :not(), and :has(), the parser recursively evaluates each comma-separated argument inside the parentheses and adopts the highest scoring one — matching the behaviour specified in CSS Selectors Level 4. :where() is special-cased to always return (0,0,0). Combinators (>, +, ~) and the universal selector (*) are explicitly ignored. Comma-separated selector lists are evaluated as separate selectors and the highest-specificity branch is used.
Tips for writing better selectors
#id creates (1,0,0), making it nearly impossible to override without another ID or !important. Prefer classes for styling..card .title .text scores (0,3,0) and tightly couples your CSS to your HTML structure. .card-text at (0,1,0) is easier to maintain and override.:where() gives them zero specificity, so any author style — no matter how simple — can override them without friction.h1 a, h2 a, h3 a scoring (0,0,2), write :is(h1, h2, h3) a — same specificity, much less repetition.!important with higher specificity — a spiral that's hard to unwind. Fix the root specificity problem instead.@layer rule (modern browsers) lets you control which layer wins before specificity is even compared — a powerful alternative to fighting specificity directly.