What is specificity?
Here's the other half of the "why isn't my CSS applying?" mystery. When two rules
of equal origin clash, the last one doesn't just win by default: the one with
higher specificity wins, a measure of the selector's weight. It is calculated
by counting three categories, which you can picture as a three-column number
(A, B, C):
| Category | Counts | Weight |
|---|---|---|
id (#header) |
nº of ids | 100 |
class / attribute / pseudo-class (.notice, [type], :hover) |
nº of them | 10 |
element / pseudo-element (div, ::before) |
nº of them | 1 |
The universal selector
*and the combinators (,>,+,~) do not contribute specificity.
Compared examples
/* specificity 0,0,1 → 1 */
p { color: black; }
/* specificity 0,1,1 → 11 */
p.featured { color: green; }
/* specificity 0,1,0 → 10 */
.featured { color: blue; }
/* specificity 1,0,0 → 100 */
#notice { color: red; }
On a <p class="featured" id="notice">, #notice (100) would win because the
id weighs more than any combination of classes or elements. And between
p.featured (11) and .featured (10), p.featured wins for being more
specific, even though .featured is declared later.
The comparison is category by category, from left to right: a single id
beats any number of classes, and a single class beats any number of
elements. That's why 10 classes (0,10,0) do not beat an id (1,0,0).
⚠️ CLASSIC TRAP: id beats class, and class beats tag, regardless of order.
Picture a menu with .link { color: gray } for the normal state and
#menu a { color: tomato } to highlight it: the id wins and the links come out
orange, even though the class is written later. It's not magic or a browser bug;
it's the left column outranking the right one. When a rule "doesn't work", the
first thing to ask is which other rule is beating it on specificity.
!important: the last resort
Adding !important to a declaration takes it out of the normal specificity
competition and places it on top:
.button { color: blue !important; }
#header .button { color: red; } /* loses: the !important wins */
It works, and that's exactly why it's tempting: when something "won't apply",
dropping in an !important fixes it instantly. The catch is that it's a quick fix
that costs you later, bad practice except in very specific cases:
- It breaks the natural cascade and makes the CSS hard to predict.
- To override an
!importantyou need another!importantthat is even more specific: an endless!importantwar begins. - It is almost always a symptom of a poorly resolved specificity problem, papered over rather than fixed.
Good practice is to keep specificity low and uniform (rely on
classes, avoid ids for styling and don't over-nest selectors). That way the
cascade and the order of appearance are enough for the correct rule to win, and
!important stays where it belongs: a last resort, not your go-to hammer for
everything.