Problem Solved

CSS · specificity

Home › Courses › CSS › Problems › Specificity
🎨 Concept · 3 min read

CSS specificity.
Four levels. One winner.

Specificity is the score that decides which CSS rule wins when multiple rules target the same element. It's the reason your .button rule "doesn't work" — an #header rule with an ID is winning. It's also the reason !important exists, and the reason !important is a trap. This page covers the four levels, how to calculate, and the modern tools that make the specificity war rare.

Short answer: Specificity has four levels, from highest to lowest: inline styles, IDs, classes/attributes/pseudo-classes, elements/pseudo-elements. A single item in a higher level beats any number in a lower level — one ID beats ten classes. When scores tie, source order decides (later wins). Use classes, not IDs. Keep selectors short. Avoid !important except for utility overrides. Use @layer for explicit control.

✅ The four levels

From highest to lowest.

Inline styles

<div style="color: red">. Score (1,0,0,0). Beats every rule except !important.

IDs

#header. Score (0,1,0,0). Beats any number of classes.

Classes, attributes, pseudo-classes

.button, [type="text"], :hover. Score (0,0,1,0). The workhorse level.

Elements, pseudo-elements

div, ::before. Score (0,0,0,1). Lowest specificity. Use for base styles.

✅ Before and after

The most common specificity conflict.

losing-rule.css
#header .button {
color: blue;
}

.button {
color: red; /* your "fix" — doesn't apply */
}
Specificity:
#header .button → (0,1,1,0) = wins
.button → (0,0,1,0) = loses

❌ #header .button has 1 ID → wins.
❌ .button has 0 IDs → loses.
❌ The button stays blue, not red.
❌ Adding more classes won't help.

Add .button.special? → (0,0,2,0) — still loses to (0,1,1,0).
Only another ID would win: #header .button.special → (0,1,2,0).

This is the specificity war.
winning-rule.css
/* Use classes for all component styles */
.header .button {
color: blue;
}

/* And when you need to override, use the same
level — same number of classes, later wins. */
.button {
color: red;
}

/* OR bump specificity with a modifier class: /
.button--danger {
color: red;
}
Specificity:
.header .button → (0,0,2,0)
.button → (0,0,1,0) — still loses
.button--danger → (0,0,1,0) — also loses

Fix 1: same or higher class count:
.header .button--danger → (0,0,2,0) → ties, later wins

Fix 2: reorder — put .button rules before .header .button.

Fix 3: use @layer to control order explicitly.
@layer base, components, utilities;
base wins first, utilities win last.

✅ No IDs. No !important. Predictable cascade.

✅ @layer — the modern fix

Control order without touching specificity.

layers.css
@layer base, components, utilities;

@layer base {
.button {
color: blue;
padding: 0.5rem 1rem;
}
}

@layer components {
.button {
background: white;
border: 1px solid #ccc;
}
}

@layer utilities {
.text-red {
color: red !important;
}
}
Layer order: base, components, utilities.
Later layers win regardless of specificity.

If .button is in @layer base → blue wins.
If .button is in @layer components → components wins.
If .text-red is in @layer utilities → utility wins.

✅ Same selector in different layers → the
later layer wins.
✅ No specificity math needed.
✅ Perfect for design systems and component
libraries.
✅ !important inside @layer utilities is standard
(this is how Tailwind works internally).

This is the modern answer to "how do I avoid
specificity wars." Use @layer to define intent.

Don't use !important to fix specificity. It always works at first — the rule applies, the visual changes. But every !important demands another !important to override it. Pages accumulate them until nobody can predict which styles apply. The fix is not a stronger override — it's using the right selector at the right level. Use classes not IDs. Keep selectors short. Use @layer for intentional ordering. Reserve !important for utility classes (where it's part of the pattern) or third-party overrides you can't edit.

⚠️ Common mistakes

The specificity mistakes that cost real time.

1. Using IDs in CSS

High specificity that's hard to override. Fix: use classes for styling; IDs only for anchors and JS hooks.

2. Reaching for !important

Spreads and demands more !important. Fix: fix the selector, not the override.

3. Adding specificity instead of reducing it

.header .nav .link wins because it's deeper. Fix: use short selectors + @layer.

4. Assuming .class beats element.class

Both have 1 class — source order decides. Fix: understand the score, not the visual length.

5. Inline styles with JS

Beats everything except !important — hard to override in CSS. Fix: use classes toggled by JS.

6. Inline style="..."

Same problem — hidden specificity. Fix: move to a class.

7. :where() for lowering specificity

Good tool, misunderstood. Fix: :where(.a) .b has the same specificity as .b alone.

8. Not checking DevTools

Wasting time guessing. Fix: DevTools shows the winning rule and strikes through the losing ones.

❓ Frequently asked

How does CSS specificity work?

Specificity is a four-part score that determines which CSS rule wins when multiple rules target the same element. From highest to lowest: (1) inline styles; (2) IDs; (3) classes, attributes, and pseudo-classes; (4) elements and pseudo-elements. Each level is scored independently; a single ID beats any number of classes. When scores are equal, source order decides — the later rule wins.

What is the specificity of a class vs an ID?

An ID is one level higher than a class — always. Specificity is (a,b,c,d): a = inline, b = IDs, c = classes, d = elements. A class has score 0,0,1,0. An ID has score 0,1,0,0. An ID wins regardless of how many classes target the same element. This is why IDs in CSS are discouraged — they create high-specificity rules that are hard to override without !important.

Why is !important bad?

!important overrides specificity entirely — it doesn't matter what the selector is. That sounds useful, but it creates a new problem: to override an !important rule, you need another !important rule with equal or higher specificity, and then the source order decides. Pages accumulate !important declarations until nobody can predict which styles apply. Use !important only for utility classes or third-party CSS you can't edit.

What is the difference between specificity and inheritance?

Specificity decides which rule wins when multiple rules target the same element directly. Inheritance is about child elements receiving values from their parent when no rule targets them directly. Inherited values have the lowest priority — any direct rule beats an inherited value, regardless of specificity. Not all properties inherit (margin, padding, border don't; color, font, line-height do).

How do I avoid the specificity war?

Four practices. (1) Use classes, not IDs. (2) Keep selectors short. (3) Use CSS custom properties instead of fighting specificity. (4) Use @layer to control order explicitly — later layers win regardless of specificity. Modern CSS has tools that make specificity wars rare.

What is the order of CSS specificity levels?

Highest to lowest: (1) !important declarations; (2) inline styles; (3) ID selectors; (4) class selectors, attribute selectors, pseudo-classes; (5) element selectors, pseudo-elements; (6) universal selector and combinators — all zero specificity. Lower levels are additive: a rule with one class and one element is (0,0,1,1). But a single item in a higher level always beats any number in lower levels.

🔗 Related

← All CSS problems
Problem solved · CSS