Cascade, Specificity & Inheritance
Sooner or later you will write a rule that "does not work". Usually the rule is fine: another rule is simply winning. CSS has a clear, predictable process for choosing between conflicting declarations, and once you know it you can fix these problems in seconds instead of guessing.
This lesson explains the cascade, how specificity is scored, where !important fits, and how inheritance fills in values you never set.
The cascade in short
For every element and every property, the browser collects all the declarations that apply. If they disagree, it compares them in this order and stops at the first step that produces a winner:
- Origin and importance. Your style sheets beat the browser's defaults. A declaration marked
!importantbeats normal declarations. - Inline styles. A declaration in a
styleattribute beats any selector-based rule. - Cascade layers. When you use
@layer, later layers beat earlier ones. You will learn this in Cascade Layers. - Specificity. The more specific selector wins.
- Order of appearance. If everything else is equal, the declaration that comes last wins.
Most everyday conflicts are decided at step 4 or step 5, so those are the ones to understand well.
Order of appearance
When two selectors are equally specific, the later one wins. This also applies across files: a style sheet linked later in the <head> comes after one linked earlier.
.notice { color: navy; }
.notice { color: teal; } /* this one wins: same specificity, comes later */Specificity
Specificity is a score the browser gives each selector. Think of it as three columns, written as (A, B, C):
- A: the number of ID selectors.
- B: the number of classes, attribute selectors and pseudo-classes.
- C: the number of type selectors and pseudo-elements.
The universal selector * and combinators (space, >, +, ~) add nothing.
| Selector | Score (A, B, C) |
|---|---|
p | (0, 0, 1) |
nav a | (0, 0, 2) |
.card | (0, 1, 0) |
.card p::first-line | (0, 1, 2) |
a:hover | (0, 1, 1) |
input[type="email"] | (0, 1, 1) |
#login .btn | (1, 1, 0) |
To compare two scores, look at column A first. The higher A wins, no matter what is in B and C. Only if A is equal do you compare B, and then C. So one ID beats any number of classes: (1, 0, 0) beats (0, 15, 0). These are not ordinary decimal numbers; a column never "carries over" into the next.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Specificity</title>
<style>
body { font-family: system-ui, sans-serif; padding: 1rem; }
#fees .amount { color: green; } /* (1, 1, 0) */
.box .amount { color: orange; } /* (0, 2, 0) */
p.amount { color: purple; } /* (0, 1, 1) */
.amount { color: red; } /* (0, 1, 0), last but weakest */
</style>
</head>
<body>
<div id="fees" class="box">
<p class="amount">Semester fee: ₹18,500</p>
</div>
</body>
</html>The text is green. Even though the red rule comes last, #fees .amount has an ID, so it wins at the specificity step and order of appearance is never reached. Delete the first rule and run again: now .box .amount (0, 2, 0) wins over p.amount (0, 1, 1), because B is compared before C.
Pseudo-classes that take selectors
:is(), :not() and :has() take the score of the most specific selector inside their brackets. :where() always scores zero, which makes it handy for default styles that are easy to override. All four are covered in :has(), :is() and :where().
!important
Adding !important after a value makes that declaration jump ahead of all normal declarations:
.hidden {
display: none !important;
}This is acceptable for a small number of utility classes whose whole job is to always win, like the one above. Using it to win ordinary battles is a trap: the only way to beat an !important is another !important, and soon your style sheet becomes a fight nobody can follow. When a rule loses, first find out why it loses, then fix the selector or the order.
Inheritance
Some properties pass their value from a parent element to its children. This is inheritance, and it is why setting font-family once on body changes the font of the whole page.
| Usually inherited | Not inherited |
|---|---|
color | margin and padding |
font-family, font-size, font-weight | border |
line-height | background |
text-align | width and height |
visibility, cursor | display |
Text-related properties inherit; box and layout properties do not. Imagine the mess if every child copied its parent's border.
An important rule: an inherited value always loses to a declaration that targets the element directly, even a weak one. Specificity only compares rules that match the same element.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Inheritance</title>
<style>
body { font-family: system-ui, sans-serif; padding: 1rem; }
#results { color: darkred; border: 2px solid darkred; padding: 0.5rem; }
em { color: steelblue; }
</style>
</head>
<body>
<section id="results">
<h2>Class 12 results</h2>
<p>Priya scored <em>96 per cent</em> in Physics.</p>
</section>
</body>
</html>The heading and paragraph inherit dark red from the section, but the <em> is steel blue. The em rule is weak, yet it matches the <em> directly, while the ID rule only reaches it through inheritance. Notice also that the border is drawn once around the section; the children do not inherit it.
Controlling inheritance
Every property accepts these keywords:
inherit: take the parent's value, even for a property that does not normally inherit.initial: use the property's starting value from the CSS specification (forcolor, that is usually black, not the browser's default for that element).unset: act likeinheritfor inherited properties andinitialfor the others.revert: go back to the browser's default style for that element.
button {
font: inherit; /* buttons do not inherit fonts by default */
color: inherit;
}Form controls are the classic case: browsers give them their own font, so font: inherit makes buttons and inputs match the rest of your page.
Common mistakes
- Fighting with
!important. Find the winning rule in the developer tools and lower its specificity instead. - Over-qualified selectors.
body div.container ul li ais long and very specific..nav-linkis easier to read and easier to override. - Expecting specificity to beat inheritance. A direct rule on the child always wins over an inherited value.
- Thinking 11 classes beat one ID. Columns never carry over; one ID wins.
Key points
- The cascade compares origin and importance, inline styles, layers, specificity and finally order of appearance.
- Specificity is scored as (IDs, classes/attributes/pseudo-classes, types/pseudo-elements) and compared column by column.
- When specificity is equal, the declaration that appears last wins.
!importantoverrides normal declarations; keep it for rare utility classes.- Text properties like
colorandfont-familyinherit; box properties likemarginandborderdo not. inherit,initial,unsetandrevertlet you control inheritance per property.
Exercise
Write the specificity score for each selector, then decide which colour a <a class="nav-link"> inside <nav id="top"> would get: nav a { color: black; }, .nav-link { color: blue; }, #top a { color: green; }, nav .nav-link { color: orange; }. Check your answer by building the page in the Try-it editor.
Show solution
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Which colour wins?</title>
<style>
nav a { color: black; } /* (0, 0, 2) */
.nav-link { color: blue; } /* (0, 1, 0) */
#top a { color: green; } /* (1, 0, 1) */
nav .nav-link { color: orange; } /* (0, 1, 1) */
</style>
</head>
<body>
<nav id="top"><a class="nav-link" href="#">Home</a></nav>
</body>
</html>The link is green. #top a is the only selector with an ID, so it wins in column A before classes or order are considered.
Quick Quiz
1. What is the specificity of .menu li:hover?
.menu and :hover each add to column B, and li adds to column C.
2. Two rules have exactly the same specificity. Which one wins?
When everything else is equal, order of appearance decides and the later declaration wins.
3. A section with an ID sets color: red. A rule strong { color: blue; } exists. What colour is a <strong> inside the section?
Specificity only compares rules that match the same element; inherited values lose to any direct rule.
4. Which property is NOT inherited by default?
Box properties such as padding, margin and border are not inherited.
Learn CSS with a mentor
Free lessons get you started. Our Full Stack Web Development training adds real projects and code reviews from working developers.
- Project-based learning
- Mentor code reviews
- Free counselling call
