Three Numbers Decide Which Selector Wins
Specificity
Specificity is a three-part score — ids, classes/attributes/pseudos, and elements. Higher beats lower.
What you'll learn
- Calculate specificity
- Use `:where()` to keep specificity low
- Avoid the specificity war
Specificity is a score CSS computes for each selector. When two rules target the same element, the one with the higher specificity wins.
The Score — Three Slots
Think of specificity as a three-digit number (A, B, C):
- A — number of
#idselectors - B — number of
.class,[attribute],:pseudo-classselectors - C — number of element / pseudo-element selectors
Higher A beats any B. Higher B beats any C.
| Selector | Specificity |
|---|---|
* | (0, 0, 0) |
p | (0, 0, 1) |
p.lead | (0, 1, 1) |
.card.featured | (0, 2, 0) |
nav a:hover | (0, 1, 2) |
#nav a | (1, 0, 1) |
Inline style="..." | (1, 0, 0, 0) — beats all selectors |
!important | overrides specificity entirely |
Quick Examples
p { color: black; } /* (0, 0, 1) */
.lead { color: gray; } /* (0, 1, 0) — wins */
#hero p { color: red; } /* (1, 0, 1) — wins over both */ For a <p class="lead"> inside <div id="hero">, the text is
red.
The Specificity War
The classic bad spiral:
.btn { background: blue; } /* (0, 1, 0) */
.card .btn { background: green; } /* (0, 2, 0) — wins */
.sidebar .card .btn { background: pink; } /* (0, 3, 0) — wins */ Each rule “fixes” the previous by being more specific. Soon
nothing can be overridden without !important or ever-deeper
nesting.
The fix:
- Keep selectors flat (one class is usually enough)
- Use modifier classes (
.btn--primary,.btn--danger) instead of nesting - Use cascade layers (
@layer) to put high-specificity author rules over low-specificity defaults
:where() — Zero Specificity
:where() matches like its argument but contributes zero
specificity:
:where(.card, .panel) p { color: gray; } A later, simpler .lead { color: red; } overrides it without
needing to be more specific. Great for low-priority defaults.
:is() — Same Specificity as the Highest Inner
:is(.card, .panel) IS specific — it picks the specificity of its
most specific argument. Use it for grouping; use :where() for
suppressing.
Up Next
Inheritance — when no rule matches, what happens.
Inheritance →