Skip to main content

Cascade Layers & Specificity Without Tears

intermediate13 min readLesson 79 of 143

@layer to order the cascade deliberately, specificity math, :where() for zero-specificity resets, and isolation strategies.

The cascade is not an accident to fight — it is a system to order. @layer gives you explicit control.

Layers beat specificity

Declarations in a later layer win over earlier ones — regardless of specificity:

@layer reset, base, components, utilities;

@layer reset {
  * {
    margin: 0;
  }
}
@layer components {
  .btn {
    padding: 0.5rem 1rem;
  }
}
@layer utilities {
  .p-0 {
    padding: 0 !important-free;
  } /* utilities beat components */
}

(That comment is a marker, not syntax — utilities win because the layer is declared later.) A .p-0 utility now overrides component padding without !important, because utilities outranks components by layer order.

The ordering rule

Layer order is fixed at first declaration: @layer reset, base, components, utilities; sets the priority low→high. Un-layered styles beat all layered styles — that is how overrides ship last.

:where() — specificity you can spend

:where(...) contributes zero specificity, perfect for resets that must always lose:

:where(h1, h2, h3) {
  margin-block: 0.5em;
}

Any author rule now beats it. :is() is the same list but takes the highest specificity of its arguments — use it when the match should count.

When to reach for each

  • Layers: organize whole stylesheets; make third-party CSS overridable.
  • :where(): resets and defaults that must be cheap to override.
  • !important: last resort, then only for utility layers by convention.