Articles /

new-feature

CSS specificity explained

Stop guessing why your styles lose. Learn how specificity is calculated and how modern CSS gives you real control over the cascade.

The specificity algorithm

Every selector gets a three-part score: (ID, Class, Element). Higher left columns always win, regardless of how many lower-level selectors you stack.

/* (0, 0, 1) — one element */
p { color: oklch(50% 0 0); }

/* (0, 1, 0) — one class */
.text { color: oklch(55% 0.1 250); }

/* (1, 0, 0) — one ID */
#hero { color: oklch(60% 0.2 280); }

/* (1, 1, 1) — combined */
#hero .text p { color: oklch(40% 0 0); }

:is() inherits the highest specificity

:is() takes the specificity of its most specific argument. This is powerful but can surprise you when mixing IDs and classes.

/* specificity = (1, 0, 0) because #main is in the list */
:is(#main, .sidebar, footer) p {
  color: oklch(45% 0.15 200);
}
If you only need grouping without specificity inflation, reach for :where() instead.

:where() — zero specificity

:where() works identically to :is() but contributes zero specificity. This makes it ideal for default styles that consumers can easily override.

/* (0, 0, 0) — trivially overridable */
:where(.card, .panel) {
  padding: 1.5rem;
  border: 1px solid oklch(80% 0 0);
}

/* a single class override wins */
.card { padding: 2rem; }

@layer and the cascade

Cascade layers let you group rules so that layer order — not specificity — decides the winner between layers. Unlayered styles always beat layered ones.

@layer reset, base, components, utilities;

@layer reset {
  * { margin: 0; box-sizing: border-box; }
}

@layer utilities {
  .text-center { text-align: center; }
}

The !important escape hatch

!important reverses layer order: an !important rule in an earlier layer beats one in a later layer. This means reset-layer !important rules are nearly impossible to override — by design.

  • Avoid !important in component code.
  • Reserve it for utility classes and accessibility overrides.
  • Prefer @layer ordering to win specificity battles cleanly.

Practical tips

Keep specificity flat across your project. A class-only selector strategy avoids most conflicts.

  • Use :where() for base / default styles.
  • Use :is() sparingly, and never with IDs inside.
  • Wrap third-party CSS in its own @layer.
  • Check specificity in DevTools — Chrome and Firefox both display it.