Layer ordering strategy
Declare all cascade layers up front with @layer to lock in their priority order — no specificity battles required.
Conflict example — color
Same selector, different layers
.card
Quick implementation
/* Declare all layers in priority order — lowest to highest.
This single statement controls the entire cascade. */
@layer reset, base, components, theme, utilities;
/* Place CSS into each layer */
@layer reset {
*, *::before, *::after { box-sizing: border-box; }
body { margin: 0; }
}
@layer base {
body {
font-family: system-ui, sans-serif;
line-height: 1.6;
}
}
@layer components {
.card {
background: var(--card);
padding: 1rem;
border-radius: 0.5rem;
}
}
@layer theme {
.card {
background: var(--brand-surface);
}
}
@layer utilities {
.p-0 { padding: 0; }
.bg-accent { background: var(--accent); }
}
Prompt this to your LLM
Includes role, constraints, two framework variants, and edge cases to handle.
You are a senior CSS architect setting up cascade layer ordering
for a design system.
Goal: Write a complete @layer ordering strategy that controls CSS
priority across five layers — reset, base, components, theme, and
utilities — using a single up-front declaration.
Technical constraints:
- Open the stylesheet with: @layer reset, base, components, theme, utilities;
This one statement permanently defines priority order regardless of
where in the file each @layer block appears later.
- Use oklch() for all color values — no hex or rgba.
- Use CSS custom properties (var(--card), var(--accent), etc.) for tokens.
- Keep each layer's responsibility narrow:
reset → browser default normalization only
base → global typography, :root tokens
components → named UI patterns (.card, .btn, .nav)
theme → brand/color overrides over components
utilities → single-property helpers (.mt-4, .text-center)
- Do NOT place any styles outside a layer block unless you intend them
to be unlayered (unlayered styles always win over layered ones).
Framework variant (pick one):
A) Vanilla CSS — one stylesheet with all five @layer blocks.
B) CSS Modules (Next.js) — each layer exported from its own file,
imported in order into a root layout module.
Edge cases to handle:
- Third-party CSS imported without a layer will be unlayered and will
override everything. Wrap it: @layer vendor { @import "lib.css"; }
- Nested layers use dot notation: components.forms, components.buttons.
Declare them inside the parent: @layer components { @layer forms, buttons; }
- @layer declarations cannot be conditional — place the ordering
declaration before any @media or @supports blocks.
Return CSS only (or module files if variant B is chosen).
Why declaration order is everything
When you write @layer reset, base, components, theme, utilities; at the top of your stylesheet, you are permanently fixing the priority of every rule in those layers — regardless of where in the file each @layer block appears, and regardless of selector specificity. A .card rule in utilities beats a #main .featured-card rule in reset, even though the ID selector has far higher specificity. Layer order trumps specificity.
The mechanism is the one-line declaration. The first time the browser encounters @layer reset, base, components, theme, utilities;, it records the layer order. Every subsequent @layer block is sorted into that position. If you later write @layer utilities { … } before @layer reset { … } in the file, it does not matter — the declared order stands.
This is the core value: you can split layers across multiple files, imported in any order, and the cascade priority is always exactly what the declaration says.
Unlayered styles always win
Any CSS written outside a @layer block is considered unlayered. Unlayered styles sit above all layers in the cascade — they beat even utilities, regardless of specificity. This means that if you pull in a third-party library without wrapping it in a layer, it will override everything in your layered system.
The fix is straightforward: wrap the import in a layer of your own. For example: @layer vendor { @import url("normalize.css"); }. That drops the library into the vendor layer, which you can place before reset in your declaration, giving it the lowest possible priority.
Similarly, browser DevTools will flag styles as "unlayered" if you inspect an element — a useful diagnostic when debugging unexpected overrides.
Nested layers
Layers can be nested using dot notation. Inside the components layer, you can declare @layer forms, buttons; to sub-order component groups. The outer priority still applies — a components.buttons rule still loses to a utilities rule — but within components, buttons beats forms.
Nesting is most useful in large design systems where a single layer would otherwise become unwieldy. For most projects, a flat five-layer structure is sufficient and easier to reason about. Start flat; nest only when a layer grows large enough to need internal ordering.