Predict which CSS rule wins using the cascade, specificity, source order and inheritance.
Why this matters
"My CSS isn't working" usually means "another rule is winning". Several rules often target the same element, and the browser needs a way to choose. That system is the cascade, the C in CSS. Once you can predict the winner, you stop adding random !important flags and start writing CSS that stays easy to change.
A CSS rule, briefly
.card h2 {
color: #1d4ed8;
margin-bottom: 0.5rem;
}
.card h2is the selector: which elements the rule applies to.color: #1d4ed8;is a declaration: a property and a value.
CSS can live in a <style> element in the head (as in this course's exercises), in an external file linked with <link rel="stylesheet" href="styles.css"> (best for real sites), or in a style attribute on an element (best avoided).
How the browser picks a winner
When two declarations set the same property on the same element, the browser compares them in this order and stops at the first difference:
- Origin and importance. Your styles beat the browser's default styles. A declaration marked
!importantbeats normal declarations. - Cascade layers (
@layer), an advanced feature you can ignore for now. - Specificity. The more specific selector wins.
- Source order. If everything else ties, the rule that comes last wins.
Calculating specificity
Count three kinds of things in the selector and write them as (A, B, C):
| Column | Counts | Examples |
|---|---|---|
| A | id selectors | #content |
| B | classes, attributes, pseudo-classes | .warning, [type="email"], :hover |
| C | element types and pseudo-elements | p, h2, ::before |
Compare column A first; only if it ties, compare B; then C. A higher number in an earlier column always wins, no matter how big the later columns are.
| Selector | Specificity |
|---|---|
p | (0, 0, 1) |
.warning | (0, 1, 0) |
p.warning | (0, 1, 1) |
nav a:hover | (0, 1, 2) |
#content p | (1, 0, 1) |
#content .warning | (1, 1, 0) |
The universal selector * and combinators (space, >, +, ~) add nothing. Inline style="..." attributes beat any selector, which is one reason to avoid them.
Worked example: why is my warning grey?
<main id="content">
<p class="warning">Save your work!</p>
</main>
#content p { color: #333333; } /* (1, 0, 1) */
.warning { color: #b41e1e; } /* (0, 1, 0) */
The warning is grey. .warning comes later, but source order only matters when specificity ties, and #content p has an id. Column A: 1 beats 0, game over.
Three ways to fix it, from best to worst:
- Match or beat the specificity deliberately:
#content .warning { color: #b41e1e; }is (1, 1, 0), which beats (1, 0, 1). - Lower the specificity of the original rule: if you control it, write
main por a class instead of an id, so simple classes can override it. Teams often avoid ids in CSS for exactly this reason. !important: it works, but the next person who needs to override it must use!importanttoo. Keep it for rare cases such as utility classes.
Inheritance
Some properties, mostly text-related ones such as color, font-family, font-size and line-height, are inherited: children take the parent's value unless something targets the child directly. Layout properties like margin, padding, border and width are not inherited.
body { color: #222; font-family: system-ui, sans-serif; }
Every paragraph, list and heading now uses that colour and font. But a direct rule always beats an inherited value, even a weak one: p { color: gray } beats #content { color: red } for a paragraph inside #content, because the id rule targets the parent, not the paragraph.
Use your developer tools
Right-click an element, choose Inspect, and look at the Styles pane. Rules are listed from most to least specific, and losing declarations are shown crossed out. The Computed tab shows the final value of every property and lets you jump to the rule that set it. This is the fastest way to answer "which rule is winning?".
Common mistakes
- Expecting the later rule to win when the earlier one is more specific.
- Reaching for
!importantinstead of understanding the conflict. - Styling with ids, which makes overrides painful.
- Over-long selectors like
body main section div p.note, which are fragile and hard to override. - Confusing inherited values with direct rules.
Quick recap
- Conflicts are decided by importance, then specificity, then source order.
- Specificity is (ids, classes/attributes/pseudo-classes, elements), compared column by column.
- Text properties inherit; box properties do not; a direct rule beats inheritance.
- The Styles pane shows the winner and crosses out the losers.
Try it
Open the Do tab and solve a specificity puzzle without changing the HTML or using !important.