Why your rule lost
When two rules set the same property on the same element, the browser resolves it in a fixed order. Learn the order and "CSS is unpredictable" stops being true.
- Origin and importance. Your stylesheet beats the browser default. An
!importantdeclaration beats a normal one — and an!importantin the browser's own user-agent sheet beats yours. - Cascade layers. A declaration in a later
@layerbeats one in an earlier layer, whatever the selectors say. - Specificity. The more specific selector wins.
- Order. Same specificity? The one written later wins.
Specificity, counted in three columns
Count each selector as three numbers: ids, then classes, attributes and pseudo-classes, then elements and pseudo-elements. Compare left to right; the first column that differs decides it. It is not a sum — no number of classes ever beats one id.
| Selector | Specificity |
|---|---|
p | 0-0-1 |
.card | 0-1-0 |
.card p | 0-1-1 |
.card p.lede | 0-2-1 |
#main | 1-0-0 |
#main .card p.lede span em b i | 1-2-6 |
*, :where(…) | 0-0-0 |
:is(.card, #main) | 1-0-0 — the strongest argument |
an inline style="" | beats every selector |
Compare ids first. If they tie, compare classes. If those tie, compare elements. Only if all three tie does source order matter.
It resolves per property, not per rule
A "losing" rule is not discarded. Only the individual declarations that conflict are overridden.
.card {
color: #333;
padding: 16px;
}
#main .card {
color: #000;
}
/* the card ends up with color #000 AND padding 16px */
Inheritance
Some properties pass down the tree on their own: color, font-family, font-size, line-height, text-align, visibility, cursor, list-style. Most do not: padding, border, background, width, display, position.
Inheritance is weaker than any matching rule. A single p { color: black } beats a colour inherited from body, no matter how specific the body rule was.
.child {
color: inherit; /* take the parent's value, even for a non-inherited property */
border: initial; /* the property's own default */
margin: unset; /* inherit if inheritable, otherwise initial */
padding: revert; /* back to what the browser's own stylesheet said */
}
Cascade layers
@layer separates concerns so specificity fights stop happening. Layers are ordered by where they are first declared, and every declaration in a later layer beats every declaration in an earlier one — even #id in the earlier layer against a single class in the later one.
@layer reset, base, components, utilities;
@layer base {
#main p { color: #555; } /* specificity 1-0-1 */
}
@layer utilities {
.muted { color: #999; } /* specificity 0-1-0 — and it wins */
}
Unlayered CSS beats all layers. That is the mechanism for bringing in a third-party stylesheet you want to be easy to override: @import url(theirs.css) layer(vendor);
!important
!important lifts a declaration above the normal cascade. It is almost always a sign that something else is wrong — an id used for styling, an inline style, or a stylesheet you do not control.
Its real cost is that the only way to beat it is another !important, and then you are ordering those by specificity. Two rounds of that and the stylesheet is unmaintainable.
- Legitimate uses: overriding an inline style you cannot remove, a utility class that must always win, or a third-party widget.
- Before reaching for it: check whether you are fighting an id, and whether a cascade layer would settle it properly.
The full order, once more
The reversal at step 4 catches people out: inside !important, an earlier layer wins. It is deliberate — it means a reset layer can force something that no component layer can undo.
1. Browser default stylesheet
2. Your normal declarations, by layer order, then specificity, then source order
3. Inline style=""
4. Your !important declarations (layer order is REVERSED here)
5. The browser's own !important rules, and the user's
Reading it in devtools
- The Styles pane lists every matching rule, strongest first.
- A struck-through property lost. Scroll up to find the rule that beat it.
- Hover a selector and devtools shows its specificity.
- The Computed pane shows the final value with an arrow to the rule that produced it — the fastest answer to "where is this coming from".