CSS Specificity Calculator

Paste one or more selectors to see their specificity as (a, b, c) and exactly which parts count towards each number. The calculator follows Selectors Level 4, so :is(), :not() and :has() take their most specific argument, :where() counts as zero, and :nth-child(… of S) adds its selector list. Sort selectors from strongest to weakest to understand which rule wins, and export the results as text, CSV or JSON.

Preset gallery
SpecificitySelectorWhat counts
1,2,1#nav .item a:hover#nav 1,0,0 .item 0,1,0 a 0,0,1 :hover 0,1,0
1,0,1:is(#main, .content) p:is(#main, .content) 1,0,0 p 0,0,1
1,0,1button:not(.ghost, #cancel)button 0,0,1 :not(.ghost, #cancel) 1,0,0
0,2,0.btn.btn-primary.btn 0,1,0 .btn-primary 0,1,0
0,2,0[data-state="open"] > .panel[data-state="open"] 0,1,0 .panel 0,1,0
0,0,3ul li::beforeul 0,0,1 li 0,0,1 ::before 0,0,1
0,0,1:where(.card) h2h2 0,0,1

Accessibility checks

  • Check 3 selector(s) use IDs. IDs outweigh any number of classes, which makes overrides hard.
  • Pass No overly long selectors.
  • Pass Tip: wrap parts in :where() to give them zero specificity, ideal for resets and library defaults.

Export code

(1,2,1)  #nav .item a:hover
(1,0,1)  :is(#main, .content) p
(1,0,1)  button:not(.ghost, #cancel)
(0,2,0)  .btn.btn-primary
(0,2,0)  [data-state="open"] > .panel
(0,0,3)  ul li::before
(0,0,1)  :where(.card) h2
Favourites on this device (0)

No favourites yet.

Selectors

How to use the specificity calculator

  1. Paste selectors: one per line, or a comma-separated list (each selector is calculated separately).
  2. Turn sorting on to rank them from strongest to weakest.
  3. Read the breakdown: each counting part is shown with its own contribution.
  4. Export text, CSV or JSON.

Reading the result

(1,2,1) for #nav .item a:hover means one ID, two class-level parts (.item and :hover) and one element (a). Compare from the left: (0,10,0) still loses to (1,0,0).

How it works

Specificity is counted as three numbers (a, b, c). a counts ID selectors; b counts classes, attribute selectors and pseudo-classes; c counts type selectors and pseudo-elements. The universal selector and combinators count as zero. :is(), :not() and :has() take the specificity of their most specific argument, :where() always counts as zero, and :nth-child(… of S) adds the specificity of S. Two selectors are compared a first, then b, then c.

Worked examples

Example 1: why won’t my style apply?

Load Overrides. .btn is (0,1,0), body .btn is (0,1,1), and #app .btn is (1,1,0). If a library uses #app .btn, your .btn rule can’t win without an ID or a hack. Doubling a class (.btn.btn) raises specificity without IDs.

Example 2: low-specificity defaults

Load :is() vs :where(). :where(#hero, .banner) .title counts only .title (0,1,0), so any normal class can override it. That makes :where() perfect for resets and component libraries.

Example 3: framework selectors

Load Framework. Navigation selectors from popular frameworks often reach (0,4,0) or more, which explains why overriding them takes long selectors. Cascade layers are a cleaner solution.

Keeping specificity low

  • Prefer single classes (BEM, utilities) over long chains.
  • Avoid IDs in stylesheets; use them for links and JavaScript.
  • Use :where() for defaults and @layer to order groups of styles.
  • Reserve !important for utilities that must always win.

Common mistakes

  • Counting combinators. >, +, ~ and spaces add nothing.
  • Treating :not() as zero. It counts its most specific argument.
  • Forgetting pseudo-elements. ::before counts like an element.

Debugging a cascade problem

When a style doesn’t apply, open your browser’s developer tools and inspect the element. Crossed-out declarations show which rule lost. Paste both selectors here to compare them, and check whether a cascade layer, an !important or an inline style is involved before raising specificity yourself. Often the cleanest fix is lowering the other selector’s specificity, for example by wrapping it in :where() or moving it into an earlier cascade layer, rather than escalating your own.

Frequently asked questions

How is specificity calculated?

Each selector gets three numbers: a counts ID selectors, b counts classes, attribute selectors and pseudo-classes, and c counts type selectors and pseudo-elements. They are compared left to right: one ID beats any number of classes, and one class beats any number of elements. The universal selector and combinators count as zero.

What's the specificity of :is() and :where()?

:is(), :not() and :has() take the specificity of their most specific argument, so :is(#main, .content) counts as an ID. :where() always counts zero, which makes it ideal for defaults that should be easy to override.

Do inline styles and !important have specificity?

Inline style attributes beat any selector, and !important declarations win over normal ones in the cascade. Neither is a selector, so they aren't calculated here, but they're why rules sometimes win against higher specificity.

What if two selectors have the same specificity?

The one that comes later in the stylesheet wins. Cascade layers (@layer) can also change the outcome before specificity is compared.

How do I lower specificity instead of adding !important?

Wrap selectors in :where(), which has zero specificity, use cascade layers (@layer), or simplify selectors so you don't need to win with IDs or !important.

Sources