devnotes.

Specificity Rules

Specificity is a scoring system that determines which CSS rule wins when multiple rules target the same element with equal source-order priority. It's commonly represented as four comma-separated numbers.

Scoring, from highest to lowest weight:

/* Inline style: always wins over any selector (not counted in this 4-part system) */

/* IDs: 1 point each */
#header { }               /* specificity: 0,1,0,0 */

/* Classes, attributes, pseudo-classes: 1 point each */
.nav-item { }              /* specificity: 0,0,1,0 */
[type="text"] { }          /* specificity: 0,0,1,0 */
:hover { }                 /* specificity: 0,0,1,0 */

/* Type selectors, pseudo-elements: 1 point each */
p { }                      /* specificity: 0,0,0,1 */
::before { }               /* specificity: 0,0,0,1 */

/* The universal selector (*) and combinators contribute 0 */
* { }                      /* specificity: 0,0,0,0 */

Combined selectors add up their individual scores:

#sidebar .card p { }
/* 1 ID + 1 class + 1 type = 0,1,1,1 — beats any rule below */

.sidebar .card p { }
/* 3 classes... wait, 2 classes + 1 type = 0,0,2,1 */

A rule with 0,1,1,1 beats a rule with 0,0,2,1 because the comparison happens left to right: first compare ID counts (1 vs 0 — the first rule already wins here), and only move to the next column if there's a tie.

Practical guidance to avoid specificity wars:

  • Prefer classes over IDs for styling (reserve IDs for JavaScript hooks and anchor links).
  • Avoid deeply nested selectors (.a .b .c .d) — they're hard to override later and tightly couple CSS to a specific HTML structure.
  • Avoid !important except as a last resort or in narrow utility classes designed to always win.
  • When two rules with the same specificity conflict, the one written later in the file wins — so file/import order matters too, not just specificity math.