Skip to content

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;
LayerContains
resetThe normalisation of browser styles (box-sizing, margins)
tokensThe custom properties: primitives, semantic tokens, themes
baseStyles of bare elements: body, headings, links
layoutPage structure: grids, containers, stacks
componentsThe styles of the atoms, molecules and organisms
utilitiesSingle-purpose classes (.visually-hidden): they must win over components
overridesRare, 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 of a. In Sass, @use must 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%);
  }
}
  • !important is inverted. An important declaration of an earlier layer beats an important one of a later layer (the reset can protect a critical rule). The strictness block forbids !important anyway: a later layer is the clean way to win.
  • Layers nest. @layer components.buttons { } creates a sub-layer, ordered inside components; the layers rule 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" />

Generated from the JSDoc of the sources.