DataLens

The cascade, specificity, and inheritance

Understand how the browser picks a winner when several CSS rules set the same property, through source order, specificity and !important, and which properties children inherit from their parents.

Level: Intermediate · 6 min read

Practise this module

The questions for this lesson run in your browser and check your answer straight away.

The idea

Sooner or later you'll write a CSS rule and nothing happens. Almost always, another rule is winning. The browser resolves conflicts with a fixed algorithm, the cascade, and once you know its three main steps, "why isn't my CSS working?" becomes a quick diagnosis instead of a guessing game.

Step 1: later rules win (when equally specific)

Start with the simplest conflict. Two rules both set the colour of the price, and they use exactly the same selector. Which colour does the price end up?

<style>
  .price { color: green; }
  .price { color: #7a4b2a; }
</style>

<p class="price">Rp 95.000</p>

Two rules with the same selector set color. The one that comes later in the stylesheet wins: brown. This also applies across files: a stylesheet linked later overrides an earlier one.

Step 2: more specific selectors win

Now the rules use different selectors that all match the same price. Order no longer decides it. Before reading the explanation, try to guess what colour each price will be:

<style>
  #featured { color: purple; }        /* 1 id */
  .card .price { color: #7a4b2a; }    /* 2 classes */
  .price { color: green; }            /* 1 class */
  p { color: blue; }                  /* 1 type */
</style>

<article class="card" id="featured">
  <p class="price">Rp 95.000</p>
</article>
<p class="price">Rp 120.000</p>

Specificity scores each selector by counting three things, compared left to right like version numbers:

Selectoridsclasses, attributes, pseudo-classestypes, pseudo-elementsScore
p0010-0-1
.price0100-1-0
.card .price0200-2-0
nav a:hover0120-1-2
#featured1001-0-0

Higher score wins, regardless of order. One id beats any number of classes; one class beats any number of types. The first price is inside #featured, but #featured targets the article, not the paragraph, so the paragraph's own rules decide: .card .price (0-2-0) beats .price (0-1-0) → brown. The second price only matches .price and p → green.

Inline styles (style="..." on the element) beat any selector, which is one reason to avoid them.

Step 3: !important (and why to avoid it)

color: red !important; jumps to the front of the queue, above all normal declarations. It's tempting when a rule won't apply, but the only way to override it later is another !important, and stylesheets that go down that road become impossible to reason about. Reserve it for rare utility cases (like a .hidden { display: none !important; } helper). The better fix is almost always to:

  1. lower the specificity of the rule you're fighting (classes instead of ids), or
  2. make your rule match the same specificity and place it later.

Inheritance

There's one more way an element can get a style: from the element it sits inside. If you set a font on body, every paragraph and heading on the page uses it, even though no rule targets them directly. But not every property works like that:

<style>
  body { font-family: Georgia, serif; color: #333; line-height: 1.6; }
  .card { border: 2px solid #7a4b2a; padding: 1rem; }
</style>

<div class="card">
  <h2>Arabica Gayo</h2>
  <p>Text inherits the font and colour from body, but not the card's border or padding.</p>
</div>

That's why you set the font once on body instead of on every element. A child can opt in explicitly with inherit (border: inherit;), and form controls are a common gotcha: buttons and inputs don't inherit fonts by default, so many stylesheets add button, input, select, textarea { font: inherit; }.

Key idea

When rules conflict: higher specificity wins (ids > classes > types); with equal specificity, the later rule wins. Avoid ids and !important in stylesheets so specificity stays low and predictable. Text properties inherit; box properties don't.

Debugging a rule that "doesn't work"

Open developer tools (F12), select the element, and look at the Styles panel: it lists every matching rule in cascade order, with overridden declarations crossed out. The rule at the top is the winner, and its selector tells you how to beat it.

Why this matters for clean code

Predictable CSS comes from flat, low specificity: mostly single classes (.card-title, not #main .content div h2). Then source order alone decides conflicts, any rule can be overridden by a slightly later one, and you never need !important. The cascade stops being a fight and becomes a tool.

Recap

Practise this module

The questions for this lesson run in your browser and check your answer straight away.

Next: Flexbox →