CSS Tutorial · Beyond the Basics · Lesson 16 of 19

CSS Cascade and Specificity

How the browser decides which rule wins.

When rules conflict

Several rules can target the same element. The browser picks the winner in this order:

  1. Importance — !important beats normal declarations (avoid it).
  2. Specificity — the more specific selector wins.
  3. Source order — if specificity is equal, the later rule wins.

Specificity

Count selectors as (id, class, type):

Selector Specificity
p (0, 0, 1)
.note, [type], :hover (0, 1, 0)
p.note (0, 1, 1)
#intro (1, 0, 0)
inline style="" beats all selectors
Example 1Try it yourself
<style>
  body { font: 15px system-ui; color: #334155; }       /* inherited by everything */
  p { color: #16a34a; }                                /* (0,0,1) */
  .note { color: #2563eb; }                            /* (0,1,0) beats p */
  p.note { color: #7c3aed; }                           /* (0,1,1) beats .note */
  #special { color: #dc2626; }                         /* (1,0,0) beats classes */
  .late { color: #ea580c; }
  .late { color: #0891b2; }                            /* same specificity: last one wins */
</style>
<p>Type selector: green</p>
<p class="note">p.note: purple</p>
<p class="note" id="special">id selector: red</p>
<p class="late">Later rule wins: cyan</p>
<span>Inherited body colour</span>

Inheritance

Some properties — mostly text ones like color, font-family, line-height — are inherited by children. Others like margin, border and background are not.

Keep specificity low and flat (single classes), and you'll rarely fight the cascade. :where() has zero specificity, handy for defaults.

Test yourself

Which selector is most specific?

  1. .nav a
  2. #nav
  3. nav ul li a
  4. a:hover
Show answer

Answer: #nav