When two CSS rules target the same element with conflicting values, which one wins is decided by a precise, calculable algorithm — not by which rule 'feels' more specific, and definitely not by which one was written more recently, alone.
3 min read
/* 1. !important — wins regardless of everything else below (use sparingly) */
/* 2. Inline style="..." attribute — beats any stylesheet rule */
/* 3. Specificity — a calculated score per selector, covered below */
/* 4. Source order — if specificity is EQUAL, the LAST rule declared wins */CSS's name for this whole resolution process is the cascade, and it runs through these tie-breakers in a fixed order — specificity is only even consulted once !important and inline styles are ruled out, and source order is only the deciding factor when specificity is genuinely tied. Most real-world "why isn't my CSS applying" confusion comes from skipping straight to "I'll just write it later in the file so it wins" without realizing a more specific selector earlier in the file can still beat it.
p { color: blue; } /* specificity: (0, 0, 1) — one element */
.text { color: green; } /* specificity: (0, 1, 0) — one class */
#hero p { color: red; } /* specificity: (1, 0, 1) — one ID, one element */
/* On a <p class="text"> inside <div id="hero">, the color is RED —
an ID selector always outweighs any number of classes or elements */Specificity is calculated as a three-part score: (ID count, class/attribute/pseudo-class count, element/pseudo-element count) — compared left to right, like a version number, not added together as a single sum. One ID beats any number of classes; one class beats any number of bare elements; a selector with zero of a given category simply scores zero in that slot. This is precisely why #hero p beats .text here even though .text looks "more specific" by word count — IDs categorically outrank classes, full stop, regardless of how many classes are stacked.
!important usually makes the next bug worse.button { color: blue !important; }
/* Now the ONLY way to override this anywhere else in the codebase
is another !important with equal-or-higher priority — normal
specificity rules no longer apply at all */!important doesn't participate in specificity at all — it short-circuits the cascade entirely, before specificity is even consulted. The real problem: once one rule uses it, overriding that rule anywhere else legitimately requires another !important, which starts an arms race that makes the stylesheet progressively harder to reason about. It's a legitimate escape hatch for a narrow set of real situations (overriding a third-party library's inline styles you can't edit) — but reaching for it to win an ordinary specificity fight is usually a sign the actual selector needs fixing, not silencing.
The cascade doesn't care whether two conflicting rules live in the same file or in entirely separate stylesheets (your own CSS vs. a component library's CSS, for instance) — it's the same specificity-then-source-order algorithm either way. This matters in practice because load order affects source-order tie-breaks across files: a stylesheet loaded later in the <head> wins ties against one loaded earlier, which is a real, common source of "the library's default styling isn't overriding the way I expected" bugs when stylesheet order gets rearranged.
Check your understanding
A quick comprehension check — not tracked, not graded, just for you.
1. In what order does the cascade resolve conflicting CSS rules?
2. Why does `#hero p` beat `.text` even though `.text` might look shorter or 'more specific' by word count?
3. Why does reaching for `!important` to win an ordinary specificity fight tend to cause more problems later?
CSS & Modern Styling