Reset in a layer
Place your CSS reset inside @layer reset so every unlayered component style wins automatically — no !important, no specificity hacks.
Reset + component clash
Primary needed !important to override the reset's element selector.
Reset layered, no conflict
Unlayered component styles win over @layer reset automatically.
Quick implementation
/* 1. Declare layer order up front (optional but explicit) */
@layer reset, base, components, utilities;
/* 2. Put your reset inside @layer reset */
@layer reset {
*,
*::before,
*::after {
box-sizing: border-box;
}
* {
margin: 0;
padding: 0;
}
body {
line-height: 1.5;
-webkit-font-smoothing: antialiased;
}
img,
picture,
video,
canvas,
svg {
display: block;
max-width: 100%;
}
input,
button,
textarea,
select {
font: inherit;
}
p,
h1,
h2,
h3,
h4,
h5,
h6 {
overflow-wrap: break-word;
}
}
/* 3. Unlayered component styles — always beat @layer reset */
.btn {
display: inline-flex;
align-items: center;
gap: 0.5rem;
padding: 0.5rem 1.25rem;
border-radius: 0.375rem;
font-weight: 600;
cursor: pointer;
background: var(--accent-bg);
color: oklch(0.99 0 0);
}
/* No !important needed — unlayered wins automatically */
Prompt this to your LLM
Includes role, constraints, two framework variants, and edge cases to handle.
You are a senior frontend engineer setting up the CSS architecture for a
design-system component library.
Goal: Place the project's CSS reset inside an @layer so that component
styles always override reset defaults without needing !important or
inflated specificity.
Technical constraints:
- Declare the layer stack at the top of the stylesheet with:
@layer reset, base, components, utilities;
This makes the order explicit and documents intent.
- Move all reset rules (box-sizing, margin/padding zeroing, font inheritance
on form controls, image display: block, line-height on body, etc.) inside
@layer reset { ... }.
- Write all component and utility styles outside any @layer block (unlayered).
Unlayered styles automatically outrank any layered style regardless of
specificity, so no !important is ever needed to override the reset.
- Use oklch() for any color values — no hex or rgba.
- Use CSS custom properties (var(--accent-bg), var(--text), etc.) for
design tokens rather than hard-coded values.
Framework variant (pick one):
A) Vanilla CSS — single stylesheet with the layer declaration at line 1.
B) CSS Modules (Next.js / Vite) — put the reset in a global.css imported
once in _app.tsx; component modules are automatically unlayered and win.
Edge cases to handle:
- Third-party stylesheets loaded without @layer will be treated as
unlayered and will outrank your @layer reset — import them inside their
own layer (e.g., @layer vendor { @import "normalize.css"; }) if you need
your components to override them.
- If you need to override a third-party layered stylesheet, add your
overrides in a layer declared after it in the @layer order list.
- Avoid mixing layered and unlayered imports from the same library, as the
unlayered portions will always win and may produce unexpected results.
Return the full CSS scaffold: layer declaration, reset block, and two
example component classes that demonstrate the unlayered override working
correctly.
Why cascade layers solve the reset problem
CSS resets have always had a specificity problem. A traditional reset targets bare element selectors — *, body, h1, button — which carry low specificity. Component styles should easily override them, but in practice the reset and component rules are mixed in the same cascade origin, so source order determines the winner. Any reset rule that appears after a component rule in the stylesheet wins, regardless of intent. The usual escape hatches — !important, class-chained selectors, or duplicating the reset lower in the file — all add complexity and fragility.
Cascade layers, introduced in CSS in 2022 and now widely supported across all major browsers, add a new sorting step that happens before specificity. The browser resolves layers in the order they are declared; rules in a later layer beat rules in an earlier layer. Crucially, unlayered styles always beat all layered styles. By placing the reset inside @layer reset and writing component styles outside any layer, components win by default — no specificity arithmetic required.
Layer ordering and the unlayered advantage
Declare your layer stack once at the top of your stylesheet:
@layer reset, base, components, utilities;
This single line documents the entire cascade intent for every engineer who reads the file. Layers declared later in this list beat layers declared earlier — so utilities beats components, components beats base, and base beats reset. But all four named layers are beaten by any rule that exists outside a @layer block entirely.
This means you can choose your approach: either write everything inside named layers and rely on the order, or write components unlayered and let the cascade handle the reset automatically. The unlayered approach is the simpler default for most projects — only reach for multiple named component layers when you need fine-grained control over which components override which.
One common misconception is that specificity still matters across layers — it does not. A universal selector (*) in an unlayered rule beats a full class chain inside @layer reset. Layer membership is resolved before specificity, so specificity only matters within the same layer.
Third-party stylesheets and the unlayered trap
Cascade layers introduce a subtlety with third-party CSS: any stylesheet imported without a @layer wrapper is treated as unlayered and will automatically beat your layered component styles. If you import a library like Normalize.css or a UI kit without wrapping it, its rules will outrank your @layer components rules even when your selectors are more specific.
The fix is to wrap third-party imports in their own layer:
@layer vendor {
@import url("normalize.css");
}
@layer reset, vendor, base, components, utilities;
Now normalize.css lives in @layer vendor, which sits below components and utilities in the declared order. Your component styles win without any specificity conflicts. If you cannot control the import location (e.g., a CDN script tag), you can wrap the affected rules in a later-declared layer using @layer override { ... } declared after all other layers.