"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:
- Importance:
!importantdeclarations beat normal ones. - Specificity: the more specific selector wins.
- 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).
| Selector | IDs | Classes / attributes / pseudo-classes | Elements / pseudo-elements | Score |
|---|---|---|---|---|
p | 0 | 0 | 1 | 0-0-1 |
.note | 0 | 1 | 0 | 0-1-0 |
p.note | 0 | 1 | 1 | 0-1-1 |
.card .title:hover | 0 | 3 | 0 | 0-3-0 |
#hero h1 | 1 | 0 | 1 | 1-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:
- Is the selector actually matching the element? Check the spelling of the class in both the HTML and the CSS.
- Is the stylesheet loading? Look for a 404 error in DevTools.
- Is the declaration valid? A typo in the property name is silently ignored.
- 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
!importantexcept as a last resort.
CSS Specificity Examples
Each example runs in the page. Edit the code and press Run to see what changes.
Specificity battle
Predict the colour of each line, then run.
<p>Plain paragraph</p>
<p class="note">Paragraph with a class</p>
<p class="note" id="intro">Paragraph with a class and an id</p>#intro { color: green; }
.note { color: blue; }
p { color: gray; }
p.note { color: orange; }Line-by-line explanation
- Line 1Score 1-0-0. The id wins for the third paragraph, even though it's written first.
- Line 2Score 0-1-0.
- Line 3Score 0-0-1. Only the plain paragraph ends up gray.
- Line 4Score 0-1-1 beats
.note, so the second paragraph is orange.
Inheritance in action
<article class="post">
<h3>Inherited heading</h3>
<p>Inherited paragraph with a <a href="#">link</a>.</p>
<p class="own">I have my own colour.</p>
</article>.post {
color: #7c3aed;
font-family: Georgia, serif;
border: 2px solid #7c3aed;
padding: 10px;
}
.post a { color: inherit; font-weight: bold; }
.own { color: #f0592b; }Line-by-line explanation
- Lines 2 to 3Colour and font are inherited by the heading and paragraphs.
- Lines 4 to 5Border and padding are not inherited, so only the article has them.
- Line 8Links have their own default colour;
inheritmakes them follow the parent. - Line 9A direct rule beats the inherited value.
Common CSS Specificity Mistakes
Fixing problems by adding IDs and !important
#page #content .card h2.title { color: red !important; }.card-title { color: red; }Over-specific selectors make the next change even harder. Simplify the selectors instead.
Ordering rules backwards
.btn-primary { background: blue; }
.btn { background: gray; }
/* gray wins: equal specificity, later rule */.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.
Write the specificity scores (IDs-classes-elements).
/* */ ul.menu { }
/* */ #logo { }
/* */ .card:hover { }
ul.menu = 1 class + 1 element; #logo = 1 id; .card:hover = 2 class-level parts.Two rules have exactly the same specificity. Which one wins?
Which of these is inherited by default?
What colour is the text?
<div id="box" class="box"><p class="text">Hello</p></div>
<style>
#box { color: red; }
.text { color: blue; }
</style>
#box sets the div's colour; the paragraph only inherits it. .text targets the paragraph directly, so it wins.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.
<div class="shop"><button class="btn buy">Buy</button></div>.shop .btn {
background: gray;
color: white;
}
.buy {
background: green;
}- The button is green
- No !important used
- No id selectors used
CSS Specificity Quiz
3 questions to check what you learned.
Which selector is the most specific?
div p.intro#mainp
What's the specificity of
nav ul li a?- 0-1-3
- 0-0-4
- 1-0-0
- 0-4-0
What specificity does
:where(.card, #hero) phave?- 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
<div class="panel">
<div class="alert error">Payment failed.</div>
</div>.alert { padding: 10px; border: 2px solid #ccc; }
.panel .alert { background: #f3f3f3; border-color: #999; }
- Background is light red
- Border is red
- No !important used
- No id selectors used