Specificity

How the cascade resolves conflicting rules using specificity scoring

Overview

When multiple rules target the same element, specificity decides which declaration wins. Specificity is scored as three components — IDs, classes/attributes/pseudo-classes, and element/pseudo-element selectors — compared left to right. Inline styles and !important sit above the normal cascade, so understanding this hierarchy is key to predictable styling.

Syntax / Usage

Each selector earns a score (a, b, c): a counts IDs, b counts classes, attribute selectors, and pseudo-classes, c counts type selectors and pseudo-elements. Higher components win regardless of source order; ties fall back to the last rule declared.

/* (0, 0, 1) */
p { color: gray; }

/* (0, 1, 1) — wins over the above */
p.intro { color: black; }

/* (1, 0, 0) — beats any number of classes */
#lede { color: navy; }

Examples

A more specific selector overrides an earlier one despite ordering:

.button { background: blue; }          /* (0, 1, 0) */
.sidebar .button { background: red; }  /* (0, 2, 0) wins */

Using :where() to add selectors with zero specificity, keeping overrides easy:

/* :where() contributes (0, 0, 0) */
:where(.card) a {
  color: inherit;
}

/* A plain class still overrides the link above */
.card .link { color: teal; }

!important overrides normal declarations but should be a last resort:

.alert { color: black !important; }

Common Mistakes

  • Reaching for !important instead of fixing selector specificity, creating override wars
  • Assuming later rules always win — a lower-specificity later rule loses to an earlier higher one
  • Overusing ID selectors, which are hard to override later
  • Forgetting that inline style attributes outrank almost every stylesheet rule
  • Treating specificity components as a single number (they never carry over between columns)

See Also

selectors custom-properties box-model