Skip to content
Toolcroft

CSS Specificity Calculator - Understand Selector Weight

Developer Tools

Compare CSS selector specificity using modern (A,B,C) and legacy (a,b,c,d) scores, token breakdowns, Selectors Level 4 pseudos, and cascade demos.

Examples

Legacy specificity (a,b,c,d)

(0,1,2,1)

Selectors Level 4 (A,B,C): (1,2,1)

0
Inline weight (a)
1
IDs (b / A)
2
Classes (c / B)
1
Types (d / C)
aInline-style weight; 0 in selector-only calculations
b = AID selectors (#id)
c = BClasses · attributes · pseudo-classes
d = CType selectors · pseudo-elements

Token contributions

Each chip shows its legacy tuple contribution and the running total after that token.

  • #headerID+ (0,1,0,0)Running: (0,1,0,0)
  • .navClass+ (0,0,1,0)Running: (0,1,1,0)
  • aType+ (0,0,0,1)Running: (0,1,1,1)
  • :hoverPseudo-class+ (0,0,1,0)Running: (0,1,2,1)

Best-effort parser: escaped identifiers, namespace selectors, selector-bearing pseudo-classes such as :nth-child(... of ...), advanced pseudo-elements, and selector-list matching may need a full CSS parser.

Cascade beyond specificity

These are small teaching examples for same-origin declarations, not a full CSS cascade engine.

Teaching example: !important

The declarations target the same element and are in the same origin and layer. Toggle importance to see priority change before specificity is compared.

#target.notice { color: blue; }.notice { color: red !important; }

Winner: .notice (lower specificity, important)

The .notice declaration wins because importance is considered before specificity for these same-origin declarations. Its selector is lower specificity (0,0,1,0) than #target.notice (0,1,1,0).

Teaching example: normal @layer order

Both declarations are normal and target the same element. The later layer wins before specificity is compared; changing the declared layer order changes the winner.

@layer base { #target { color: navy; } }@layer utilities { button { color: teal; } }

Declared order: base, utilities · #target specificity (0,1,0,0) · button specificity (0,0,0,1)

Winner: button in the later utilities layer

Utilities is last, so its lower-specificity button selector wins. Among normal declarations in different layers, the later layer wins before specificity is compared. If both declarations were important, layer priority reverses and the earlier layer wins.

How to use this tool

  1. Enter selector. Type a CSS selector like #header .nav > a:hover into the input.
  2. Read the score. Read the modern (A,B,C) tuple alongside its legacy (a,b,c,d) mapping and inspect token contributions.
  3. Compare selectors. Add multiple selectors (one per line) to the Compare tab to rank them.
  4. Use examples. Click any example to load it and see how it is scored.

How CSS specificity works

CSS specificity helps decide which declaration wins when competing rules target the same element. It is one part of the cascade: origin, importance, cascade layers, and other cascade conditions can be considered before specificity. This calculator scores selectors only, so its inline-style weight stays at zero.

Selectors Level 4 expresses specificity as (A,B,C). This page also shows the legacy four-part form (a,b,c,d), where a is inline-style weight and is 0 for selector-only calculations. The remaining columns map directly: b = A for IDs, c = B for classes, attributes, and pseudo-classes, and d = C for types and pseudo-elements.

  • A / b: ID selectors (#id)
  • B / c: Class selectors (.class), attribute selectors ([attr]), and pseudo-classes (:hover, :nth-child)
  • C / d: Type selectors (div, p) and pseudo-elements (::before, ::after)

Within the relevant cascade origin, importance, and layer, specificity compares lexicographically: a larger value in an earlier column wins before later columns are considered. The universal selector (*) and combinators (>, +, ~) contribute zero.

Modern pseudo-class specificity

  • :where(): Always 0. Useful for base styles you want easily overridden.
  • :is(), :not(), and :has() use the specificity of their most specific argument.

Tips for managing specificity

  • Prefer classes over IDs in CSS to keep specificity manageable.
  • Avoid nesting selectors too deeply, as each level adds to specificity.
  • Use :where() for reusable utilities that should never win a specificity battle.
  • Reserve !important for truly exceptional cases like utility overrides.

!important

!important changes cascade priority; it is not a specificity value. For these same-origin declarations, an important declaration beats a normal declaration even when the normal selector has higher specificity. The complete cascade still considers origin and layer priority, so importance does not mean a declaration wins every possible cascade comparison. The interactive example above toggles importance to show how the winner changes.

.button {
  color: blue !important; /* Competes in the important-declaration cascade */
}

Why to use it sparingly: Important declarations have their own cascade ordering and can make later overrides harder to reason about. Prefer a clear layer and selector strategy when you control the stylesheet.

When it is useful: Utility classes that must override component styles, third-party styles you cannot control, or critical accessibility fixes.

Cascade layers (@layer)

CSS cascade layers let you define an order for groups of rules. Among normal declarations in different layers, the later layer has priority before specificity is compared. Among important declarations in different layers, the earlier layer has priority. Unlayered rules also have defined positions in the cascade, so a layer name such as utilities is not inherently highest priority. The control above demonstrates the normal-declaration rule with two layers and selectors of different specificity.

@layer reset, base, components, utilities;

@layer reset {
  * { margin: 0; padding: 0; }
}

@layer utilities {
  .hidden { display: none !important; }
}

For normal declarations, the last declared layer wins when declarations from different layers compete, even if its selector is less specific. Within one layer, specificity still matters. For important declarations, layer order reverses and the first layer has priority.

Specificity visualization

Compare these three selectors:

SelectorSpecificity (a,b,c,d)Explanation
p(0,0,0,1)1 type selector
.button(0,0,1,0)1 class
#header .nav a:hover(0,1,2,1)1 ID + 1 class + 1 pseudo-class + 1 type

Winner: #header .nav a:hover because it has an ID (A=1, or legacy b=1). Even if .button had 100 classes, it would still lose because b=0.

Frequently asked questions

What do the four numbers (a,b,c,d) mean?

In legacy (a,b,c,d) notation, a is inline-style weight and is 0 for selector-only calculations; b is IDs, c is classes/attributes/pseudo-classes, and d is types/pseudo-elements. Selectors Level 4 uses (A,B,C): A=b, B=c, and C=d.

Does !important affect specificity?

!important is a cascade priority, not a specificity value. For same-origin declarations, an important declaration outranks a normal declaration before selector specificity is compared. Other cascade factors, including origin and layer priority, still matter.

How does @layer order affect normal declarations?

Among normal declarations in different layers, the later layer wins before specificity is compared. For important declarations, layer order reverses and the earlier layer wins.

What is the specificity of :where()?

:where() always contributes 0 specificity, regardless of its arguments. This is by design, to allow easy overrides.

How does :not() affect specificity?

:not() itself contributes 0. The specificity comes from the most specific argument inside it. For example, :not(.active) has specificity (0,0,1,0).

Browse all developer tools