CSS Specificity & the Cascade

Which rule wins when two rules disagree, and how to stop fighting your CSS.

Lesson 30 of 49Intermediate12 min read

"Why isn't my CSS working?" Most of the time, the answer is that another rule is winning. When several rules set the same property on the same element, the browser follows a set of rules called the cascade to choose one.

This lesson explains those rules step by step. Once you understand them, you can look at any conflict in your browser's DevTools, see which rule won and why, and fix it properly instead of piling on more code.

How the browser chooses the winning rule

For each property, the browser checks, in this order:

  1. Importance: !important declarations beat normal ones.
  2. Specificity: the more specific selector wins.
  3. Source order: if specificity is equal, the rule that comes last wins.

Inline style="" attributes beat any selector in a stylesheet. In DevTools, the losing declarations appear with a line through them, which tells you immediately that something else is overriding them.

How to calculate specificity

Count three columns, like a score: (ids, classes, elements).

SelectorIDsClasses / attributes / pseudo-classesElements / pseudo-elementsScore
p0010-0-1
.note0100-1-0
p.note0110-1-1
.card .title:hover0300-3-0
#hero h11011-0-1

Compare the scores from the left: one id beats any number of classes, and one class beats any number of elements. The universal selector * and :where() count as zero.

Inheritance

Some properties pass down from a parent to its children: color, the font properties, line-height, text-align and visibility. Others don't: margin, padding, border, background and width.

A rule that targets an element directly always beats an inherited value, even a weak rule like p { }. That's why a paragraph can ignore the colour you set on its parent. Use inherit to force inheritance, as in a { color: inherit; }.

!important: the emergency exit

color: red !important; beats almost everything. It feels like a quick fix, but it starts an arms race, because the only way to beat it is another !important. Use it only for small utility classes or for overriding third-party CSS you can't edit.

A checklist for CSS that won't apply

When a style doesn't show up, work through these questions:

  1. Is the selector actually matching the element? Check the spelling of the class in both the HTML and the CSS.
  2. Is the stylesheet loading? Look for a 404 error in DevTools.
  3. Is the declaration valid? A typo in the property name is silently ignored.
  4. Is another rule winning? DevTools shows the winner and crosses out the loser.

Most problems are solved by the second or third question before specificity even comes into it.

CSS Specificity Syntax

The cascade order

/* 0-0-1 */ p { color: black; }
/* 0-1-0 */ .note { color: blue; }      /* beats p */
/* 1-0-0 */ #intro { color: green; }    /* beats .note */
/* later wins on a tie */
.note { color: purple; }                /* beats the first .note */

CSS Specificity Summary

  • The cascade chooses a winner by importance, then specificity, then source order.
  • Specificity is scored as ids, classes, elements, compared from the left.
  • One id beats any number of classes; one class beats any number of elements.
  • Some properties inherit, but any direct rule beats an inherited value.
  • Keep selectors simple and avoid !important except as a last resort.

CSS Specificity Examples

Each example runs in the page. Edit the code and press Run to see what changes.

Example 1

Specificity battle

Predict the colour of each line, then run.

#intro { color: green; }
.note { color: blue; }
p { color: gray; }
p.note { color: orange; }
ResultEdit the code, then press Run or Ctrl+Enter
Line-by-line explanation
  1. Line 1Score 1-0-0. The id wins for the third paragraph, even though it's written first.
  2. Line 2Score 0-1-0.
  3. Line 3Score 0-0-1. Only the plain paragraph ends up gray.
  4. Line 4Score 0-1-1 beats .note, so the second paragraph is orange.
Example 2

Inheritance in action

.post {
  color: #7c3aed;
  font-family: Georgia, serif;
  border: 2px solid #7c3aed;
  padding: 10px;
}

.post a { color: inherit; font-weight: bold; }
.own { color: #f0592b; }
ResultEdit the code, then press Run or Ctrl+Enter
Line-by-line explanation
  1. Lines 2 to 3Colour and font are inherited by the heading and paragraphs.
  2. Lines 4 to 5Border and padding are not inherited, so only the article has them.
  3. Line 8Links have their own default colour; inherit makes them follow the parent.
  4. Line 9A direct rule beats the inherited value.

Common CSS Specificity Mistakes

Fixing problems by adding IDs and !important

Don't
#page #content .card h2.title { color: red !important; }
Do
.card-title { color: red; }

Over-specific selectors make the next change even harder. Simplify the selectors instead.

Ordering rules backwards

Don't
.btn-primary { background: blue; }
.btn { background: gray; }
/* gray wins: equal specificity, later rule */
Do
.btn { background: gray; }
.btn-primary { background: blue; }

Write general rules first, specific ones after.

CSS Specificity in Real Projects

Debugging with DevTools

Right-click an element → Inspect. In the Styles panel you'll see every rule that matches, ordered by priority. Rules that lost are crossed out. Hover a selector to see its specificity. That panel answers "why isn't my CSS working?" in seconds.

CSS Specificity Exercises

Answer each one and check it instantly. If you get stuck, open the hint before the answer.

Fill in the blanksmedium

Write the specificity scores (IDs-classes-elements).

/*  */ ul.menu { }
/*  */ #logo { }
/*  */ .card:hover { }
Multiple choicemedium

Two rules have exactly the same specificity. Which one wins?

Multiple choicemedium

Which of these is inherited by default?

Predict the outputhard

What colour is the text?

<div id="box" class="box"><p class="text">Hello</p></div>
<style>
  #box { color: red; }
  .text { color: blue; }
</style>
Fix the bugmedium

The "Buy" button should be green, but a more specific rule keeps it gray. Fix it without !important and without an id, by changing the second selector so it wins.

.shop .btn {
  background: gray;
  color: white;
}

.buy {
  background: green;
}
ResultEdit the code, then press Run or Ctrl+Enter
Checks
  • The button is green
  • No !important used
  • No id selectors used

CSS Specificity Quiz

3 questions to check what you learned.

  1. Which selector is the most specific?

    • div p
    • .intro
    • #main
    • p
  2. What's the specificity of nav ul li a?

    • 0-1-3
    • 0-0-4
    • 1-0-0
    • 0-4-0
  3. What specificity does :where(.card, #hero) p have?

    • 1-0-1
    • 0-1-1
    • 0-0-0
    • 0-0-1

CSS Specificity Challenge

Win the cascade the clean way

The .alert.error box should be red-tinted, but currently a rule for .panel .alert wins. Add a rule that wins using only classes (no ids, no !important):

  • background #ffe4e4
  • border-color #e5484d
.alert { padding: 10px; border: 2px solid #ccc; }
.panel .alert { background: #f3f3f3; border-color: #999; }
ResultEdit the code, then press Run or Ctrl+Enter
Checks
  • Background is light red
  • Border is red
  • No !important used
  • No id selectors used