Skip to main content

CSS Specificity Calculator for Selectors

Calculate selector specificity and compare two selectors. Inspect how classes, attributes, pseudo-classes, and elements contribute to the result.

CSS Specificity Calculator workspace

Samples:
CSS selector

IDs (a)

0

Classes (b)

3

Elements (c)

0

Breakdown

.card[].title:hover

Selectors Level 4 Spec

Returns specificity as (a, b, c) triple per the modern CSS Selectors Level 4 spec. Correctly handles :is(), :not(), :has() (max of args), and :where() (always 0).

Compare Mode

Paste two selectors and the tool names the winner and the deciding column (a, b, or c). Settles "why is the wrong rule applying" bugs in seconds.

Token Breakdown

Each part of the selector is colored and labeled (ID / class / attribute / pseudo-class / pseudo-element / element / combinator) so you see exactly where the weight comes from.

100% Client-Side

All parsing runs in your browser via custom JavaScript — no fetch, no signup. Works offline once loaded.

CSS Specificity Calculator: Compute Any Selector's (a, b, c) Score

Calculate selector specificity and compare two selectors. Inspect how classes, attributes, pseudo-classes, and elements contribute to the result. Specificity is only one stage of the cascade. Origin, importance, cascade layers, scope, and source order can affect the winning declaration. Use the calculated score to investigate a conflict, then inspect the actual computed styles in the target page.

How to use the CSS specificity calculator

  1. Paste a selector such as main #content .card .button:hover into the input — the score updates as you type.
  2. Read the (a, b, c) result and the colored token breakdown showing where each point comes from (ID, class, attribute, pseudo-class, pseudo-element, element, combinator).
  3. Open Compare mode and paste a second selector to see which of two competing rules wins, and by which column.
  4. Load a sample selector (10 are pre-built, covering :is(), :where(), attribute, and pseudo-element cases) to learn the rules fast.
  5. To lower a score, swap an ID for a class or wrap part of the selector in :where(), then re-check the new (a, b, c).

How specificity decides which rule wins

Browsers compare the three columns left to right, not as one number. A selector with a higher a beats any selector with a lower a, regardless of b and c. Only when a ties do they compare b, then c. If all three tie, the rule that appears later in the source wins. The exact algorithm is defined in the MDN Specificity reference and the W3C Selectors Level 4 specification, which every major browser conforms to.

Combinators (>, +, ~, and the descendant space) add nothing to the score; they only change which elements match. The universal selector * also adds zero. Three functional pseudo-classes are special: :is(), :not(), and :has() take the maximum specificity of their arguments, while :where() always counts as (0, 0, 0) no matter what it wraps.

Worked examples: selector → score

#main .card → (1, 1, 0)

One ID and one class. This beats any rule made only of classes and elements, however long.

header nav ul li a → (0, 0, 5)

Five element names, zero classes. A single class rule .link at (0, 1, 0) outranks it outright.

.row :is(h1, h2, .title) → (0, 2, 0)

The descendant .row adds one class; :is() takes the max of its arguments, and .title (one class) outweighs h1, adding a second class. Swap :is for :where and the wrapper contributes zero, dropping the whole selector to (0, 1, 0) — just .row.

Edge case · :not() inflates more than you expect

p:not(.intro):not(.footer) scores (0, 2, 1), not (0, 0, 1). The :not() pseudo-class itself is free, but it inherits the max specificity of its argument — each .intro and .footer adds one class. Two negations means two classes.

Pair this with the CSS Beautifier to read the stylesheet you are debugging, the CSS Minifier to compress it for production, and the Color Contrast Checker to verify accessibility once the cascade resolves.

CSS Specificity Rules — What Counts & What Does Not

Selector PartWeight AddedNotes
Inline style (style attribute)(1, 0, 0, 0) — beats every selectorPer spec, inline style is a separate dimension above selectors. CSS-in-JS often emits it.
ID selector (#id)adds 1 to "a"IDs are heavy — avoid using them in stylesheets when classes will do.
Class (.cls)adds 1 to "b"Workhorse selector. Stack two classes (.btn.btn) to outweigh a single class without raising ID count.
Attribute ([type="text"])adds 1 to "b"Same weight as a class — useful for form-state styling without classes.
Pseudo-class (:hover, :focus)adds 1 to "b"Includes :is(), :has() which inherit max specificity of arguments.
Element (div, span)adds 1 to "c"Lightest — most selectors involve at least one element name.
Pseudo-element (::before)adds 1 to "c"Same weight as element. Double-colon syntax is modern; single-colon is legacy alias.
* universal selectoradds 0 to allPure noise for specificity — only useful for cascading effects.
Combinators (>, +, ~, space)0Direction/relationship, not weight. Do not affect specificity.
:where(X) — special0Argument specificity is DISCARDED. Use :where to opt out of specificity wars.
:is(X) / :not(X) / :has(X)takes MAX of argument specificityLets you express alternatives without inflating beyond the heaviest alternative.

Five Common Specificity Problems & Fixes

1. Style not applying

Compute specificity of the rule you want vs the rule that won. Whichever has higher (a, b, c) wins. Bump yours by adding a class, NOT an ID.

2. Two rules with same specificity

CSS uses source order: whichever appears LATER in the stylesheet wins. Reorder or use a more specific selector.

3. !important war

Avoid !important except for utility classes (e.g. .hidden { display: none !important }). One !important beats specificity; two !importants compete by specificity.

4. Inline style wins everything

Inline style="..." beats every stylesheet selector except !important. Move to a class-based rule or use :is() to lift cascade weight.

5. Component styles stomping app

Wrap library/component styles in :where(.lib) — specificity contributes 0, so app rules always win.

Four Specificity Best Practices for 2026

1. Keep Specificity Flat

Aim for (0, 1, 0) or (0, 2, 0) for most rules — one or two classes. Component-driven CSS pairs naturally with low-specificity selectors that compose cleanly.

2. Avoid IDs in Stylesheets

ID selectors are heavy and locked to single elements. Use classes for styling, reserve IDs for JS hooks and accessibility relationships (aria-labelledby).

3. Use :where() for Library Styles

Component libraries should wrap their CSS in :where(.lib) so application code can override with a single class. Modern CSS-in-JS libraries embrace this pattern.

4. !important Is a Last Resort

Use !important only for utility classes that MUST win (e.g. .visually-hidden). For everything else, raise the specificity slightly with an additional class — never via ID, never via !important.

The "256 classes beat an ID" myth, settled

You may have read that 256 chained classes (.a.a.a…) overflow into the ID column and beat a single #id. That was a real bug in old IE, Gecko, and WebKit builds, where each column was stored in an 8-bit field that wrapped at 256. It is not the spec, and no current browser does it. The spec uses a number system with a base large enough that the columns never carry — so (0, 256, 0) still loses to (1, 0, 0). Treat each column as a separate tier, never as a decimal score.

One thing to know about this calculator: it shows an internal "score" only to size the comparison bars and sort results — it deliberately weights columns as a×10000 + b×100 + c. That packed number is for the UI alone and would misrank pathological selectors with 100+ classes. The win/lose verdict and the (a, b, c) readout always use true column-by-column comparison, exactly as browsers do.

Last updated: September 15, 2026 · Runs 100% in your browser — no uploads, tool input is not sent to Toolk.

Frequently asked questions

Why does p:not(.intro):not(.footer) score (0, 2, 1) and not (0, 0, 1)?

The :not() pseudo-class itself adds nothing, but per Selectors Level 4 it inherits the maximum specificity of its own argument — and here each argument is a class worth one b point. Two chained negations therefore contribute two classes on top of the element.

Can 256 chained classes ever beat one ID?

No in any current browser. The overflow-at-256 behavior was an 8-bit column bug in old IE, Gecko, and WebKit builds; the spec's number system never carries between columns, so (0, 256, 0) still loses to (1, 0, 0). Compare columns left to right, never as a decimal total.

How do I lower a selector's specificity safely?

Swap IDs for classes, delete redundant ancestors, or wrap the parts you do not need counted in :where() — it always contributes (0, 0, 0) regardless of what it wraps, unlike :is(), :not(), and :has() which take their argument's maximum.

Are my selectors uploaded while calculating?

No. Parsing and scoring are processed locally in your browser, and Toolk's page analytics do not receive the selectors you paste. Tool input is not sent to Toolk, and results match what Chrome and Firefox DevTools report for the same rule.

Which tool helps when two rules tie on specificity?

Use the compare mode here to confirm a genuine tie, then check source order — later rules win ties. To make the competing declarations visible first, run the stylesheet through the CSS Beautifier at /tools/css-beautifier.

Need a different tool?

Browse all 103 browser-based tools (103 currently marked free), or tell us what useful utility we should build next.

Browse all tools