Cascade layers
@layer decides which rule wins before specificity: a rule of a later layer beats a rule of an earlier layer, whatever their selectors. The specificity wars of large stylesheets (.a .b .c, !important) end: the order of the layers is declared once, and each style goes in its layer.
The order of the design system
scss
// The cascade order of the design system, loaded first: a later layer always wins, whatever the specificity.
// Unlayered styles would beat every layer: every rule of the design system and of the applications is in one.
@layer reset, tokens, base, layout, components, utilities, overrides;| Layer | Contains |
|---|---|
reset | The normalisation of browser styles (box-sizing, margins) |
tokens | The custom properties: primitives, semantic tokens, themes |
base | Styles of bare elements: body, headings, links |
layout | Page structure: grids, containers, stacks |
components | The styles of the atoms, molecules and organisms |
utilities | Single-purpose classes (.visually-hidden): they must win over components |
overrides | Rare, justified exceptions (third-party widgets) |
No other layer name is allowed, so the order stays the one agreed: the review checks it (no Stylelint rule knows the layers of a project).
Rules to know
- Unlayered styles beat every layer. A stylesheet without
@layer(a third-party library, a forgotten file) wins over the whole design system. Put everything in a layer, or import third-party CSS into one:@import url('widget.css') layer(overrides);. - The first declaration of the order wins. Declare the order before any layered rule: the first
@layer a, b;or@layer a { }seen sets the position ofa. In Sass,@usemust come before any other rule, so the order lives in its own partial, loaded first:
scss
// Global stylesheet of the design system: the layer order first, then each layer. A later layer always wins,
// whatever the specificity: utilities override components without `!important`.
@use 'layers';
@use 'tokens/primitives';
@use 'tokens/semantic';
@layer reset {
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
}
}
@layer base {
body {
font-family: var(--font-family-sans);
font-size: var(--font-size-body);
color: var(--color-text);
background: var(--color-surface);
}
}
@layer utilities {
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
overflow: hidden;
white-space: nowrap;
clip-path: inset(50%);
}
}!importantis inverted. An important declaration of an earlier layer beats an important one of a later layer (the reset can protect a critical rule). Thestrictnessblock forbids!importantanyway: a later layer is the clean way to win.- Layers nest.
@layer components.buttons { }creates a sub-layer, ordered insidecomponents; thelayersrule checks the top-level name.
Angular components
Component styles (emulated encapsulation) are scoped by Angular, and their layer is set in the file:
scss
@layer components {
.button {
color: var(--color-action-text);
}
}A utility class then always wins over a component style, with no !important and no ::ng-deep:
html
<ds-button class="visually-hidden" />