CSS Portal

CSS Specificity Calculator

Every CSS rule you write carries a specificity weight - a score the browser uses to decide which style wins when two rules target the same element. That score is expressed as three numbers: (ID, Class, Element). A higher number in an earlier column always wins, regardless of how large the later columns are. So (1,0,0) beats (0,99,99) every time.

Paste any selector below to instantly see its breakdown. Each part of the selector - IDs, classes, attributes, pseudo-classes, element types - is colour-coded and annotated so you can see exactly what's contributing to the total. Use the compare panel to rank multiple selectors against each other and find out which one the browser will give priority to.

If this site has been useful, we’d love your support! Consider buying us a coffee to keep things going strong!
CSS Specificity Calculator
ID
0
#id
Class
0
.class / [attr] / :pseudo-class
Element
0
tag / ::pseudo-element
Specificity 0,0,0
!important detected — this overrides specificity entirely and wins against any other declaration (except another !important with higher specificity).

Token Breakdown

← Enter a selector above to analyze it

Compare Selectors

Link copied to clipboard
If this site has been useful, we’d love your support! Consider buying us a coffee to keep things going strong!

How specificity scoring works

The browser scores every selector using three independent counters, written as (A, B, C).

  • A counts ID selectors (#id).
  • B counts class selectors (.class), attribute selectors ([type="text"]), and pseudo-classes (:hover, :focus, etc.).
  • C counts element type selectors (div, a) and pseudo-elements (::before, ::after).

Comparison always starts from the left. A selector with (1,0,0) beats anything with (0,x,x) regardless of x. If A is equal, move to B; if B is also equal, compare C. Only when all three match does source order in the stylesheet decide the winner.

Selector typeContributes toExample
#idA (+1,0,0)#header
.classB (+0,1,0).nav-link
[attr]B (+0,1,0)[type="submit"]
:pseudo-classB (+0,1,0):hover, :nth-child(2)
elementC (+0,0,1)div, a
::pseudo-elementC (+0,0,1)::before, ::placeholder
* (universal)Nothing (0,0,0)*
CombinatorsNothing (0,0,0)>, +, ~, space

Common gotchas and edge cases

Several modern pseudo-classes have specificity behaviour that surprises even experienced developers:

:where() Always contributes (0,0,0) regardless of what's inside. Useful for writing low-specificity base styles that are easy to override.
:is() and :has() Take the specificity of their most specific argument. So :is(#id, .class) scores (1,0,0), not (0,1,0).
:not() Same rule as :is() — it adopts the specificity of its most specific argument. :not(#id) contributes (1,0,0).
!important Sits outside the specificity system entirely. It wins against any normal declaration, and when two !important rules conflict, specificity is used as a tiebreaker between them.
Inline styles Are treated as a fourth column — effectively (1,0,0,0) — and beat all selector-based specificity, except !important.
:nth-child(An+B of S) The selector argument S adds its own specificity in newer browsers. This tool scores the pseudo-class itself at (0,1,0).

How this tool parses selectors

The parser is a hand-written tokeniser that walks the selector character by character, recognising IDs, classes, attribute selectors, element types, pseudo-classes, and pseudo-elements. It tracks parenthesis depth to correctly handle nested function selectors like :is(), :not(), :has(), and :where().

For :is(), :not(), and :has(), the parser recursively evaluates each comma-separated argument inside the parentheses and adopts the highest scoring one — matching the behaviour specified in CSS Selectors Level 4. :where() is special-cased to always return (0,0,0). Combinators (>, +, ~) and the universal selector (*) are explicitly ignored. Comma-separated selector lists are evaluated as separate selectors and the highest-specificity branch is used.

Tips for writing better selectors

Avoid ID selectors in CSS A single #id creates (1,0,0), making it nearly impossible to override without another ID or !important. Prefer classes for styling.
Keep selectors flat .card .title .text scores (0,3,0) and tightly couples your CSS to your HTML structure. .card-text at (0,1,0) is easier to maintain and override.
Use :where() for resets Wrapping base styles in :where() gives them zero specificity, so any author style — no matter how simple — can override them without friction.
Use :is() to simplify lists Instead of h1 a, h2 a, h3 a scoring (0,0,2), write :is(h1, h2, h3) a — same specificity, much less repetition.
Treat !important as a last resort Once you use it, the only escape is another !important with higher specificity — a spiral that's hard to unwind. Fix the root specificity problem instead.
Organise by specificity layers The CSS cascade @layer rule (modern browsers) lets you control which layer wins before specificity is even compared — a powerful alternative to fighting specificity directly.
If this site has been useful, we’d love your support! Consider buying us a coffee to keep things going strong!