CSS Specificity Deep Dive

Difficulty: Intermediate

Question

How does CSS specificity work? How do you calculate it?

Answer

Specificity determines which CSS rule wins when multiple rules target the same element.

Calculation (A, B, C): - A: Number of ID selectors - B: Number of class selectors, attribute selectors, pseudo-classes - C: Number of element selectors, pseudo-elements

Cascade order (highest to lowest priority): 1. !important declarations 2. Inline styles (style attribute) 3. ID selectors (#id) 4. Class/attribute/pseudo-class selectors 5. Element/pseudo-element selectors 6. Universal selector (*) - zero specificity

Equal specificity: last rule in source order wins. Inheritance: some properties inherit (color, font), others don't (margin, padding).

Code examples

Specificity Calculation Examples

/* (0, 0, 1) - one element */
p { color: black; }

/* (0, 1, 0) - one class */
.text { color: blue; }

/* (0, 1, 1) - one class + one element */
p.text { color: green; }

/* (1, 0, 0) - one ID */
#main { color: red; }

/* (0, 2, 1) - two classes + one element */
.container .text p { color: purple; }

/* (1, 1, 1) - one ID + one class + one element */
#main .text p { color: orange; }

/* (0, 1, 0) - one pseudo-class */
:hover { color: pink; }

/* (0, 0, 2) - one element + one pseudo-element */
p::first-line { color: gray; }

/* Winner for <p class="text" id="main">:
   #main (1,0,0) beats .text (0,1,0)
   beats p (0,0,1) */

Compare specificity left to right: (1,0,0) always beats (0,99,99). IDs always beat classes, classes always beat elements.

Cascade and Inheritance

/* Cascade: same specificity, last wins */
.btn { color: red; }
.btn { color: blue; } /* blue wins */

/* Inheritance: some props inherit */
.parent {
  color: navy;       /* inherits to children */
  font-family: sans-serif; /* inherits */
  border: 1px solid; /* does NOT inherit */
  padding: 1rem;     /* does NOT inherit */
}

/* Force inheritance */
.child {
  border: inherit; /* explicitly inherit */
}

/* Reset to default */
.reset {
  color: initial;  /* browser default */
  color: unset;    /* inherit if inheritable, initial if not */
  all: unset;      /* reset ALL properties */
}

/* Inherited properties:
   color, font-*, text-*, line-height,
   visibility, cursor, list-style

   NOT inherited:
   margin, padding, border, background,
   width, height, display, position */

Inheritance flows from parent to child for text-related properties. Layout properties like margin and padding never inherit.

Avoiding Specificity Wars

/* BAD: specificity escalation */
.nav .menu .item a { color: blue; }
.nav .menu .item a.active { color: red; }
/* Need even more specificity to override? */
#nav .menu .item a.active { color: green; }
/* This spirals out of control! */

/* GOOD: flat, low specificity */
.nav-link { color: blue; }
.nav-link--active { color: red; }

/* AVOID !important (hard to override) */
.text { color: red !important; }
/* Only way to override: */
.text { color: blue !important; } /* specificity war */

/* Acceptable uses of !important: */
/* 1. Utility classes */
.hidden { display: none !important; }
/* 2. Overriding third-party CSS */
.third-party-widget {
  margin: 0 !important;
}

Keep specificity low and flat. BEM naming (.block__element--modifier) helps avoid nesting. Only use !important for utilities or third-party overrides.

Key points

Concepts covered

Specificity, Cascade, Inheritance, !important, Specificity Calculation