🚀AdvancedAdvanced CSS Architecture

CSS Cascade Layers (@layer)

Organize CSS specificity with @layer to create predictable, manageable style hierarchies free from specificity wars.

Overview

Cascade layers give you explicit control over the order of the CSS cascade. Instead of fighting specificity wars, you declare layers in order, and later layers always win over earlier ones regardless of selector specificity. ```css /* Declare layer order first */ @layer reset, base, components, utilities; /* Add rules to each layer */ @layer reset { * { margin: 0; box-sizing: border-box; } } @layer components { .btn { background: blue; color: white; } } @layer utilities { .text-red { color: red; } /* overrides .btn color even with lower specificity */ } ``` **Key rules:** - Styles in a later layer beat earlier layers regardless of specificity - Unlayered styles beat ALL layered styles (treat unlayered as highest priority) - Layer order is set by the first @layer statement that names them - Layers can be nested: `@layer framework.base { }` - Importing with layers: `@import url("reset.css") layer(reset);`

Key Points

  • •Declare layer order with @layer name1, name2, name3 before any layer rules
  • •Later layers have higher priority than earlier layers
  • •Unlayered CSS sits above all layers in the cascade
  • •Layers created inside @import keep third-party CSS sandboxed in your layer stack
  • •Nested layers use dot notation: @layer framework.base, framework.components
  • •You can add to a layer multiple times across your stylesheet
  • •Within a layer, normal specificity rules still apply
  • •@layer is supported in Chrome 99+, Firefox 97+, Safari 15.4+

Examples

Basic layer setup for a design system

/* Step 1: Declare order at the top of your main CSS file */
@layer reset, tokens, base, components, utilities;

/* Step 2: Populate each layer */
@layer reset {
  *, *::before, *::after {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
  }
}

@layer tokens {
  :root {
    --color-primary: #6366f1;
    --color-text: #0f172a;
    --radius-md: 8px;
    --space-4: 16px;
  }
}

@layer components {
  .card {
    background: white;
    border-radius: var(--radius-md);
    padding: var(--space-4);
  }
}

@layer utilities {
  /* Even with low specificity, utilities win over components */
  .p-0 { padding: 0; }
  .hidden { display: none; }
}

Utilities always win over components because they are in a later layer, not because of higher specificity.

Sandboxing third-party CSS with @import layers

/* Put third-party styles in early layers so your code overrides them */
@import url("normalize.css") layer(reset);
@import url("bootstrap.css") layer(vendor);

/* Declare your layers after (they are higher priority) */
@layer reset, vendor, base, site;

@layer site {
  /* These rules beat Bootstrap even at low specificity */
  .card {
    border-radius: 12px;
    border: none;
    box-shadow: 0 2px 8px rgba(0,0,0,0.1);
  }
}

This pattern is the primary motivation for @layer: wrapping third-party CSS so your overrides are always predictable.

Nested layers for library authors

/* Framework ships its own layer hierarchy */
@layer ui {
  @layer reset {
    * { box-sizing: border-box; }
  }

  @layer base {
    body { font-family: system-ui; }
  }

  @layer components {
    .btn { padding: 8px 16px; }
  }
}

/* Consumers can override the nested layers */
@layer ui.components {
  /* Adding to the nested layer from outside the framework */
  .btn { border-radius: 99px; }
}

/* Or override everything by writing outside ui entirely */
.btn { background: red; } /* unlayered, wins over all ui layers */

Revert-layer for graceful fallbacks

@layer base, overrides;

@layer base {
  .btn {
    background: #6366f1;
    color: white;
    padding: 8px 16px;
    border-radius: 6px;
    border: none;
    cursor: pointer;
  }
}

@layer overrides {
  .btn-ghost {
    /* Revert all properties to what they would be without the overrides layer */
    background: revert-layer;
    color: #6366f1;
    border: 2px solid currentColor;
  }
}

revert-layer rolls back properties to the value from the previous layer, not the browser default.

Common Mistakes

  • Adding unlayered rules that unintentionally beat all layers (unlayered always wins)
  • Declaring a @layer after rules have already been added to it silently reorders nothing (order is set by the first declaration)
  • Assuming later specificity beats layers (a .btn rule in an earlier layer loses to a * rule in a later layer)
  • Forgetting that !important reverses layer order (important rules in earlier layers win over later layers)

Note

A practical layer stack for most projects: @layer reset, theme, base, components, utilities. Put third-party imports in reset or a vendor layer, your design tokens in theme, element defaults in base, your UI components in components, and Tailwind-style helpers in utilities.

Exercises

Write CSS to style the HTML scaffold. The preview updates live — when it looks right, mark as done.

Loading editor…

Completed: 0 / 3 exercises

Knowledge Check

Question 1 of 4

In @layer reset, vendor, site: which layer has the highest priority?