Cascade layers (@layer) and controlling specificity on purpose. The goal is CSS where resets, framework styles, components, and utilities can coexist without endless !important or selector arms races.
Why layers exist
In the intro series you learned that the cascade picks winners from origin, importance, specificity, and source order. That works until you have a reset, third-party CSS, component styles, and utility classes all fighting over the same button. Specificity creep follows: .nav .menu .button.active becomes the norm because someone needed to win once.
@layer adds an explicit ordering step. Styles in a later-declared layer beat styles in an earlier layer, regardless of selector weight (unless !important is involved – more on that shortly). You decide the stack once, then write normal selectors inside each layer.
Declaring and using layers
Define layer order at the top of your stylesheet, then assign rules to layers:
@layer reset, base, components, utilities;
@layer reset {
*, *::before, *::after {
box-sizing: border-box;
}
}
@layer base {
body {
font-family: system-ui, sans-serif;
line-height: 1.5;
}
}
@layer components {
.card {
padding: 1rem;
border: 1px solid #ccc;
border-radius: 0.5rem;
}
}
@layer utilities {
.text-center {
text-align: center;
}
}
Here, .text-center in the utilities layer beats .card text alignment even if you later write .card.card--featured with higher specificity in the components layer. The layer order does the work.
Importing into layers
When you split CSS across files, import each file into the right layer:
@layer reset, base, components, utilities;
@import url("reset.css") layer(reset);
@import url("typography.css") layer(base);
@import url("buttons.css") layer(components);
@import url("helpers.css") layer(utilities);
That keeps vendor or framework CSS in a predictable slot. A Bootstrap import might live in layer(framework) between base and components, so your component styles can override it without selector gymnastics.
Unlayered styles still win
Styles outside any layer beat all layered styles (again, except the !important flip). That is useful for one-off fixes but dangerous if it becomes a habit. Prefer putting everything in a layer, even a catch-all overrides layer at the end:
@layer reset, base, components, utilities, overrides;
@layer overrides {
/* deliberate exceptions live here, visibly */
.legacy-widget h2 {
font-size: 1rem;
}
}
:where() for zero-specificity selectors
Layers handle ordering between groups. Inside a layer, specificity still matters. Pair layers with :where() for selectors that should be easy to override:
@layer components {
:where(.prose) h2 {
font-size: 1.5rem;
margin-block: 1.5rem 0.75rem;
}
:where(.prose) a {
color: #0366d6;
text-decoration: underline;
}
}
:where() contributes zero specificity. A single class elsewhere can override these rules without fighting a tag selector’s weight. We cover :is() and :has() in the modern selectors tutorial; for now, remember :where() when you want low specificity on purpose.
!important inside layers
!important reverses layer order: important declarations in earlier layers beat important declarations in later layers. Normal declarations still follow the usual layer order. In practice, avoid !important in layered CSS unless you are integrating with a library that already uses it heavily.
A practical stack
A sensible default for many projects:
- reset – box model, margin zeroing, sensible defaults
- base – typography, colours, element-level styles
- layout – page shells, grids, regions
- components – cards, nav, buttons, form controls
- utilities – small single-purpose classes
Name layers for what they contain, not for how important you feel they are. The order declaration is the contract. Document it at the top of your main CSS file so the next person knows where new styles belong.
When not to bother
A single-page exercise or a tiny stylesheet does not need layers. They pay off when CSS comes from several sources or several authors, or when you keep hitting specificity walls. If your entire site is one styles.css under two hundred lines, layers may be overhead. If you are pulling in a framework plus your own components, they are worth the setup.
The next tutorial looks at custom properties in real layouts – theming, component tokens, and how they fit alongside layers without turning every colour into a variable because you can.

