CSS specificity calculator — see why one selector beats another

Paste one or more selectors and the calculator scores each one as IDs, classes and elements, shows which part adds what, and names the most specific selector in the list.

Live demo · real result from this tool
Highest#nav .menu > li a:hover → (1, 2, 2)

Table

SelectorSpecificity (a,b,c)How it adds up
#nav .menu > li a:hover1,2,2#nav (1,0,0) + .menu (0,1,0) + li (0,0,1) + a (0,0,1) + :hover (0,1,0)
.btn.btn-primary:not(.disabled)0,3,0.btn (0,1,0) + .btn-primary (0,1,0) + :not(.disabled) (0,1,0)
ul li::before0,0,3ul (0,0,1) + li (0,0,1) + ::before (0,0,1)
:where(.card) h20,0,1:where(.card) (0,0,0) + h2 (0,0,1)
:is(#main, .content) p1,0,1:is(#main, .content) (1,0,0) + p (0,0,1)

a = IDs, b = classes, attributes and pseudo-classes, c = elements and pseudo-elements. :is(), :not() and :has() count their most specific argument; :where() counts zero; * and combinators count nothing. Inline styles and !important sit above all selectors.

Processed on your device — nothing is uploaded.

How to use the CSS specificity calculator

  1. Paste selectors into Selectors, one per line. A line with commas is split into separate selectors.
  2. Read the Highest line for the strongest selector in your list.
  3. Use the table: Specificity (a,b,c) gives the score and How it adds up lists each part with its own score.
  4. Fix any row marked invalid; its message says what went wrong and where.

What it does and when to use it

When two rules set the same property on the same element, the browser uses the one with higher specificity, and only falls back to source order when the scores tie. Most “why is my CSS not applying?” moments come down to this. Paste the selector you wrote and the one that is winning, and the table shows exactly which part tips the balance.

It is also useful when planning a stylesheet: you can check that utility classes score low enough to be overridden, or that :where() really removes the weight of a reset. To tidy the stylesheet afterwards, run it through the CSS minifier, and keep shared values in custom properties with the CSS variables generator so fewer overrides are needed. For a full component, the CSS button generator writes low, single-class selectors.

How it works

The calculator reads each selector from left to right. #id adds 1 to a; .class, [attribute] and pseudo-classes such as :hover add 1 to b; element names and pseudo-elements such as ::before add 1 to c. The old single-colon forms :before and :after count as pseudo-elements. Combinators (>, +, ~, space) and * add nothing. :nth-child(2 of .item) counts as one pseudo-class plus its of selector. Results are sorted to find the highest.

MDN lists :is() and :where() as Baseline widely available since January 2021 and :has() since December 2023.

Worked examples

The sample list. The default selectors give Highest: #nav .menu > li a:hover → (1, 2, 2). In the same list, :where(.card) h2 scores only 0,0,1.

Long chains lose to one class. a.nav-link[aria-current="page"] scores 0,2,1, while header nav ul li a scores 0,0,5. The short selector wins despite having fewer parts.

A modern and a broken selector. li:nth-child(2 of .item) scores 0,2,1. The typo .a..b is reported as invalid | Expected a class name after . at position 3.

Limits and tips

  • The nesting selector & counts as zero here, because its real weight depends on the parent rule.
  • Scores do not prove a rule applies. The selector must also match the element, and cascade layers and origin come before specificity.
  • Lower is easier to maintain. If you keep raising specificity to win, consider :where() for base styles instead.

Frequently asked questions

What do the three numbers mean?
a counts ID selectors, b counts classes, attribute selectors and pseudo-classes, and c counts element names and pseudo-elements. They are compared left to right, so one ID beats any number of classes.
Does 0,0,11 beat 0,1,0?
No. The columns never carry over. Eleven element selectors still lose to a single class, because the b column is compared before c.
How are :is(), :not() and :has() counted?
Each takes the specificity of its most specific argument. :is(#main, .content) p scores 1,0,1 because #main is the strongest choice inside. :where() always counts zero, which makes it useful for low-priority defaults.
What about inline styles and !important?
They are outside selector specificity. An inline style attribute beats any selector, and an !important declaration beats normal ones. The calculator only scores selectors.
Why does a comma-separated selector show as several rows?
A selector list such as h1, .title is really separate selectors. Each one is scored on its own, and an element matched by the list gets the score of the selector that matched it.

Sources

  1. Specificity - CSS | MDN — MDN Web Docs, accessed 2026-10-08
  2. :where() - CSS | MDN — MDN Web Docs, accessed 2026-10-08

Written by the ToolsRift team · Last updated

Pages are drafted with AI assistance and checked by automated tests. How we write and check pages.

Was this tool useful?

Report a problem

Start typing to search every tool.

↑ ↓ to moveEnter to openEsc to close