CSS Specificity
Specificity determines which CSS rule wins when multiple rules target the same element. It's calculated as a score in three categories.
Specificity score: (A, B, C)
| Category | What counts | Example |
|---|---|---|
| A - IDs | #id | #header = (1,0,0) |
| B - Classes/attributes/pseudo-classes | .class, [attr], :hover | .btn:hover = (0,2,0) |
| C - Elements/pseudo-elements | div, p, ::before | div p = (0,0,2) |
Calculation examples
css
p /* (0,0,1) */
.class /* (0,1,0) */
#id /* (1,0,0) */
div .class p /* (0,1,2) */
#id .class:hover /* (1,2,0) */Comparing scores
css
/* which color wins? */
#header .nav a { color: red; } /* (1,1,1) */
.nav a:hover { color: blue; } /* (0,2,1) */
/* red wins - (1,1,1) beats (0,2,1) */
/* A column always beats any number of B or C */Special cases
css
/* inline styles - highest specificity (beats everything except !important) */
<div style="color: red">
/* !important - overrides everything */
.btn { color: blue !important; }
/* :is(), :not(), :has() - take specificity of their most specific argument */
:is(#id, .class) { } /* (1,0,0) */
/* :where() - always zero specificity, easy to override */
:where(#id, .class) { } /* (0,0,0) */Cascade layers
Specificity is compared only inside the same @layer. A rule in a later layer wins over a more specific rule in an earlier one, and unlayered styles beat all layers:
css
@layer reset, components;
@layer reset { #app a { color: gray; } } /* (1,0,1) but loses */
@layer components { .link { color: blue; } } /* wins - later layer */Avoid
!important- it breaks the cascade and makes debugging painful. Use:where()for low-specificity defaults and@layerto order groups of styles instead of fighting with selectors.
updated
did you know it?