In short
CSS specificity, the cascade and inheritance are the three rules the browser uses to decide which style wins when two rules target the same element. Learn them and "why is my CSS not working?" becomes easy to answer. What is the cascade in CSS?
CSS specificity, the cascade and inheritance are the three rules the browser uses to decide which style wins when two rules target the same element. Learn them and "why is my CSS not working?" becomes easy to answer.
What is the cascade in CSS?
Sometimes two or more rules try to set the same property on the same element. For example, one rule says a heading is blue and another says it is red. The browser cannot use both, so it needs a way to pick a winner. That process is called the cascade (the "C" in CSS stands for "Cascading").
The browser checks these things, in this order:
- Origin and importance – styles you write beat the browser's default styles. A declaration marked
!importantjumps ahead of normal ones. - Specificity – the more specific selector wins. An ID is more specific than a class, and a class is more specific than a tag name.
- Source order – if specificity is equal, the rule that comes last in the code wins.
Think of it like a talent show with three rounds. If one contestant wins a round, the show stops. Only a tie moves the contest to the next round.
How is CSS specificity calculated?
Specificity is a score with three columns, often written like 0-1-0. Each column counts a different kind of selector. A higher number in a left column always beats any number in a column to its right, so 1-0-0 beats 0-15-0.
| Selector | Example | Score (ID-Class-Type) |
|---|---|---|
| Type (tag) or pseudo-element | p, h1, ::before | 0-0-1 |
| Class, attribute or pseudo-class | .card, [type="email"], :hover | 0-1-0 |
| ID | #main | 1-0-0 |
| Two types | nav a | 0-0-2 |
| Class + type | p.intro | 0-1-1 |
| ID + class + type | #main .card h2 | 1-1-1 |
| Universal selector | * | 0-0-0 |
| Inline style | style="color: red" | Beats all selectors |
Combinators like >, + and the space do not add any points. Let's see specificity in action:
<article id="post" class="card">
<h2 class="title">Learning CSS</h2>
</article>
h2 {
color: black; /* 0-0-1 */
}
.card h2 {
color: green; /* 0-1-1 */
}
.title {
color: orange; /* 0-1-0 */
}
#post .title {
color: purple; /* 1-1-0 – this wins */
}
The heading is purple. #post .title has an ID, so its score 1-1-0 beats every other rule. Notice that .card h2 (0-1-1) beats .title (0-1-0) even though .title comes later, because specificity is checked before source order.
Tip: Keep specificity low and flat. If most of your selectors are a single class like
.cardor.button, they are easy to override later, and you will rarely fight the cascade.
When does source order decide?
When two selectors have the same score, the one written later wins. This is why you often put general styles at the top of a file and special cases below.
.button {
background: #2563eb;
color: white;
}
.button {
background: #16a34a;
}
Both selectors score 0-1-0, so the second rule wins and the button is green. The color: white from the first rule still applies, because the second rule does not set color. The cascade works property by property, not rule by rule.
How does inheritance work in CSS?
Inheritance means some properties pass down from a parent element to its children, like eye color passing from parents to kids. Most text properties inherit: color, font-family, font-size, line-height and text-align. Most box properties do not: margin, padding, border, width and background.
body {
font-family: system-ui, sans-serif;
color: #1f2937;
}
Every paragraph, list and heading inside body now uses this font and color, without you writing a rule for each one. An inherited value is always weaker than any rule that targets the element directly, even a plain p selector.
You can control inheritance with four special keywords that work on any property:
inherit– take the parent's value, even for properties that normally don't inherit.initial– reset to the property's default value from the CSS specification.unset– act likeinheritfor inherited properties and likeinitialfor the rest.revert– go back to the browser's default style for that element.
.card {
border: 2px solid #e5e7eb;
}
.card .inner {
border: inherit; /* copies the parent's border */
}
a {
color: inherit; /* links match the text around them */
}
The a { color: inherit; } trick is very common. Links stop being default blue and take the color of the paragraph or menu they sit in.
Why you should avoid !important
Adding !important after a value makes that declaration beat normal declarations, no matter the specificity:
.alert {
color: red !important;
}
It looks like an easy fix, but it creates a new problem. The only way to override an !important rule is with another !important rule, and soon your stylesheet becomes a battle that nobody can follow.
Common mistake: Using
!importantto "fix" a style that won't apply. Instead, open your browser's developer tools (right-click, then Inspect). The Styles panel shows every rule for the element, with the losing values crossed out. This tells you exactly which rule is winning and why.
There are a few fair uses, such as small utility classes like .hidden { display: none !important; } that must always work. Use it on purpose, never as a habit. You can read more on MDN's specificity page.
Practice exercise
Create a <ul class="menu"> with three <li class="item"> elements. Write three rules that set the color of the items: one using li, one using .item, and one using .menu li. Predict which color wins before you check in the browser. Then add a fourth rule that beats all three without using an ID or !important.
Hint: .menu li scores 0-1-1. A selector like .menu .item scores 0-2-0, which is higher because the class column counts first.
Key takeaways
- The cascade picks a winner by checking importance, then specificity, then source order.
- Specificity is scored in three columns: IDs, then classes/attributes/pseudo-classes, then types.
- When specificity is equal, the rule written last wins.
- Text properties like
colorandfont-familyinherit; box properties likemargindo not. - Avoid
!important; use developer tools to find out why a rule is losing.
Frequently asked questions
Does a class always beat an element selector?
Yes. One class (0-1-0) beats any number of type selectors (0-0-1, 0-0-2 and so on), because the class column is further left.
Do inline styles beat IDs?
Yes. A style attribute in the HTML beats any selector in your stylesheet. Only an !important declaration in the stylesheet can override it, which is one more reason to avoid inline styles.
What does :where() do to specificity?
The :where() pseudo-class always has a specificity of zero, so anything inside it is very easy to override. It is useful for writing default styles in a reset or a library.