Reference guide · html-css-js · Published 2026-08-15 · 3 min read
CSS specificity: which rule wins
CSS specificity explained: inline styles, IDs, classes and elements, the cascade, and the cost of using !important.
- ·Specificity table
- ·Worked example
- ·Avoid important
What specificity means
When two CSS rules target the same property of the same element, the browser has to pick one. It picks the selector with the higher specificity. Specificity is a four-part score, counted in the order: inline styles, IDs, classes and attributes, element and pseudo-element types.
| Selector kind | Weight | Example |
|---|---|---|
Inline style attribute | Highest | style="color: red" |
| ID selector | 1,0,0 | #nav |
| Class, attribute, pseudo-class | 0,1,0 | .card, [type="email"], :hover |
| Element, pseudo-element | 0,0,1 | div, ::before |
| Universal, combinators | No weight | *, >, + |
A selector with one ID beats any number of classes. That is the part people misjudge: .btn never overrides #submit-btn even with hundreds of rules behind it.
The worked example
Compare these rules on the same <button id="save" class="btn primary">.
button { color: gray; } /* 0,0,1 */
.btn { color: blue; } /* 0,1,0 */
.btn.primary { color: green; } /* 0,2,0 */
#save { color: red; } /* 1,0,0 */
The winning color is red. The ID rule scores 1,0,0, and nothing else in the list touches an ID. Two classes together score 0,2,0, which beats a single class but loses to the ID. If you added style="color: purple" to the markup, the inline style would beat the ID rule.
Where the cascade matters
Specificity is not the only deciding factor. Three more rules affect the outcome.
- Source order: when specificity ties, the rule declared later in the CSS wins.
- Inheritance: some properties inherit from the parent. A child rule beats the inherited value on its own element, and specificity decides between competing child rules.
!importantmoves a declaration to its own lane: it beats normal rules from any specificity, and tie-breaks inside!importantstill follow specificity and source order. An inline!importantbeats a stylesheet!important.
Why !important is a trap
!important works exactly once. The next developer hits the same conflict and adds another !important. The stylesheet fills with them, specificity stops holding any meaning, and every new component inherits the mess. There are two places where it is normally fine: overriding user agent styles you cannot reach otherwise, and the few cases where a third-party script writes inline styles you must beat. Everywhere else, treat it as a code smell.
/* Rethink these */
.note { color: #111 !important; }
/* Prefer fixing the selector that is too strong */
.alert .note { color: #111; }
Debugging with devtools
When a style is not applying, the computed styles panel in DevTools is the evidence. Select the element, open the Styles tab, and the browser lists every matching rule with its origin, specificity, and a strike-through on the declarations that lost. If the property you expect is struck through, look for the rule that beat it further up the list. From there the fix is honest: either lower the specificity of the winning selector, raise yours, or remove the inline style that is sitting on the element. Renaming #nav to .nav alone often removes a whole class of conflicts, which is the direction the CSS versus inline styles comparison takes. The remaining conflicts usually reset to the box model, where margins and padding fail for separate reasons.