Specificity explains why one CSS rule wins over another. When styles do not apply as expected, the issue is often not that CSS is broken; it is that another selector is stronger or appears later in the cascade.
The goal is not to write the strongest selector. The goal is to write selectors that are strong enough to target the right element but still easy to override later.
When multiple CSS rules target the same element, the browser uses specificity to determine which rule wins. Specificity is calculated as a score - the rule with the highest score is applied.
Specificity is calculated using four categories (A, B, C, D):
| Category | What counts | Score |
|---|---|---|
| A - Inline styles | style="..." attribute | 1,0,0,0 |
| B - ID selectors | #id | 0,1,0,0 |
| C - Class, attribute, pseudo-class | .class, [attr], :hover | 0,0,1,0 |
| D - Element, pseudo-element | div, p, ::before | 0,0,0,1 |
/*
* Specificity scores (A, B, C, D):
*
* * -> 0,0,0,0 (universal)
* p -> 0,0,0,1 (element)
* .class -> 0,0,1,0 (class)
* #id -> 0,1,0,0 (ID)
* style="" -> 1,0,0,0 (inline)
*
* p.class -> 0,0,1,1 (element + class)
* div p -> 0,0,0,2 (2 elements)
* .nav .link -> 0,0,2,0 (2 classes)
* #header .nav a -> 0,1,1,1 (ID + class + element)
* #header #nav -> 0,2,0,0 (2 IDs)
*/
/* Which color wins? */
p { color: blue; } /* 0,0,0,1 */
.text { color: green; } /* 0,0,1,0 - wins over p */
#main { color: red; } /* 0,1,0,0 - wins over .text */
/* inline style="color: orange" - wins over #main */
/* !important - overrides everything (use sparingly!) */
p { color: blue !important; } /* wins even over inline styles */
/* Specificity tie - last rule wins */
.btn { background: blue; }
.btn { background: red; } /* wins - same specificity, later in file */
/* Practical: avoid high specificity chains */
/* BAD - hard to override */
#sidebar .widget ul li a { color: blue; }
/* GOOD - low specificity, easy to override */
.sidebar-link { color: blue; }
The cascade determines which CSS rule applies when multiple rules conflict. It considers three factors in order:
Low-specificity CSS is easier to maintain. Utility classes, component classes, and modern helpers like :where() can keep selectors predictable.
/* Inheritance - some properties inherit from parent */
/* Inherited: color, font-*, line-height, text-*, visibility */
/* Not inherited: margin, padding, border, background, width, height */
body {
color: #333; /* all text inherits this color */
font-family: Arial; /* all text inherits this font */
}
/* inherit keyword - force inheritance */
.child {
border: inherit; /* inherit border from parent (not normally inherited) */
color: inherit; /* explicitly inherit color */
}
/* initial keyword - reset to browser default */
.reset {
color: initial; /* resets to browser default (usually black) */
font-size: initial; /* resets to browser default (usually 16px) */
}
/* unset keyword - inherit if inheritable, else initial */
.unset {
color: unset; /* inherits (color is inheritable) */
margin: unset; /* initial (margin is not inheritable) */
}
/* revert keyword - reset to browser stylesheet value */
.revert {
all: revert; /* reset ALL properties to browser defaults */
}
/* Specificity best practices */
/* 1. Keep specificity low - use classes, not IDs */
/* 2. Avoid !important - it breaks the cascade */
/* 3. Use :is() and :where() for complex selectors */
/* :is() - matches any selector in list, takes highest specificity */
:is(h1, h2, h3) { margin-top: 0; }
/* :where() - same but ZERO specificity (easy to override) */
:where(h1, h2, h3) { margin-top: 0; }
.lesson-box:empty::before {
content: "CSS Specificity Cascade How It Works: add visible content";
}
No. Source order matters only after importance and specificity are equal.
IDs have high specificity, which makes later overrides harder.
It is not always bad, but it should be used deliberately because it can make future overrides difficult.
Explore 500+ free tutorials across 20+ languages and frameworks.