Overview
@layer declares named buckets with an explicit precedence. Specificity decides within a layer; layer order decides between layers. Cascade layers have been Baseline widely available since September 2024. The umbrella page is css.
Declare layer order once, at the top
One statement fixes the order; later layers win regardless of specificity. A normal declaration outside any layer beats every layered one, so reserve that lane for emergency overrides and document why.
@layer reset, base, components, utilities, overrides;
@layer base { :root { --color-ink: oklch(0.2 0 0); } }
@layer components { .card { padding: var(--space-3); } }
@layer utilities { .text-center { text-align: center; } }A useful stack: reset (box-sizing, focus defaults), base (tokens, element defaults), components, utilities, overrides (page-level patches). Add lanes such as vendor to the declaration line rather than letting order drift.
Quarantine third-party CSS in a lower layer
Most cascade pain is a vendor stylesheet whose specificity outranks yours. Import it into a layer below your code.
@layer vendor, reset, base, components, utilities;
@import url("/static/vendor/calendar.css") layer(vendor);Your .calendar-day rule in components now wins over the vendor’s .calendar .day, with no !important and no selector escalation. An unlayered @import is the usual cause when this fails.
Retire !important from app code
!important reverses the layer order: an important declaration in an earlier layer beats an important one in a later layer, and any important declaration beats a normal one. A single !important in components therefore outranks normal rules in utilities.
- App rules: move the rule to a later layer instead.
- Utility classes: no
!important; their layer already wins. - Library or print stylesheets that must defeat host CSS: acceptable, isolated to one file.
Keep specificity and order separate
Inside one layer, specificity applies: an id beats a class. Across layers, only order matters, so #sidebar a in base loses to .link in utilities.
Order Tailwind v4’s layers explicitly
@import "tailwindcss" declares @layer theme, base, components, utilities;. A first @layer statement fixes order, so put yours before the import and slot custom layers where they belong. A list placed after the import appends new layers after utilities, where they win.
@layer theme, base, vendor, components, utilities;
@import "tailwindcss";Unlayered CSS (for example a global reset) beats Tailwind’s utilities; put it in @layer base. Component libraries that ship utility class strings, such as shadcn, need no layer of their own.
Debug layers in DevTools
Chrome and Firefox DevTools show the layer name beside each rule in the Styles panel. If a rule is struck through and another layer’s rule wins, move the rule to a later layer; do not escalate the selector.
Avoid common pitfalls
- Anonymous
@layer { }blocks. Each creates a separate unnamed layer in source order. Name layers so the order line is the source of truth. - Tokens in
components. Define tokens inbaseso every later layer reads them; see css-custom-properties. - Legacy
!important. Audit it when adopting layers; it inverts the order for that property.