Specificity and the Cascade
When several declarations set the same property on an element, the cascade picks one. It compares, in this order:
- Origin and importance: browser, user and author styles;
!importantreverses their order. - The
styleattribute beats any selector. - Cascade layers (
@layer): later layers win; unlayered styles beat all layers (without!important). - Specificity: (id, class, type), compared left to right.
- Source order: the last declaration wins.
A property that is not set at all is inherited from the parent (text
properties such as color, font) or takes its initial value (margin,
border).
Details
Specificity
Specificity is three numbers, (A, B, C), compared left to right: one id beats any number of classes.
| Counts as | Example | Specificity |
|---|---|---|
| A: id | #header | (1, 0, 0) |
| B: class, attribute, pseudo-class | .btn, [type='text'], :hover | (0, 1, 0) |
| C: type, pseudo-element | p, ::before | (0, 0, 1) |
Nothing: *, combinators (>, +, ~, space) | ul > li: only the two types count | (0, 0, 2) |
:is(), :not(), :has(): their most specific argument | :is(#a, .b) p | (1, 0, 1) |
:where(): always zero | :where(#a, .b) p | (0, 0, 1) |
Nesting &: like :is() around the parent selector | #a, .b { & p {} } = :is(#a, .b) p | (1, 0, 1) |
:nth-child(… of S): the pseudo-class plus the most specific in S | :nth-child(2 of .item) | (0, 2, 0) |
- The
styleattribute is not specificity: it is a separate, earlier step of the cascade and beats any selector without!important. :where()is for library and reset styles that any user selector should be able to override.- Because nesting works like
:is(),&takes the specificity of the most specific selector in the parent list, even for elements matched by the less specific one.
Origin and !important
From the weakest to the strongest:
- Browser (user-agent) styles.
- User styles (browser settings, a user stylesheet).
- Author styles: the site's CSS.
- CSS animations (
@keyframes). - Author
!important. - User
!important. - Browser
!important. - CSS transitions.
!important lifts a declaration into a separate, stronger group, and inside it
the order of origins is reversed: user !important beats author
!important, so the user's accessibility settings win.
Cascade Layers
@layer reset, base, components, utilities;
- Layers are ordered by first appearance; one
@layer a, b, c;statement up front fixes the order. Later layers win, whatever the specificity inside them:.btninutilitiesbeats#id .btnincomponents. - Unlayered styles beat all layers. Third-party CSS can be put into a low
layer with
@import url(lib.css) layer(vendor);. !importantreverses the order of layers: an!importantdeclaration in the earliest layer wins, and layered!importantbeats unlayered!important. This lets a reset protect a rule that must not be overridden.- Nested layers (
@layer components.button) and anonymous layers are possible.
@scope
@scope (.card) to (.card-content) { img { … } } applies the rules only inside
.card but not inside .card-content (the limit itself is excluded too); when
specificity is equal, the rule from the closer scope root wins (this step comes
before source order). Browser support: Baseline 2026, newly available.
Inheritance and the Global Keywords
- Inherited properties: mostly text —
color,font-*,line-height,text-align,visibility,cursor, and all custom properties. Not inherited: box and layout —margin,padding,border,width,background,display,position. - Form controls do not inherit
fontby default: the browser stylesheet sets it. A reset addsfont: inherittobutton, input, select, textarea.
| Keyword | Value |
|---|---|
inherit | the parent's computed value |
initial | the initial value from the spec (display: initial is inline) |
unset | inherit for an inherited property, initial for the rest |
revert | rolls back to the previous origin: usually the browser's default style |
revert-layer | rolls back to the previous cascade layer (from unlayered styles, to the layered ones); with no layer left, works as revert |
all: … | sets every property except direction, unicode-bidi and custom ones |
Example
@layer base, components;
@layer components {
/* (1, 1, 0), but loses to .btn below: unlayered styles beat layers */
#app .btn {
color: red;
}
}
/* Unlayered: beats the layered rule despite lower specificity */
.btn {
color: green;
padding: 4px;
}
/* (0, 1, 1): beats .btn (0, 1, 0), so the padding is 8px */
button.btn {
padding: 8px;
}
/* (0, 0, 1): any class overrides it */
:where(.card, .panel) button {
font-weight: bold;
}
Follow-up Questions
- Why avoid
!important? Only another!importantcan beat it (more specific, later in the source or in an earlier layer), so it escalates. Layers and:where()solve the same problem;!importantstays for utilities and overriding third-party inline styles. - What is the specificity of
nav ul li a.active:hover? (0, 2, 4). Of#main :not(.x) p? (1, 1, 1).
Common Mistakes
- "Specificity is a single number: id 100, class 10." The columns never overflow into each other.
- "A higher layer loses to more specific selectors in a lower layer." Layer order is checked before specificity.
- "Inline styles have specificity (1, 0, 0, 0)." They are a separate step of the cascade; the result is the same, but it explains why layers do not affect them.
- Expecting
!importantin a later layer to win: with!importantthe earlier layer wins.
See Also
- CSS Selectors
- Box Model
- CSS Custom Properties
- Styling in React: CSS Modules, BEM, CSS-in-JS
- MDN: Cascade