Back to Blog
CSSCascade LayersArchitectureSpecificity

CSS Cascade Layers Guide

Master CSS @layer for organizing styles and controlling cascade precedence.

B
Bootspring Team
Engineering
December 22, 2018
5 min read

CSS Cascade Layers (@layer) let you control style precedence without fighting specificity. Here's how to use them.

Basic Layers#

1/* Define layer order */ 2@layer reset, base, components, utilities; 3 4/* Styles in later layers override earlier layers */ 5@layer reset { 6 * { 7 margin: 0; 8 padding: 0; 9 box-sizing: border-box; 10 } 11} 12 13@layer base { 14 body { 15 font-family: system-ui, sans-serif; 16 line-height: 1.5; 17 } 18 19 a { 20 color: blue; 21 } 22} 23 24@layer components { 25 .btn { 26 padding: 0.5rem 1rem; 27 border-radius: 4px; 28 background: #3b82f6; 29 color: white; 30 } 31} 32 33@layer utilities { 34 .text-center { 35 text-align: center; 36 } 37 38 .mt-4 { 39 margin-top: 1rem; 40 } 41}

Layer Precedence#

1/* Order declaration determines precedence */ 2@layer first, second, third; 3 4/* third > second > first */ 5@layer first { 6 .box { 7 background: red; 8 } 9} 10 11@layer second { 12 .box { 13 background: green; /* Wins over first */ 14 } 15} 16 17@layer third { 18 .box { 19 background: blue; /* Wins over second */ 20 } 21} 22 23/* Result: .box has blue background */

Anonymous Layers#

1/* Unnamed layers in order of appearance */ 2@layer { 3 /* Anonymous layer 1 */ 4 .card { 5 padding: 1rem; 6 } 7} 8 9@layer { 10 /* Anonymous layer 2 - higher precedence */ 11 .card { 12 padding: 2rem; 13 } 14}

Nested Layers#

1@layer components { 2 /* Sub-layers within components */ 3 @layer buttons { 4 .btn { 5 padding: 0.5rem 1rem; 6 } 7 } 8 9 @layer cards { 10 .card { 11 border-radius: 8px; 12 } 13 } 14} 15 16/* Access nested layers with dot notation */ 17@layer components.buttons { 18 .btn-primary { 19 background: #3b82f6; 20 } 21} 22 23@layer components.cards { 24 .card-elevated { 25 box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); 26 } 27}

Importing with Layers#

1/* Import entire stylesheet into layer */ 2@import url('reset.css') layer(reset); 3@import url('typography.css') layer(base); 4@import url('components.css') layer(components); 5 6/* Import into nested layer */ 7@import url('buttons.css') layer(components.buttons);

Framework Integration#

1/* Layer third-party CSS */ 2@layer reset, vendor, components, utilities; 3 4/* Import framework into vendor layer */ 5@import url('bootstrap.css') layer(vendor); 6 7/* Your styles override framework */ 8@layer components { 9 .btn { 10 /* Your button styles override Bootstrap */ 11 border-radius: 9999px; 12 } 13}

Unlayered Styles#

1@layer base, components; 2 3@layer base { 4 .text { 5 color: gray; 6 } 7} 8 9/* Unlayered styles have highest precedence */ 10.text { 11 color: red; /* Wins over any layer */ 12} 13 14@layer components { 15 .text { 16 color: blue; /* Still loses to unlayered */ 17 } 18}

Design System Layers#

1/* Define comprehensive layer structure */ 2@layer 3 reset, 4 tokens, 5 base, 6 layout, 7 components, 8 patterns, 9 utilities, 10 overrides; 11 12@layer reset { 13 *, 14 *::before, 15 *::after { 16 box-sizing: border-box; 17 margin: 0; 18 padding: 0; 19 } 20} 21 22@layer tokens { 23 :root { 24 --color-primary: #3b82f6; 25 --color-secondary: #8b5cf6; 26 --spacing-4: 1rem; 27 --radius-md: 0.5rem; 28 } 29} 30 31@layer base { 32 body { 33 font-family: system-ui; 34 color: #1f2937; 35 } 36 37 h1, 38 h2, 39 h3 { 40 line-height: 1.2; 41 } 42} 43 44@layer layout { 45 .container { 46 max-width: 1200px; 47 margin: 0 auto; 48 padding: 0 var(--spacing-4); 49 } 50 51 .grid { 52 display: grid; 53 gap: var(--spacing-4); 54 } 55} 56 57@layer components { 58 .button { 59 padding: 0.5rem 1rem; 60 border-radius: var(--radius-md); 61 background: var(--color-primary); 62 } 63 64 .card { 65 padding: var(--spacing-4); 66 border-radius: var(--radius-md); 67 background: white; 68 } 69} 70 71@layer utilities { 72 .sr-only { 73 position: absolute; 74 width: 1px; 75 height: 1px; 76 overflow: hidden; 77 clip: rect(0, 0, 0, 0); 78 } 79 80 .flex { 81 display: flex; 82 } 83 84 .items-center { 85 align-items: center; 86 } 87} 88 89@layer overrides { 90 /* Last resort overrides */ 91}

Component Variants#

1@layer components { 2 @layer base { 3 .alert { 4 padding: 1rem; 5 border-radius: 4px; 6 border: 1px solid; 7 } 8 } 9 10 @layer variants { 11 .alert-success { 12 background: #d1fae5; 13 border-color: #10b981; 14 color: #065f46; 15 } 16 17 .alert-error { 18 background: #fee2e2; 19 border-color: #ef4444; 20 color: #991b1b; 21 } 22 } 23 24 @layer states { 25 .alert-dismissible { 26 padding-right: 3rem; 27 position: relative; 28 } 29 } 30}

Media Query Layers#

1@layer base, responsive; 2 3@layer base { 4 .sidebar { 5 width: 100%; 6 } 7} 8 9@layer responsive { 10 @media (min-width: 768px) { 11 .sidebar { 12 width: 300px; 13 } 14 } 15}

Specificity Within Layers#

1@layer components { 2 /* Lower specificity still wins within same layer */ 3 .btn { 4 background: blue; 5 } 6 7 /* Higher specificity in same layer */ 8 button.btn { 9 background: red; /* Wins due to specificity */ 10 } 11} 12 13@layer utilities { 14 /* Different layer - always wins over components */ 15 .bg-green { 16 background: green !important; /* Wins over everything in components */ 17 } 18}

Revert Layer#

1@layer base { 2 a { 3 color: blue; 4 text-decoration: none; 5 } 6} 7 8@layer components { 9 .link { 10 /* Revert to previous layer's value */ 11 color: revert-layer; /* blue from base */ 12 text-decoration: underline; 13 } 14 15 .link-reset { 16 /* Revert all properties */ 17 all: revert-layer; 18 } 19}
1@layer screen, print; 2 3@layer screen { 4 .no-print { 5 display: block; 6 } 7 8 .print-only { 9 display: none; 10 } 11} 12 13@layer print { 14 @media print { 15 .no-print { 16 display: none; 17 } 18 19 .print-only { 20 display: block; 21 } 22 23 body { 24 font-size: 12pt; 25 } 26 } 27}

JavaScript Layer Detection#

1// Check if cascade layers are supported 2if (CSS.supports('@layer base { }')) { 3 console.log('Cascade layers supported'); 4} 5 6// Dynamically add layered styles 7const style = document.createElement('style'); 8style.textContent = ` 9 @layer dynamic { 10 .dynamic-content { 11 opacity: 1; 12 } 13 } 14`; 15document.head.appendChild(style);

Best Practices#

Structure: ✓ Declare layer order upfront ✓ Use descriptive layer names ✓ Nest related sub-layers ✓ Keep utilities last Third-Party: ✓ Layer external CSS ✓ Your code in higher layers ✓ Avoid !important wars ✓ Document layer structure Architecture: ✓ reset → base → components → utilities ✓ Consistent naming convention ✓ Single responsibility layers ✓ Document layer purposes Avoid: ✗ Too many layers ✗ Deeply nested layers ✗ Unlayered styles mixed in ✗ Ignoring specificity within layers

Conclusion#

CSS Cascade Layers provide explicit control over style precedence without specificity hacks. Define layer order upfront, use them for design systems with reset, base, components, and utilities layers. Import third-party CSS into lower-precedence layers. Remember that unlayered styles always win, and use revert-layer to fall back to previous layer values.

Share this article

Help spread the word about Bootspring