About CSS Specificity Calculator
Specificity is three counts, written here as 1-2-1: the number of IDs, then the number of classes, attribute selectors and pseudo-classes, then the number of element names and pseudo-elements. The browser compares the counts from the left and stops at the first one that differs, so a single ID outranks any number of classes. This calculator works those counts out for every selector you enter, sorts the list and names the winner, or tells you when the top selectors tie and source order decides instead. It scores selectors only: inline styles, !important and cascade layers sit outside the three counts and are covered in the notes below.
CSS specificity chart for common selectors
Every value below is the output of the calculator above for that exact selector.
| Selector | Specificity | What is counted |
|---|---|---|
| * | 0-0-0 | The universal selector counts for nothing |
| li | 0-0-1 | One element |
| ul li | 0-0-2 | Two elements |
| ul > li | 0-0-2 | Two elements; the child combinator adds nothing |
| li::before | 0-0-2 | One element and one pseudo-element |
| h1 + p::first-line | 0-0-3 | Two elements and one pseudo-element |
| .btn | 0-1-0 | One class |
| :root | 0-1-0 | One pseudo-class |
| a:hover | 0-1-1 | One pseudo-class and one element |
| input[type="text"] | 0-1-1 | One attribute selector and one element |
| li:nth-child(2n+1) | 0-1-1 | One pseudo-class and one element |
| .nav > li + li | 0-1-2 | One class and two elements |
| .btn.btn-primary | 0-2-0 | Two classes |
| #header | 1-0-0 | One ID |
| #header .nav a | 1-1-1 | One ID, one class, one element |
| ul#nav li.active a | 1-1-3 | One ID, one class, three elements |
| body #content .data img:hover | 1-2-2 | One ID, a class and a pseudo-class, two elements |
| #a #b | 2-0-0 | Two IDs |
Worked comparisons: which selector wins
The sample list the page loads with
#sidebar .widget a / .nav .nav__item a:hover / header nav ul li a / a.button / :where(.reset) a
Ranked 1-1-1, 0-3-1, 0-1-1, 0-0-5, 0-0-1. #sidebar .widget a wins outright.
Two classes and a pseudo-class lose to one ID, and five element names lose to one class.
One ID against eleven classes
#header / .a.b.c.d.e.f.g.h.i.j.k
#header is 1-0-0 and the class chain is 0-11-0. #header is ranked first.
This is why the value is not read as the number 100: the middle count can exceed 9 without spilling into the ID count.
Two component selectors that tie
.card .title / .sidebar .title
Both 0-2-0. Result reports a tie and that the rule declared last in the stylesheet wins.
Tied rows stay in the order you entered them.
A negation that carries an ID
:not(#main) / :not(.active)
:not(#main) is 1-0-0 and :not(.active) is 0-1-0.
The :not() itself adds nothing; its argument sets the score.
:is() with a mixed list
:is(#main, .card) p
1-0-1
The whole :is() takes the score of #main, its most specific argument, even for elements that only match .card.
Specificity of :is(), :not(), :has() and :where()
The four pseudo-classes that take a selector list do not count as a pseudo-class themselves; what matters is the list inside the brackets. An ordinary functional pseudo-class such as :nth-child() is shown last for contrast.
| Pseudo-class | Rule | Example | Calculator result |
|---|---|---|---|
| :is() | Takes its most specific argument | :is(#main, .card) p | 1-0-1 |
| :not() | Takes its most specific argument | :not(.active) | 0-1-0 |
| :has() | Takes its most specific argument | .card:has(> img) | 0-1-1 |
| :where() | Always zero | :where(.reset) a | 0-0-1 |
| :nth-child() | Counts as one pseudo-class | li:nth-child(2n+1) | 0-1-1 |
Wrapping base and reset styles in :where() keeps them at zero, so any single class can override them later.
What ranks above selector specificity
Specificity is only one step of the cascade, and it is not the first. Before the browser compares selectors it sorts declarations by origin and importance: !important declarations beat normal ones regardless of selector. Next comes the style attribute. A normal declaration written inline as style="..." beats every selector in a stylesheet, including one with several IDs, and only !important can override it from a stylesheet.
Cascade layers come next. When rules are placed in @layer blocks, a normal declaration in a later layer beats one in an earlier layer whatever their selectors score, and normal styles outside any layer beat all layered normal styles. Only when two declarations survive all of that in the same tier does specificity decide, and only when specificity ties does source order decide.
So if this calculator says your selector wins and the style still does not apply, look for an inline style, an !important on the other rule, or a layer ordering before you make the selector heavier.
Input this calculator does not handle
The tool counts with pattern matching and does not parse CSS fully. These cases give a wrong or meaningless score.
- Comma-separated lists. A line such as .a, #b is scored as one selector (1-1-0). Put each selector on its own line.
- Nested functional pseudo-classes. div:not(:has(.a)) or :is(.a, :not(#x)) are over-counted; only one level of brackets is resolved.
- Attribute values containing a dot or a hash. a[href$=".pdf"] is reported as 0-2-1 when the correct value is 0-1-1, because .pdf is read as a class. Check these by hand.
- Whole rules. Braces, declarations and at-rules such as @media are not stripped, so enter the selector text only.
- Inline styles, !important and @layer. None of these can be entered; the ranking compares selectors alone.
- Whether a selector matches. The tool does not see your HTML, so a winning score only matters if both selectors target the same element.
How to use it
- 1
Type or paste into Selectors, one selector per line. Leave out the braces and declarations, and split a comma-separated list onto separate lines.
- 2
Read the Most specific selector panel: it shows the winning selector, its Specificity value and the three counts behind it.
- 3
Check Result. It says either that the top selector wins outright or that it is tied, in which case the rule declared last in the stylesheet applies.
- 4
Scan the table Ranked from most to least specific to see where each of your other selectors falls and which column separates them.
- 5
Edit a selector and compare again until the rule you want on top is on top without reaching for !important.