CSS Specificity Calculator

A CSS specificity calculator tells you exactly why one rule beats another: paste selectors one per line and it breaks each score into IDs, classes, and elements, then ranks them so you can see which rule the browser will actually apply. Specificity is the tiebreaker the cascade uses when two rules target the same element. This guide explains the counting algorithm, the modern pseudo-classes that change the math, and how cascade layers fit in.

The (a, b, c) tuple, explained

Browsers score every selector as three counters — (a, b, c) — compared left to right like digits, never added together:

  • a — ID selectors. Each #id adds 1. This is the highest column.
  • b — class selectors, attribute selectors, and pseudo-classes. Each .card, [type="text"], or :hover adds 1.
  • c — type selectors and pseudo-elements. Each div or ::before adds 1.

Combinators (>, +, ~, and the descendant space) and the universal selector * add nothing. Comparison is lexicographic: (1,0,0) beats (0,12,5) because the first column decides before the rest are even read. Worked example — nav#main ul.menu li a:hover:

PartKindColumn
#mainIDa = 1
.menu, :hoverclass, pseudo-classb = 2
nav, ul, li, atype selectorsc = 4

Result: (1, 2, 4). This tool shows the same breakdown as three columns — IDs, Classes, Elements — in its results table.

Why the 100 / 10 / 1 score is misleading

You will often see specificity taught as "ID = 100, class = 10, element = 1". It is a handy mnemonic that breaks in real stylesheets: eleven classes score 110, which looks bigger than one ID's 100 — but (1,0,0) always beats (0,11,0), because columns never carry over into each other. Treat the score as three separate counters and you will never be fooled by the addition.

Modern selectors: :is(), :where(), :not(), :has()

These functional pseudo-classes follow special rules:

  • :where() — always zero specificity, no matter what is inside. :where(#sidebar .menu) scores (0,0,0).
  • :is() and :not() — take the specificity of the most specific selector in their argument list. :is(#sidebar, .menu) scores (1,0,0); :not(.a, .b) scores (0,1,0).

This calculator applies those rules for simple, single-level cases. Deeply nested combinations, selector lists inside those functions, and :has() may not be scored with full accuracy — the tool is a text-based approximation, not a full CSS parser (see the limitations below). This makes :where() the deliberate tool for defaults that should always be easy to override — a reset written with :where() never fights your components — while :is() is convenient shorthand that keeps the full weight of whatever it matches.

Specificity is not the whole cascade

Specificity only breaks ties between declarations that are otherwise equal. When a style refuses to apply, check these first:

  1. !important beats any normal declaration, regardless of specificity.
  2. Normal declarations outside any @layer beat normal declarations inside a layer — even (1,0,0) loses to (0,0,1) here.
  3. An inline style="" attribute beats all selector-based normal declarations.
  4. Only then does the (a,b,c) tuple decide; if still tied, the later rule in source order wins.

How to use it

  1. Paste the selectors you want to compare, one per line.
  2. Results update as you type: a table of Rank, Selector, IDs, Classes, and Elements, sorted from highest to lowest specificity.
  3. The column breakdown shows exactly why one selector outranks another — the first column where they differ decides.

How it works

Each selector is scanned for IDs (#id), classes/attributes/pseudo-classes (.class, [attr], :hover), and elements/pseudo-elements (div, ::before). The three counts are compared left to right — any ID beats any number of classes, and any class beats any number of elements.

Limitations

This is a text-based approximation, not a full CSS parser. Deeply nested :not(), :is(), or :has() arguments, and selector lists inside those functions, may not be scored with full accuracy. For everyday selectors it matches the CSS specification's behavior.

Quick debugging checklist

When a style doesn't apply: first look for !important or an inline style on the element, then check whether the rules sit in different cascade layers, then compare the (a,b,c) tuples. In DevTools, crossed-out declarations show you exactly which rule won and why — specificity surprises are almost always one of these four causes.

Frequently asked questions

How is CSS specificity calculated?
As three counts: the number of IDs, the number of classes/attribute selectors/pseudo-classes, and the number of elements/pseudo-elements. A selector with more IDs always wins, regardless of how many classes or elements the other selector has.
Does inline style count here?
This tool only compares selectors you paste in, so it doesn't include inline style="" attributes, which outrank every selector-based rule except !important.
What about !important?
!important overrides normal specificity entirely and isn't part of the specificity calculation itself — it's a separate mechanism this tool doesn't model.
Does it handle :not(), :is(), and :where() correctly?
For simple, single-level cases it does — :where() contributes zero, and :not()/:is() take on the specificity of their argument. Deeply nested combinations may not be fully accurate; see the limitations below.
Is one ID always stronger than any number of classes?
Yes. The columns never carry over, so (1,0,0) beats (0,50,0) every time. This is exactly where the "ID = 100, class = 10" addition misleads. If an ID is winning unexpectedly, lower its weight with :where() or restructure the selector — stacking more classes against it cannot work.
Why won't my class override a style?
Walk the cascade in order: an !important declaration, an inline style attribute, or an unlayered-versus-layered mismatch all beat specificity silently. Then compare the tuples — the winning selector may carry an ID or extra classes you missed. DevTools shows crossed-out losing declarations and the rule that beat them.
Do cascade layers make specificity irrelevant?
Not irrelevant, just demoted: layers are compared before specificity. Normal unlayered styles beat normal layered styles at any specificity; among layered styles, later layers win first, and specificity only decides ties within the same layer. A practical setup: third-party CSS in early layers, your app CSS unlayered or in a later layer.