ModernCalcs

CSS Specificity Calculator

Calculate the (IDs, classes, elements) specificity of any CSS selector, including :not(), :is(), and :where().

.card > #header .title::before(1, 2, 1)
nav a:hover(0, 1, 2)

Format: (ID selectors, class/attribute/pseudo-class selectors, element/pseudo-element selectors) — compared left to right, highest wins.

(1, 1, 1)
(0, 2, 1)
Selector A wins (higher specificity).

CSS Specificity Calculator: Know Exactly Which Rule Wins

'Why isn't my CSS applying?' is very often a specificity problem, not a syntax one — a more specific selector elsewhere in the stylesheet is winning the cascade. This calculator computes the exact (ID, class, element) specificity score for any selector, including the trickier cases like :not(), :is(), and :where(), and lets you compare two selectors head-to-head.

Formula
Specificity = (ID count, class/attribute/pseudo-class count, element/pseudo-element count)

Compared left to right as a three-part tuple — not added into a single number.

Why Specificity Isn't a Single Number

It's tempting to think of specificity as 'IDs worth 100 points, classes worth 10, elements worth 1' and sum them, but that's not quite how it works — the three counts are compared independently, left to right, like a version number. A selector with 1 ID always beats a selector with 100 classes, because the ID count is compared first and any difference there decides the outcome immediately.

:not(), :is(), and :has() Don't Add Their Own Specificity

These functional pseudo-classes are specificity-neutral wrappers — they take on the specificity of whichever argument inside them is most specific, rather than contributing a fixed value themselves. :not(.a, #b, div) evaluates the specificity of .a, #b, and div, and uses whichever is highest (#b) as its contribution.

:where() Is the Zero-Specificity Escape Hatch

Unlike :is()/:not(), :where() was specifically designed to always contribute zero specificity no matter what's inside it. This makes it valuable for base/reset styles you want to be trivially overridable — a single class selector anywhere else will always beat a :where()-wrapped rule, by design.

Practical Examples

Why an ID Beats Many Classes

The classic specificity surprise.

  • 1.#main .content p → (1, 1, 1)
  • 2..content p.intro → (0, 2, 1)
  • 3.First selector wins: 1 ID beats 2 classes

Comparing :not() Specificity

The pseudo-class takes its argument's specificity.

  • 1.:not(.a, #b) → contributes (1, 0, 0)
  • 2.Same as if you'd written #b directly

What Counts Toward Each Part

  • IDs: #header
  • Classes, attributes, pseudo-classes: .card, [type], :hover, :nth-child()
  • Elements, pseudo-elements: div, ::before
  • Zero specificity: *, >, +, ~, :where()

Good Use Cases

  • Debugging why a more 'obvious' style isn't applying
  • Comparing two competing selectors before refactoring CSS
  • Understanding :not()/:is()/:where() specificity rules
  • Writing intentionally low-specificity base styles with :where()

Frequently Asked Questions

How is CSS specificity calculated?

As a three-part value (IDs, classes/attributes/pseudo-classes, elements/pseudo-elements). Count ID selectors (#id) for the first part, class selectors, attribute selectors, and pseudo-classes (.class, [attr], :hover) for the second, and element/type selectors and pseudo-elements (div, ::before) for the third. Higher values in the leftmost part always win, regardless of how many of the lower parts the other selector has.

What's the specificity of :not(), :is(), and :has()?

These take on the specificity of their most specific argument — :not(.a, #b) contributes the specificity of #b (the more specific of the two), not both combined and not a fixed value of its own.

Why does :where() always contribute zero specificity?

It's specifically designed as a zero-specificity version of :is() — useful for writing default/reset styles that are trivially easy to override, since anything with any specificity at all will beat a :where()-wrapped selector.

Does inline style or !important show up in this calculator?

No — inline styles (style="...") and !important both override normal specificity comparison entirely, sitting outside the specificity system this calculator computes for selectors in a stylesheet.

What happens when two selectors have equal specificity?

The one that appears later in the CSS (or later in a later-loaded stylesheet) wins — this is the 'cascade' part of 'Cascading Style Sheets,' and it's exactly why property order matters when specificity is tied.

Why does a universal selector (*) contribute nothing?

The universal selector, and combinators like >, +, ~, and the descendant space, don't add to specificity at all — they affect which elements match, not how strongly the rule should apply relative to other matching rules.