Skip to main content

CSS Cascade Layers Guide

Master @layer to control CSS specificity and organize your stylesheets.
Click any code block to copy to clipboard.

What are Cascade Layers?

CSS Cascade Layers, introduced with the @layer at-rule, give you explicit control over the cascade order of your styles. They let you organize CSS into named groups with predictable priority, regardless of specificity or source order within each layer.

Why do they exist? Traditional CSS specificity can become unpredictable as projects grow. Layers solve this by creating a hierarchy where the layer order determines which styles win, not the specificity of individual selectors.

Browser Support

Supported in all modern browsers since 2022: Chrome 99+, Firefox 97+, Safari 15.4+, Edge 99+. Check caniuse.com for current support.

Basic Syntax

Named Layer

Wrap styles in a named layer using the @layer block:

@layer components {
  .button {
    padding: 0.5rem 1rem;
    border-radius: 0.25rem;
    background: blue;
    color: white;
  }

  .card {
    padding: 1rem;
    border: 1px solid #ccc;
    border-radius: 0.5rem;
  }
}

Anonymous Layer

Create an unnamed layer (cannot be referenced later):

@layer {
  /* These styles are in an anonymous layer */
  .special-element {
    color: red;
  }
}

Declaring Layer Order

Pre-declare the order of layers at the top of your stylesheet. This is the recommended approach:

/* Declare layer order first - this is crucial! */
@layer reset, base, components, utilities;

/* Now define each layer in any order you want */
@layer utilities {
  .text-center { text-align: center; }
}

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

@layer components {
  .btn { padding: 0.5rem 1rem; }
}

@layer reset {
  *, *::before, *::after { box-sizing: border-box; }
}

Layer Priority

The order in which layers are declared determines their priority. Later layers have higher priority and will override earlier ones, even if the earlier layer has more specific selectors.

Layer Priority (lowest to highest)

1
reset
2
base
3
components
4
utilities
5
(unlayered)
Later declared layers override earlier ones

Priority Example

@layer base, components;

@layer base {
  /* This has higher specificity... */
  #sidebar .nav-link.active {
    color: blue;
  }
}

@layer components {
  /* ...but this WINS because components comes after base */
  .nav-link {
    color: red;
  }
}

The .nav-link will be red, even though the base layer selector is more specific.

Nested Layers

Layers can be nested for more granular organization:

@layer framework {
  @layer base {
    /* framework.base */
  }

  @layer components {
    /* framework.components */
  }
}

/* Or use dot notation */
@layer framework.utilities {
  /* framework.utilities */
}

Importing with Layers

You can import external CSS files directly into a layer using the layer() function with @import.

Basic Import into Layer

/* Import third-party CSS into a specific layer */
@import url("normalize.css") layer(reset);
@import url("bootstrap.css") layer(framework);

/* Declare your layers after imports */
@layer reset, framework, components, utilities;

Third-Party CSS Management

This is incredibly useful for managing third-party CSS that you do not control:

/* Put all third-party CSS in low-priority layers */
@import url("vendor/datepicker.css") layer(vendor);
@import url("vendor/carousel.css") layer(vendor);

/* Your layers come after, so they can override vendor styles */
@layer vendor, base, components, utilities;

@layer components {
  /* This will override any datepicker styles */
  .datepicker-input {
    border-color: var(--brand-color);
  }
}

Import into Nested Layer

@import url("library.css") layer(framework.defaults);

Practical Architecture

Here is a recommended layer structure for organizing a production CSS architecture:

1. Reset/Normalize

Browser resets, normalize.css, or CSS remedy. Lowest priority.

2. Base/Defaults

Typography, element defaults, CSS variables.

3. Components

Reusable UI components like buttons, cards, forms.

4. Utilities

Single-purpose utility classes (Tailwind-style).

Complete Architecture Example

/* main.css - Your main stylesheet */

/* 1. Declare layer order first */
@layer reset, base, components, utilities, overrides;

/* 2. Import external CSS into appropriate layers */
@import url("normalize.css") layer(reset);

/* 3. Define each layer */
@layer base {
  :root {
    --color-primary: #3b82f6;
    --color-text: #1f2937;
    --radius: 0.5rem;
  }

  body {
    font-family: system-ui, sans-serif;
    color: var(--color-text);
    line-height: 1.5;
  }

  h1, h2, h3 { font-weight: 700; }
  a { color: var(--color-primary); }
}

@layer components {
  .btn {
    display: inline-flex;
    align-items: center;
    padding: 0.5rem 1rem;
    border-radius: var(--radius);
    font-weight: 500;
    transition: all 0.2s;
  }

  .btn-primary {
    background: var(--color-primary);
    color: white;
  }

  .card {
    padding: 1.5rem;
    border-radius: var(--radius);
    border: 1px solid #e5e7eb;
  }
}

@layer utilities {
  .text-center { text-align: center; }
  .flex { display: flex; }
  .gap-4 { gap: 1rem; }
  .mt-4 { margin-top: 1rem; }
}

@layer overrides {
  /* Emergency overrides and edge cases */
  .force-hidden { display: none !important; }
}

Real-World Examples

Preventing Third-Party CSS Conflicts

Put third-party libraries in a low-priority layer so your styles always win:

/* All vendor CSS goes in the lowest priority layer */
@import url("node_modules/some-library/styles.css") layer(vendor);

@layer vendor, theme, components;

@layer components {
  /* Your .modal styles will ALWAYS override the library's */
  .modal {
    background: white;
    border-radius: 1rem;
    /* No need for !important or complex selectors */
  }
}

Framework Integration Pattern

Structure layers for a design system or component library:

/* design-system.css */
@layer
  ds.reset,
  ds.tokens,
  ds.base,
  ds.components,
  ds.utilities;

@layer ds.tokens {
  :root {
    --ds-color-primary: #0066cc;
    --ds-spacing-4: 1rem;
  }
}

@layer ds.components {
  .ds-button { /* ... */ }
  .ds-card { /* ... */ }
}

/* app.css - Your application styles */
@import url("design-system.css");

/* App layers come after, so they can customize the design system */
@layer app.components, app.pages;

@layer app.components {
  /* Override design system button for your brand */
  .ds-button {
    border-radius: 9999px; /* Make all buttons pill-shaped */
  }
}

Tailwind CSS Integration

Tailwind CSS v3.2+ uses cascade layers internally. You can integrate custom layers:

/* Tailwind already uses @layer base, components, utilities */

/* Add your custom styles to Tailwind's layers */
@layer components {
  .my-custom-card {
    @apply rounded-lg p-6 bg-white shadow-lg;
  }
}

/* Or create layers that work alongside Tailwind */
@layer vendor, base, components, utilities, overrides;

/* Put third-party CSS below Tailwind's utilities */
@import url("legacy-styles.css") layer(vendor);

Unlayered Styles

Styles that are not wrapped in any @layer have the highest priority and will override all layered styles, regardless of specificity.

@layer base, components;

@layer components {
  .button {
    background: blue; /* In a layer */
  }
}

/* Unlayered - this WINS over everything in layers */
.button {
  background: red;
}

/* The button will be red, because unlayered styles
   always beat layered styles */

Using This Strategically

You can use unlayered styles for:

  • Critical overrides that must always apply
  • Page-specific styles that should override components
  • Temporary fixes while refactoring

Recommended Pattern

Keep most styles in layers and reserve unlayered styles for intentional overrides:

/* All normal styles go in layers */
@layer reset, base, components, utilities;

@layer base { /* ... */ }
@layer components { /* ... */ }
@layer utilities { /* ... */ }

/* Only use unlayered for intentional overrides */
/* These should be rare and well-documented */
.page-checkout .button {
  /* Override for checkout page only */
  background: green;
}

Quick Reference

SyntaxDescription
@layer name { }Create or add to a named layer
@layer { }Create an anonymous layer
@layer a, b, c;Declare layer order (c has highest priority)
@import url() layer(name)Import CSS file into a layer
@layer parent.child { }Create or reference a nested layer

Best Practices

1. Always declare layer order first. Put @layer reset, base, components, utilities; at the top of your main stylesheet.

2. Put third-party CSS in low-priority layers. This prevents external libraries from overriding your styles unexpectedly.

3. Keep unlayered styles minimal. They should be intentional overrides, not the default.

4. Use layers for architecture, not specificity hacks. Layers replace the need for!important and overly specific selectors.