IAnswerable

Tame Specificity with Cascade Layers

29 Oct 2026

The !important arms race happens because specificity is a blunt instrument. Two rules fight over a button color, one side wins with !important, six months later someone needs to override that and adds their own !important, and now you’ve got a stylesheet where half the rules are marked urgent. Cascade layers fix the actual problem: they let you control which group of styles wins, independent of how specific any individual selector is.

The core idea

@layer lets you name groups of rules and set their priority explicitly, in the order you declare the layer names — not in source order, not by specificity:

@layer reset, base, components, utilities;

@layer reset {
  * { margin: 0; padding: 0; }
}

@layer components {
  .button { background: blue; padding: 0.5rem 1rem; }
}

@layer utilities {
  .bg-red { background: red; }
}

Layers declared later win over layers declared earlier, regardless of selector specificity within them. So .bg-red in utilities beats .button in components, even though .button is arguably “more specific” in the class sense — because utilities is the later layer. That’s the actual win: a one-class utility can reliably beat a deeply nested component selector without a single !important.

Where it actually helps

The classic pain point is a component library (or Tailwind-style utilities) fighting your own CSS. Put third-party or reset CSS in an early layer, your components in the middle, and escape-hatch utility classes last:

@layer reset, vendor, components, utilities;

Anything outside a layer — unlayered CSS — beats every layered rule, no matter its specificity. That’s the one rule to memorize: unlayered always wins over layered. So if a third-party stylesheet isn’t layered, wrapping your own overrides in @layer won’t beat it; you’d need to import the vendor CSS into a layer too:

@import url("vendor.css") layer(vendor);

How the cascade actually resolves now

flowchart TD
    A["Two conflicting declarations"] --> B{"Both !important?"}
    B -->|Yes| C["Higher layer wins (reverse order)"]
    B -->|No, neither important| D{"Is one unlayered?"}
    D -->|Yes| E["Unlayered rule wins"]
    D -->|No, both layered| F["Later-declared layer wins"]
    C --> G["Tie in importance/layer?"]
    F --> G
    G --> H["Higher specificity wins"]
    H --> I["Later source order wins"]
  

Layer order is the tiebreaker that runs before specificity, not after. That’s the whole mental model shift: stop thinking “which selector is stronger” and start thinking “which layer wins.”

Practical rules

Verdict

Four layers — reset, base, components, utilities — covers most sites. Stop reaching for !important to win a cascade fight; reach for a layer instead. It’s the tool actually built for that problem.