More games at WuGames.ioSponsoredDiscover free browser games — play instantly, no download, no sign-up.Play

CSS Specificity Calculator

Compute CSS specificity for any selector with a color-coded breakdown, multi-selector comparison, and a source-order tiebreaker for specificity ties.

CSS Selectors (one per line)
One selector per line. Comma-separated rules count as one input line.
Specificity Reference
ComponentExamplesWeight
a IDs#header, #main(1, 0, 0)
b Classes / Attributes / Pseudo-classes.btn, [type="text"], :hover, :nth-child(2)(0, 1, 0)
c Types / Pseudo-elementsdiv, p, ::before, ::placeholder(0, 0, 1)
*, :where(...), +, >, ~(0, 0, 0)
:is(A, B), :not(A), :has(A)max of argument

CSS Specificity Calculator — Parse Selectors, Compute (a, b, c)

Paste any CSS selector — simple or compound, with combinators, pseudo-classes, pseudo-elements, or modern :is/:not/:where/:has — and the calculator returns the W3C specificity triple. The breakdown shows which component each token contributes to, and a comparison view ranks multiple selectors so you can see at a glance which rule wins the cascade.

What is CSS specificity in one paragraph?

When two CSS rules try to set the same property on the same element, the browser needs a tiebreaker. The first tiebreaker is *origin and importance* (user-agent < author < user, then !important reverses that). After that, it's *specificity*: each selector gets a weight, expressed as three numbers — (a, b, c) — and the higher tuple wins, compared left-to-right.

The three slots count:

- **a** — the number of ID selectors (`#foo`)
- **b** — the number of class selectors (`.bar`), attribute selectors (`[type=text]`), and pseudo-classes (`:hover`)
- **c** — the number of type selectors (`div`) and pseudo-elements (`::before`)

A selector like `#nav .item a:hover` has one ID, two class/pseudo-class entries, and one type — specificity (1, 2, 1). It beats `.menu .item a:focus` (0, 3, 1) because the first slot wins regardless of how high the later slots are.

How do `:is()`, `:not()`, and `:has()` affect specificity?

All three contribute the *maximum* specificity of their arguments, not zero, not the sum.

Example:

```
:is(.btn, #primary) { color: red; }
```

The specificity is determined by `#primary` (1, 0, 0), not by `.btn` (0, 1, 0). The whole `:is(...)` token contributes (1, 0, 0).

The same rule applies to `:not(...)` and `:has(...)` — they take the heaviest selector inside the parentheses. This is why `:not(#admin)` is a heavy selector despite looking innocent; it's specificity (1, 0, 0) all on its own.

This behavior is defined in CSS Selectors Level 4. Older `:not()` (Level 3, single-simple-selector form) followed the same rule, so most browsers have always agreed.

What about `:where(...)`?

`:where(...)` is the escape hatch: it contributes **zero specificity** no matter what's inside.

```
:where(#header, .nav, h1) { color: blue; }
```

Specificity = (0, 0, 0). This is intentional. `:where()` lets you write a base style that grouped selectors share without locking out future overrides — anything that targets the same elements with even one class will win.

A common pattern is wrapping reset or default styles in `:where(...)` so they have effectively no specificity weight, while leaving authored component styles free to override them with normal selectors. This was added in CSS Selectors Level 4 (2018) and shipped in all evergreen browsers by 2021.

Why do I sometimes see `(a, b, c, d)` with four slots?

Older articles add a fourth slot at the start for **inline style** (the `style="..."` attribute), giving (1, 0, 0, 0) for any inline declaration. The W3C dropped this from the formal specificity tuple because inline style is a separate origin layer — it wins all author-level cascade decisions before specificity even gets compared.

In practice:

1. `!important` declarations win (origin layer).
2. Inline style beats any selector without `!important`.
3. Selectors are then ranked by (a, b, c) — IDs, classes/attrs/pseudo-classes, types/pseudo-elements.
4. If specificity ties, the rule declared last in source order wins.

This calculator follows the modern three-slot convention. If you're comparing against an older tutorial, mentally pad with a leading 0 — `(0, 1, 2, 1)` and `(1, 2, 1)` describe the same selector.

Two of my rules tie on specificity — which one actually wins?

This is the single most common real-world CSS bug: two selectors hit the same element with the *identical* specificity tuple, e.g. both `.card .title` and `.panel .heading` at (0, 2, 0). Specificity alone can't break the tie, so the browser falls back to the next step in the cascade.

The resolution order, once specificity is equal, is:

1. **Cascade layers** (`@layer`) — a declaration in a later-declared layer wins over an earlier layer, and unlayered styles beat layered ones. This overrides specificity entirely within author origin.
2. **Source order** — if both rules are in the same layer (or no layers are used), the rule declared *last* wins. Later wins.

That's why this calculator's comparison table now includes a **Cascade Winner** column: paste your tied selectors in the order they appear in your stylesheet, and the tool flags the equal-specificity group and marks the source-order winner (the last line). So if `.card .title` is on line 12 and `.panel .heading` is on line 30, line 30 wins — the tool says so explicitly instead of leaving you to remember the rule.

Note the tool models the *source-order* tiebreak only; it does not parse `@layer` or `!important`, which sit above source order in the cascade. If your rules are in different layers, the layer order decides regardless of which line is later.

CSS Specificity Calculator — Compute CSS specificity for any selector with a color-coded breakdown, multi-selector comparison, and a source-order tie
CSS Specificity Calculator

How do I beat an inline `style="..."` declaration?

An inline style attribute outranks *any* selector-based rule that doesn't use `!important`, no matter how high its specificity climbs. `style="color:red"` beats even `#a#b#c.d.e div { color: blue; }` because inline style sits in a higher slot of the cascade than ordinary selector specificity.

Your options, in order of preference:

1. **Remove the inline style** — the clean fix. Inline styles are usually injected by a script or a legacy template; move the value into a stylesheet rule.
2. **Use `!important`** in a stylesheet rule — an `!important` author declaration beats an inline style that is *not* itself `!important`. This is the one legitimate use of `!important` many engineers reach for here.
3. **An inline `!important`** (`style="color:red !important"`) beats a stylesheet `!important` — at which point you're back to editing the markup.

Specificity never enters this fight: you cannot out-specify an inline style with selectors alone. This calculator scores selectors, so inline styles are out of scope by design — but knowing they win this tier is essential when debugging why a rule won't apply.

Does the universal selector `*` or combinators add specificity?

No. None of the following contribute to specificity:

- `*` (universal selector)
- ` ` (descendant combinator, written as whitespace)
- `>` (child combinator)
- `+` (adjacent sibling combinator)
- `~` (general sibling combinator)
- `||` (column combinator, for tables — Level 4)

A selector like `* > * + *` has specificity (0, 0, 0) — three universals and two combinators, all weightless. Combinators describe *relationships*, not what's being matched, so they don't add weight.

This is why `body > main p` (0, 0, 3) ties with `h1 ~ h2 ~ p` (0, 0, 3) — same three type selectors, different combinators, same specificity. The cascade then decides by source order.

What's the difference between pseudo-classes and pseudo-elements?

**Pseudo-classes** describe a state of an element:

- `:hover`, `:focus`, `:active`, `:checked`, `:disabled`
- `:nth-child(2)`, `:nth-of-type(odd)`, `:first-child`
- `:lang(en)`, `:dir(rtl)`, `:state(custom)`

They contribute to slot **b** — same weight as a class selector.

**Pseudo-elements** describe a virtual sub-part of an element:

- `::before`, `::after`, `::first-line`, `::first-letter`
- `::placeholder`, `::marker`, `::backdrop`, `::selection`

They contribute to slot **c** — same weight as a type selector.

The syntactic difference is a single colon (`:hover`) vs double colon (`::before`). CSS2 used a single colon for the four oldest pseudo-elements (`:before`, `:after`, `:first-line`, `:first-letter`) — this calculator accepts both forms and classifies those four as pseudo-elements either way.

How do I lower specificity when I'm stuck in a war with another stylesheet?

Several techniques in increasing severity:

1. **Use `:where()`** — wrap the heavy parts of your selector in `:where()` so they contribute nothing. `:where(.legacy) .button` → (0, 1, 0) instead of (0, 2, 0). Best for reset stylesheets.
2. **Drop IDs from selectors** — refactor `#nav .item` to `.nav .item` if the markup allows. IDs are the single heaviest contributor.
3. **Use a single class** — flat selectors like `.btn-primary` win because they tie at (0, 1, 0) against most authored styles, leaving source order to decide.
4. **Add a class duplicate** — `.btn.btn` (0, 2, 0) is a known hack to bump specificity up *without* needing IDs.
5. **Use cascade layers** — `@layer base, components, utilities;` lets you control which file wins regardless of specificity. The later-declared layer wins, full stop.
6. **Use `!important`** — last resort. It works but creates a brittle chain of override wars.

The modern playbook is: minimize specificity in component CSS (single classes), wrap legacy/reset rules in `:where()`, and use cascade layers for tooling/utility split.

Is this calculator private and offline?

Yes. Everything is computed locally in the browser by a few hundred lines of JavaScript:

- The selector parser is hand-written, no external CSS engine called.
- No telemetry, no analytics for the calculations themselves.
- The Examples button populates the textarea with built-in strings; nothing fetched.
- The page shares the site's normal assets (Bootstrap CSS, icons) but no third-party API is contacted for the actual specificity math.

You can verify by opening DevTools → Network and watching while you type or click Calculate — no request fires. This also means the calculator works offline once the page has loaded, which is handy if you're auditing a stylesheet on a flight or in a low-signal environment.

Key Features

  • W3C-correct (a, b, c) tuple for any CSS selector
  • Color-coded breakdown highlighting which token contributes to each slot
  • Multi-selector comparison view, sorted by specificity descending
  • Correct handling of :is(), :not(), :has() — max specificity of argument
  • :where(...) recognized as zero specificity
  • Pseudo-elements (::before, ::after, ::first-line) classified separately from pseudo-classes
  • Legacy CSS2 single-colon pseudo-elements (:before, :after) accepted
  • Universal selector * and combinators (>, +, ~, space) correctly weighted as zero
  • Attribute selectors, including [attr=value] and [attr^=value]
  • Functional pseudo-classes :nth-child(), :nth-of-type(), :lang(), :dir()
  • Examples preset loads 10 representative selectors for instant comparison
  • Tie detection — highlights selectors with equal specificity
  • Cascade tie resolver — names the source-order winner when specificity ties
  • Pure JavaScript, no external CSS engine
  • Works offline after first load
  • 100% client-side — your selectors stay in your browser