CSS @layer as Architecture: Native ITCSS
@layer (Cascade Layers) is the most important modern CSS feature for methodology-based architecture. It lets you declare the order of the cascade explicitly — so specificity stops being a war and becomes a system you control.
1. The Problem @layer Solves
Without layers, the cascade is determined by source order + specificity. This leads to:
/* styles.css — loaded first */
.btn-primary { background: blue; padding: 1rem; }
/* theme.css — loaded second */
.btn-primary { background: red; } /* Wins — last declaration */
/* plugin.css — loaded third */
.btn-primary { background: green !important; } /* Wins everything */
/* your override — loaded fourth */
.btn-primary { background: var(--color-primary); } /* LOSES to !important */
The typical result: !important escalation, specificity fights, unpredictable overrides.
With @layer, you declare a priority order upfront. Higher layers always win — regardless of source order or specificity:
/* Declare the order. Later = higher priority. */
@layer reset, theme, plugin, components, utilities;
/* Now even if plugin.css uses !important, your components layer beats it */
@layer components {
.btn-primary { background: var(--color-primary); } /* WINS */
}
2. How @layer Works
Basic Syntax
/* Option 1: Declare all layers upfront (recommended) */
@layer reset, base, components, utilities;
/* Option 2: Declare inline with content */
@layer reset {
*, *::before, *::after { box-sizing: border-box; margin: 0; }
}
@layer base {
body { font-family: var(--font-body); }
}
/* Option 3: Append to a layer from anywhere */
@layer components {
.card { background: var(--color-surface); }
}
/* Later in the file — same layer */
@layer components {
.btn { padding: 0.7rem 1.5rem; }
}
Layer Priority Rules
Layer Order: @layer A, B, C, D;
Priority: D > C > B > A ← Later = higher priority
Unlayered CSS beats ALL layers:
.btn { color: red; } ← Beats everything, including D
@layer utilities { .btn { color: blue; } } ← Loses to unlayered
[!IMPORTANT] Unlayered CSS always beats
@layer. This is crucial — if a WordPress plugin or third-party stylesheet doesn't use layers, it wins over everything. Wrap third-party CSS in a low-priority layer to prevent this.
3. @layer as Native ITCSS
ITCSS defined 7 conceptual layers in 2015. @layer (2022) makes those layers native CSS:
/* ITCSS Layer → @layer equivalent */
/* 1. Settings → tokens (no CSS output — defined as :root vars) */
/* 2. Tools → mixins (no CSS output — Sass preprocessor) */
/* 3. Generic → reset */
/* 4. Elements → elements */
/* 5. Objects → objects */
/* 6. Components → components */
/* 7. Utilities → utilities */
@layer reset, elements, objects, components, utilities;
Full ITCSS-Aligned @layer Setup
/* ── main.css ── */
/* Step 1: Declare all layers in order (lowest to highest priority) */
@layer reset, elements, objects, components, utilities;
/* Step 2: Import external stylesheets INTO a layer to control their priority */
@layer reset {
@import url('https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.1/normalize.min.css');
}
/* Step 3: Fill each layer */
/* ── Layer: reset ── */
@layer reset {
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html { -webkit-text-size-adjust: none; }
img, video, svg { display: block; max-width: 100%; height: auto; }
input, button, textarea, select { font: inherit; }
}
/* ── Layer: elements (bare HTML, no classes) ── */
@layer elements {
body {
font-family: var(--font-body);
font-size: var(--text-base);
line-height: 1.6;
color: var(--color-text);
background: var(--color-bg);
}
h1, h2, h3, h4 { font-family: var(--font-heading); line-height: 1.2; }
a { color: var(--color-primary); transition: color 0.2s; }
p { line-height: 1.7; }
}
/* ── Layer: objects (structure only, skin-neutral) ── */
@layer objects {
.o-container {
width: 100%;
max-width: 1200px;
margin-inline: auto;
padding-inline: clamp(1rem, 4vw, 2rem);
}
.o-grid {
display: grid;
gap: var(--grid-gap, 1.5rem);
grid-template-columns: var(--grid-cols, repeat(auto-fill, minmax(280px, 1fr)));
}
.o-stack > * + * { margin-top: var(--stack-gap, 1.5rem); }
.o-media { display: flex; align-items: flex-start; gap: 1.25rem; }
.o-media__body { flex: 1; min-width: 0; }
}
/* ── Layer: components (styled UI components) ── */
@layer components {
.c-btn {
display: inline-flex;
align-items: center;
gap: 0.5rem;
padding: 0.7rem 1.5rem;
border-radius: var(--radius-full);
font-weight: 600;
border: 2px solid transparent;
cursor: pointer;
transition: all 0.2s ease;
}
.c-btn--primary { background: var(--color-primary); color: white; }
.c-btn--primary:hover { background: var(--color-primary-dark); }
.c-card {
background: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: var(--radius-xl);
overflow: hidden;
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.c-card:hover { transform: translateY(-4px); box-shadow: var(--shadow-lg); }
}
/* ── Layer: utilities (highest priority, always win) ── */
@layer utilities {
.u-hidden { display: none !important; }
.u-sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0,0,0,0); }
.u-truncate { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.u-mt-auto { margin-top: auto !important; }
.u-mx-auto { margin-inline: auto !important; }
.u-w-full { width: 100% !important; }
.u-text-center { text-align: center !important; }
}
4. Wrapping Third-Party CSS in a Layer
This is one of the most powerful use cases — taming WordPress plugin styles:
/* Control third-party priority by wrapping in a low-priority layer */
@layer reset, third-party, elements, objects, components, utilities;
/* Contact Form 7 — now lower priority than your components */
@layer third-party {
@import url('/wp-content/plugins/contact-form-7/includes/css/styles.css');
}
/* WooCommerce — now lower priority */
@layer third-party {
@import url('/wp-content/plugins/woocommerce/assets/css/woocommerce.css');
}
/* Your components ALWAYS win now — no !important needed */
@layer components {
.wpcf7-form-control { border: 1.5px solid var(--color-border) !important; } /* Wins cleanly */
}
[!TIP] This technique eliminates 90% of
!importantusage in WordPress child themes. Wrap plugin stylesheets in@layer third-partyand your component layer automatically overrides them.
5. Nested Layers
Layers can be nested for more granular control:
/* Nested layers for a large design system */
@layer design-system {
@layer tokens, reset, base, objects, components;
@layer components {
@layer forms, navigation, cards, modals;
@layer cards {
.c-card { background: var(--color-surface); }
}
@layer modals {
.c-modal { position: fixed; inset: 0; z-index: var(--z-modal); }
}
}
}
6. @layer + Media Queries
Layers work inside media queries:
/* Mobile-first components layer */
@layer components {
.c-nav__list { display: none; }
@media (min-width: 768px) {
.c-nav__list { display: flex; }
}
}
/* Container queries inside a layer */
@layer components {
.card-wrapper { container-type: inline-size; }
@container (min-width: 400px) {
.c-card { flex-direction: row; }
}
}
7. @layer + CSS Nesting: The Ultimate Combination
@layer components {
.c-card {
background: var(--color-surface);
border-radius: var(--radius-xl);
/* Nesting inside the layer */
.c-card__title { font-size: var(--text-xl); }
.c-card__body { padding: var(--space-6); }
&.c-card--featured {
border: 1px solid var(--color-primary);
.c-card__title { color: var(--color-primary); }
}
&:hover { transform: translateY(-4px); }
}
}
8. Browser Support and Progressive Enhancement
| Browser | @layer Support | Since |
|---|---|---|
| Chrome | ✅ | v99 (March 2022) |
| Firefox | ✅ | v97 (Feb 2022) |
| Safari | ✅ | v15.4 (March 2022) |
| Edge | ✅ | v99 (March 2022) |
| Overall | ~96% | — |
@layer is safe to use in production today.
/* If you need a fallback for ancient browsers (rare): */
@supports (not (@layer x {})) {
/* Legacy fallback — almost never needed */
.c-btn { background: var(--color-primary); }
}
9. @layer vs Specificity vs !important
Understanding the full cascade priority order:
Priority (highest → lowest):
──────────────────────────────────────────────────────
1. Transitions (in-progress transitions)
2. !important user agents (browser UA !important)
3. !important user (reader stylesheet !important — rare)
4. !important author (your !important)
↳ !important utilities layer (highest of your @layers + !important)
↳ !important components layer
↳ !important unlayered !important
5. Normal author
↳ Unlayered CSS ← Beats all author @layers
↳ @layer utilities ← Highest @layer
↳ @layer components
↳ @layer objects
↳ @layer elements
↳ @layer reset ← Lowest @layer
6. Normal user
7. User agent (browser defaults)
──────────────────────────────────────────────────────
10. Complete WordPress Layer Architecture
/* child-theme/style.css */
/* Declare layers: lowest priority → highest priority */
@layer wp-reset, wp-blocks, third-party, elements, objects, components, theme, utilities;
/* WordPress reset and base */
@layer wp-reset {
*, *::before, *::after { box-sizing: border-box; }
}
/* Gutenberg block styles — contained so they never leak into components */
@layer wp-blocks {
.wp-block-paragraph { max-width: 72ch; }
.wp-block-heading { font-family: var(--font-heading); }
.wp-block-image img { border-radius: var(--radius-md); }
}
/* Plugin CSS wrapped so YOUR components win */
@layer third-party {
/* CF7, WooCommerce, etc. reset here via @import */
}
/* Page elements */
@layer elements {
body { font-family: var(--font-body); color: var(--color-text); }
}
/* Layout patterns */
@layer objects {
.o-container { max-width: 1200px; margin-inline: auto; }
}
/* Styled components — always win over wp-blocks and third-party */
@layer components {
.c-btn-primary { background: var(--color-primary); color: white; }
.wpcf7-form-control { border: 1.5px solid var(--color-border); border-radius: var(--radius-md); }
.woocommerce #place_order { background: var(--color-primary); border-radius: var(--radius-md); }
}
/* WordPress customizer token overrides */
@layer theme {
:root {
--color-primary: #6c63ff;
--font-body: 'Inter', sans-serif;
}
}
/* Always-win utilities */
@layer utilities {
.u-hidden { display: none !important; }
.u-sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; }
}