CSS specificity determines which styles win when multiple rules target the same element. Master this and you'll never use !important again.
The Specificity Hierarchy
Inline styles → (1,0,0,0)
IDs → (0,1,0,0)
Classes/attributes → (0,0,1,0)
Elements/pseudo → (0,0,0,1)
Universal (*) → (0,0,0,0)Calculating Specificity
/* (0,0,0,1) — one element */
p { color: red; }
/* (0,0,1,0) — one class */
.text { color: blue; }
/* (0,0,1,1) — one class + one element */
p.text { color: green; }
/* (0,1,0,0) — one ID */
#main { color: purple; }
/* (0,1,1,1) — one ID + one class + one element */
div#main.container { color: orange; }
/* Winner: orange (highest specificity) */The :where() and :is() Difference
/* :is() takes the highest specificity of its arguments */
:is(#header, .nav) a { } /* specificity of #header: (0,1,0,1) */
/* :where() has ZERO specificity — always */
:where(#header, .nav) a { } /* specificity: (0,0,0,1) */
/* :where() is perfect for resets and defaults */
:where(h1, h2, h3) { margin: 0; } /* easily overridable */Cascade Layers
@layer reset, base, components, utilities;
@layer reset {
* { margin: 0; padding: 0; }
}
@layer components {
.btn { background: blue; } /* Always beats reset regardless of specificity */
}
@layer utilities {
.bg-red { background: red; } /* Always beats components */
}Common Mistakes
- !important wars: If you need !important, your architecture is wrong. Use cascade layers instead.
- Over-qualifying selectors:
div.container > ul > li > a.linkis fragile. Use.nav-link. - Relying on source order: Source order only matters when specificity is equal.
Modern Best Practices
- Use cascade layers for architectural specificity control
- Use
:where()for low-specificity defaults - Prefer classes over IDs for styling
- Use BEM or utility classes to keep specificity flat