Specificity: counted, not vibed
When two selectors target the same element
Whichever has higher specificity wins. Specificity is a 4-part score:
(inline, IDs, classes/attrs/pseudo-classes, elements/pseudo-elements)
Read left-to-right; the first non-equal column decides. Each column is just a count.
button β (0, 0, 0, 1) .cta β (0, 0, 1, 0) .cta:hover β (0, 0, 2, 0) /* :hover is a pseudo-class */ #main button.cta β (0, 1, 1, 1) .parent .cta β (0, 0, 2, 0)
#main button.cta beats .cta:hover because column 2 (IDs) is 1 vs 0 β and ID wins even though :hover had more class-level pieces.
When specificity ties
The later rule in source order wins. So if two rules with identical specificity target the same element, last one written wins.
Inline is a separate origin, not a specificity row
<p class="alert" style="color: red">β¦</p>
You'll see older articles write style="" as specificity (1, 0, 0, 0) and call inline "the highest row". The modern CSS Cascade spec describes inline styles as a separate origin in the cascade β they sit between author rules and !important author rules and win against any normal author selector regardless of how many IDs you stack. Practical takeaway is the same β inline beats #a #b #c β but the *reason* on an interview question is "origin", not "row 1 specificity". The only thing that beats inline is !important (next lesson).
Calculate this in your head
For section#main article.post.featured h2:first-of-type:
- IDs: 1 (
#main) - Classes/attrs/pseudo-classes: 3 (
.post,.featured,:first-of-type) - Elements: 3 (
section,article,h2) - Score:
(0, 1, 3, 3)
Compare to .featured alone = (0, 0, 1, 0). The first wins by a mile (ID column 1 vs 0).
The lesson
Don't reach for !important to win a fight. Increase your specificity by one class level, or rewrite to use the same level as the rule you're trying to override.