CSS FundamentalsIntermediate8 min04 / 9

The Cascade & Specificity

Discover why some CSS rules win over others — and build a reliable mental model for debugging 'why isn't my style applying?'

You've written a CSS rule, refreshed the browser, and… nothing changed. This is one of the most common moments of confusion for CSS beginners, and it has a very logical explanation.

CSS stands for Cascading Style Sheets. When two or more rules compete to style the same element, CSS uses a well-defined algorithm — the cascade — to pick the winner. The cascade considers three things in order: specificity, source order, and !important. Once you understand these, mysterious overrides stop feeling like magic and start feeling like a puzzle you can always solve.

Think of it like

The cascade is like a job interview panel

Picture three judges scoring candidates. The first judge (Specificity) gives a score based on how precisely each rule targets the element. If scores are tied, the second judge (Source Order) picks whoever came last — that's why p { color: tomato; } wins over an earlier p { color: steelblue; } when selectors are identical. The third judge (!important) has veto power and overrides everything — but using that power carelessly causes chaos.

#Specificity: The Score

Specificity is a score that measures how precisely a selector targets an element. CSS calculates it using three buckets — (A, B, C):

| Bucket | What counts | Example | |--------|-------------|-------| | A | ID selectors | #hero | | B | Class, attribute, pseudo-class | .btn, [type], :hover | | C | Element / pseudo-element | p, h1, ::before |

Compare bucket by bucket, left to right. One ID (1,0,0) beats any number of classes (0,99,0). And above all selectors sits the inline style, written directly on the element — it has no selector at all, so it auto-wins.

Full ranking, lowest to highest: `` element selector < class/attribute/pseudo-class < ID < inline style ``

For <p id="hero" class="intro">, the ID rule wins: color is tomato.
/* (0,0,1) — one element */
p            { color: steelblue; }

/* (0,1,0) — one class   */
.intro       { color: goldenrod; }

/* (1,0,0) — one ID      */
#hero        { color: tomato; }
Watch out

!important: avoid it — it's a trap

Adding !important after a value (color: seagreen !important;) yanks that declaration above the entire specificity system — it even beats inline styles. It feels like a quick fix, but once you use it the only way to override it is with another !important, and you end up in an arms race. Prefer writing a more specific selector instead. Reserve !important for true utility classes like .hidden { display: none !important; } where an unconditional rule makes semantic sense.

#Inheritance

Some CSS properties flow down from a parent element to its children automatically — this is inheritance. Text properties like color, font-family, and line-height inherit by default. Box-model properties like margin, padding, border, and width do not.

Inheritance is a feature: set font-family on body and every text element on the page picks it up without you writing a rule for each one. If you ever need to force a non-inheriting property to inherit, set its value to the keyword inherit.

#A Mental Model for Debugging

When a style isn't applying, walk through this checklist:

  1. Is the selector matching? Open browser DevTools (F12 or right-click → Inspect). The Styles panel shows every competing rule — crossed-out declarations are the losers.
  2. Is something more specific winning? Look for an ID selector or inline style overriding your class.
  3. Is source order the issue? A later rule with the same specificity silently overwrites yours.
  4. Is `!important` involved? DevTools labels these clearly.
  5. Is it an inherited property? Check whether the property you expect to inherit actually does (text properties: yes; box properties: no).

DevTools is your fastest path to answers — the winning value is shown in bold; everything else is struck through.

Common mistake

Specificity is per-rule, not cumulative

Writing .nav .nav .nav .nav in four separate rules does not 'bank' specificity. Each rule is scored independently when the cascade runs. Only selectors within a single rule combine their scores.

Quick check

Given these two CSS rules, what colour will a paragraph with class='highlight' render in? p { color: steelblue; } .highlight { color: goldenrod; }

Key takeaways

  • The cascade resolves conflicts using specificity first, then source order — later rules win ties.
  • Specificity is scored as (IDs, classes/attributes/pseudo-classes, elements) — a higher score wins regardless of order.
  • Inline styles beat any stylesheet selector; !important beats everything — use both sparingly.
  • Inheritable properties (color, font-*) flow from parent to child automatically; box-model properties do not.
  • Browser DevTools is the fastest debugging tool — look for crossed-out declarations to find what's overriding your rule.
Practice challenges
Test yourself · earn XP
0/4
Predict the output#1

Given the HTML <p id="hero" class="intro">Hi</p> and the CSS below, what color does the paragraph render?

predict-output
p        { color: steelblue; }
.intro   { color: goldenrod; }
#hero    { color: tomato; }
Fix the bug#2

This code has a bug — the developer expected the .btn-primary background to win over the button rule, but the button stays steelblue. What's wrong?

fix-bug
button {
  background-color: steelblue;
}

.btn-primary {
  background-colour: seagreen;
}
Fill in the blank#3

The value below yanks a declaration above the entire specificity system — even above inline styles. The lesson warns to reserve it for true utility classes. Complete the .hidden rule with that keyword/flag.

.hidden {
  display: none ;
}
Reorder the lines#4

For <p class="note">, these four rules all set color and all match. Order them from LOWEST cascade priority (top) to HIGHEST priority (bottom) — the bottom rule's color is what actually renders.

1
p          { color: steelblue; }   /* (0,0,1) element */
2
#lead      { color: tomato; }      /* (1,0,0) ID */
3
p          { color: seagreen !important; }  /* !important wins */
4
.note      { color: goldenrod; }   /* (0,1,0) class */
Your turn
Practice exercise

The button below should have a green background. Three rules are competing for it. Make the .btn-primary rule win — without using !important or adding inline styles. You may rewrite or remove any existing rule.

Try it live — edit the code and hit Run to see it rendered:

solution.css · editable