Chapter 4: Cascade, Specificity, and Inheritance
Why your styles don’t apply, what wins in conflicts, and how to fix specificity wars
This chapter is where I’ll explain one of the most misunderstood aspects of CSS.
Ever wondered:
- Why doesn’t my style apply even though the selector looks right?
- Why does this
!important
rule win even when it’s further up the file? - Why does my child element inherit font but not color?
Let’s finally understand:
- The Cascade: how the browser resolves conflicts
- Specificity : how selectors are ranked
- Inheritance: what’s passed down by default
Section 4.1: The Cascade
Cascade = the browser’s decision making process.
When multiple CSS rules match the same element, the browser uses three layers of rules to pick the winner:
1. Origin
Where the CSS comes from:
- User agent styles (browser defaults)
- Author styles (your CSS files)
- User styles (for accessibility)
2. Importance
!important
beats normal rules- Author
!important
beats user normal - User
!important
beats everything
3. Specificity
How uniquely the rule targets the element
4. Source Order
If specificity is equal, the last rule wins
Example:
/* Rule 1 */
p {
color: red;
}
/* Rule 2 */
p {
color: green;
}
Rule 2 wins because it’s later in the file.
But if you add this:
p {
color: red !important;
}
Now Rule 1 wins, even though it’s earlier.
Section 4.2: Specificity Cheatsheet (With Real Examples)
CSS assigns weight to selectors using a 4-part value:
(a, b, c, d)
= inline, ID, class, tag
Selector | Specificity |
---|---|
* |
(0,0,0,0) |
div |
(0,0,0,1) |
ul li a |
(0,0,0,3) |
.nav-link |
(0,0,1,0) |
#header |
(0,1,0,0) |
style="..." |
(1,0,0,0) |
Rule of Thumb:
ID
selectors beatclass
selectorsClass
selectors beattag
selectorsInline styles
beat everything else!important
overrules all but it’s a last resort
Real World Example:
/* Specificity: (0,0,1,1) */
.card .title {
color: red;
}
/* Specificity: (0,1,0,0) */
#main {
color: blue;
}
The second rule wins (ID > class)
Simplifying Specificity with :where()
:where()
has zero specificity. It’s great for defaults.
:where(h1, h2, h3) {
margin: 0;
}
Use it in component libraries or resets to avoid fighting specificity.
Section 4.3: How Inheritance Works (And How to Reset It)
What is Inheritance?
Some CSS properties automatically pass down from parent to child, others don’t.
Inherited by default:
color
font-family
line-height
letter-spacing
visibility
Not inherited by default:
margin
,padding
,border
background
,position
,display
Overriding inheritance:
span {
color: inherit; /* takes from parent */
font-size: initial; /* resets to browser default */
line-height: unset; /* removes inherited or default */
}
inherit
→ explicitly take value from parentinitial
→ reset to CSS defaultunset
→ smart fallback: acts likeinherit
for inherited props, likeinitial
otherwise
Why Some Styles Don’t Apply
You wrote:
button {
font-family: 'Inter';
}
But the browser shows Times New Roman?
Check if:
- The button is using a user agent style
- A more specific selector overrides it
- The value wasn’t inherited: some elements (e.g., form controls) don’t inherit fonts by default
Fix:
button, input, select, textarea {
font-family: inherit;
}
Summary
-
The Cascade chooses the final rule using:
- Importance (
!important
) - Specificity (ID > class > element)
- Source order (later wins if equal)
- Importance (
-
Specificity is cumulative, more selectors = higher specificity
-
Inheritance is property-based, not all styles inherit
-
Use
inherit
,initial
, andunset
when needed, don’t guess
Learn More
Coming Up Next
In Chapter 5, we’ll cover positioning, z-index, and stacking contexts, the real reason your tooltip disappears behind the modal.
Let’s finally make sense of CSS layering.