Scope the Magic
Who Gets Which Spell?
Section titled “Who Gets Which Spell?”Declaring CSS variables is cute.
Scoping them is where they become a real superpower.
The question is always:
“On which element do I store this value so the right things inherit it?”
We’ll use three main levels:
- Global —
:rootfor site‑wide tokens - Section / context — wrappers like
.lesson,.sidebar,.dialog - Component — local defaults on
.card,.button, etc.
🌍 Global vs Local Scope
Section titled “🌍 Global vs Local Scope”Global, in :root, is great for design tokens you want everywhere:
:root { --page-bg: #020617; --page-text: #e5e7eb; --accent: #ff6ec7;}Then components can drink from that global well:
.card { background: #020617; color: var(--page-text); border: 1px solid color-mix(in srgb, var(--accent) 18%, transparent);}But sometimes you need a different vibe in one area — a lab mode, a warning zone, a “boss level” section.
That’s where local scope comes in:
.lab-section { --accent: #38bdf8; /* cyan accent just for this section */}Inside .lab-section, anything using var(--accent) picks up the new value.
Outside, the global --accent still wins.
🧬 Nested Overrides: The Cascade Still Rules
Section titled “🧬 Nested Overrides: The Cascade Still Rules”Variables don’t dodge the cascade — they use it.
:root { --panel-bg: #020617;}
.lab-section { --panel-bg: #020617;}
.lab-section .panel--warning { --panel-bg: #450a0a; /* deepest wins here */}For .panel--warning:
- It looks for
--panel-bgon itself → finds#450a0a - If it didn’t exist, it would climb to
.lab-section - If still missing, it would fall back to
:root
Same cascade rules you already know, just with fancier loot.
🧱 Component Defaults + External Overrides
Section titled “🧱 Component Defaults + External Overrides”A nice pattern: give components safe defaults on the component itself,
then allow parents to override with variables.
.card { --card-bg: #020617; /* component default */ --card-radius: 1.25rem;
background: var(--card-bg); border-radius: var(--card-radius);}
.card--highlight { --card-bg: #0f172a;}
.feature-strip { --card-bg: #1e293b; /* overrides all cards inside */ --card-radius: 999px;}Priority order here:
- Custom property declared closest to the card
- Then parent wrappers (
.feature-strip) - Then
:root
Outside of all those, the component still looks fine because it brought its own default values.
🧪 MiniMo 02 — Scoped Studio
Section titled “🧪 MiniMo 02 — Scoped Studio”Let’s see scope in action: three panels, one variable name.
Here’s the core CSS behind that MiniMo:
:root { --panel-bg: #020617; --panel-accent: #ff6ec7;}
.scope-lab { --panel-bg: #020617;}
.scope-lab__row--alt { --panel-bg: #020617; --panel-accent: #38bdf8; /* cyan, local to this row */}
.scope-panel--alert { --panel-bg: #450a0a; --panel-accent: #f97373;}
.scope-panel { background: radial-gradient(circle at top, var(--panel-bg), #020617 70%); border: 1px solid color-mix(in srgb, var(--panel-accent) 22%, transparent); box-shadow: 0 0 0 1px rgba(15, 23, 42, 0.8), 0 18px 50px color-mix(in srgb, var( --panel-accent ) 16%, #020617);}Same variable names, different scopes, different moods.
Try:
- Changing
--panel-accenton:root - Overriding it on just one row
- Giving a single “alert” card its own local override
You’ll feel exactly how far the magic reaches.
💡 When to Pick Which Scope
Section titled “💡 When to Pick Which Scope”Use this as a quick cheat sheet:
:root→ brand tokens, layout scales, reusable design system values- Section wrapper → per‑page themes, mode toggles, “lab” sections, dark/light walls
- Component → sane defaults so your UI never looks broken, even out of context
Variables don’t replace the cascade.
They give you named hooks to ride the cascade on purpose.
➡️ Next Up
Section titled “➡️ Next Up”We’ve decided who sees which value.
Next: Theme Shifting Tricks — flipping whole looks with a single class.