~/TechPurAI
~/tutorials/css-from-scratch/the-cascade-and-specificity
beginner·part 3 of 22·3 min read

The cascade and specificity: how conflicting CSS rules actually resolve

Updated Aug 16, 2026CSS

Part 1 mentioned that later rules override earlier ones. That's only part of the real story — CSS resolves conflicts through a genuine, defined algorithm, and understanding it is what turns "why isn't my style applying" from a mystery into something diagnosable in seconds.

A real conflict, and why one rule wins

css
p {
  color: #555;
}

.intro {
  color: #3d2b1f;
}
html
<p class="intro">Small-batch coffee, roasted weekly.</p>

Both rules match this real paragraph. The class selector wins, and the text renders in #3d2b1f — not because it comes second in the file, but because a class selector is genuinely more specific than an element selector, and specificity is checked before source order ever comes into play.

Specificity: a real, calculable score

text
Inline style           → 1,0,0,0  (highest of the four)
ID selector             → 0,1,0,0
Class, attribute,
  pseudo-class selector → 0,0,1,0
Element, pseudo-element → 0,0,0,1
css
#site-header .nav-link { }        /* 0,1,1,0 */
.product-card h3 { }              /* 0,0,1,1 */
nav a:hover { }                   /* 0,0,2,1 */

Each selector gets a real, calculable specificity score across four categories, compared left to right — an ID selector beats any number of class selectors, and a class selector beats any number of element selectors, regardless of how many of the lower category are stacked together. #site-header .nav-link (one ID, one class) beats .product-card h3 (one class, one element) — not because it's more complex-looking, but because its highest category (an ID) outranks the other rule's highest category (a class).

Source order: the real tiebreaker, not the first rule

css
.price { color: #555; }
.price { color: #ff8a3d; }

Source order only decides the outcome when two real rules have identical specificity — here, both selectors are single classes, so the one that appears later in the file (or later in a later-linked stylesheet) wins. This is genuinely why "just move my CSS rule further down the file" is a common real fix — but only actually works when specificity is already tied; it does nothing against a rule with a genuinely higher specificity score written earlier.

!important: a real escape hatch, and a real trap

css
.price {
  color: #ff8a3d !important;
}

!important overrides normal specificity entirely — a declaration marked this way wins regardless of the selector's own specificity score, with only another !important declaration able to override it. It's a real, working tool, but a genuinely dangerous habit: once one real rule uses !important, overriding it later requires either another !important (compounding the problem) or increasing specificity to a point that also affects other, unrelated selectors — a real, common source of stylesheets that become progressively harder to reason about over time.

Common mistake

Reaching for !important as the fast fix the moment a style "isn't working," instead of checking the real specificity of the conflicting rule first. In the overwhelming majority of real cases, a style isn't applying because a more specific selector elsewhere is winning — a genuine, diagnosable cause, not a reason to reach for a blunt override that creates a harder problem down the line.

Inheritance: a real, separate mechanism from the cascade

css
body {
  font-family: "Inter", sans-serif;
  color: #2b2b2b;
}

Some properties (color, font-family, line-height) are inherited by default — set once on <body>, and every descendant element uses that same value unless a more specific rule overrides it for that element specifically. Other properties (border, padding, background) are never inherited by default, since inheriting them would rarely make real visual sense — a <p> automatically inheriting its parent's border would be a genuinely confusing default. This is exactly why setting real, sitewide typography once on body (covered fully in part 6) is standard practice, rather than repeating font-family on every individual element.

Next: the box model — content, padding, border, and margin, and the real property that changes how they're all measured together.

VK

Vijay Kumar

Founder of TechPurAI — writing hands-on tutorials and honest tool breakdowns.

LinkedIn ↗
← previous2. Selectors: element, class, ID, and combinatorsnext →4. The box model: content, padding, border, and margin