JS Takes the Wand
When JavaScript Grabs the Wand
Section titled “When JavaScript Grabs the Wand”So far, our variables have been static spell ingredients: declared in CSS, used in CSS.
Now we hand the wand to JavaScript and let it rewrite the values at runtime.
This is where things get wild:
- Sliders that change
--glow-strength - Toggles that flip light/dark modes
- “Lab mode” buttons that restyle a whole layout with one function call
JavaScript doesn’t style the page directly — it just updates the tokens.
🤝 Why JS + CSS Variables Is So Good
Section titled “🤝 Why JS + CSS Variables Is So Good”Instead of sprinkling element.style.whatever = ... everywhere, you:
- Keep the visual logic in CSS (colors, spacing, radii, shadows).
- Use JavaScript only to change variable values.
- Let the cascade and
var()do the rest.
Change one custom property → every place that uses it reacts.
📥 Reading a Variable from JavaScript
Section titled “📥 Reading a Variable from JavaScript”You read the computed value of a custom property like this:
const root = document.documentElement;const styles = getComputedStyle(root);const accent = styles.getPropertyValue('--accent').trim();
console.log('Current accent is:', accent);A couple of notes:
document.documentElementis the<html>element (a great place for global tokens).getPropertyValue('--accent')returns the final computed value after the cascade..trim()cleans up any extra whitespace.
You can do the same on any element that declares/overrides the variable.
✍️ Writing a Variable from JavaScript
Section titled “✍️ Writing a Variable from JavaScript”To change a variable, update the style of an element and set the property name:
const root = document.documentElement;
root.style.setProperty('--accent', '#22c55e'); // limeroot.style.setProperty('--panel-radius', '1.75rem');Important details:
- The name includes the
--prefix. - The value must be a string that results in valid CSS when used.
- You usually write to:
document.documentElementfor global theme switches- A specific wrapper (e.g.
.demo-shell) for scoped experiments
Everything using var(--accent) or var(--panel-radius) updates instantly.
🧪 MiniMo 03 — Theme Switcher Wand
Section titled “🧪 MiniMo 03 — Theme Switcher Wand”Three theme buttons, one set of tokens.
JavaScript only changes the variables; CSS handles the look.
Here’s the core idea behind that MiniMo:
:root { --page-bg: #020617; --card-bg: #020617; --accent: #ff6ec7; --glow: rgba(255, 110, 199, 0.42);}
.theme-card { background: radial-gradient(circle at top, var(--card-bg), #020617 80%); border: 1px solid color-mix(in srgb, var(--accent) 24%, transparent); box-shadow: 0 0 0 1px rgba(15, 23, 42, 0.85), 0 24px 60px var(--glow);}const root = document.documentElement;
const themes = { default: { '--page-bg': '#020617', '--card-bg': '#020617', '--accent': '#ff6ec7', '--glow': 'rgba(255, 110, 199, 0.42)', }, neon: { '--page-bg': '#020617', '--card-bg': '#020617', '--accent': '#38bdf8', '--glow': 'rgba(56, 189, 248, 0.5)', }, sunset: { '--page-bg': '#020617', '--card-bg': '#1c1917', '--accent': '#fb923c', '--glow': 'rgba(251, 146, 60, 0.5)', },};
function applyTheme(name) { const theme = themes[name]; for (const [key, value] of Object.entries(theme)) { root.style.setProperty(key, value); }}The markup just adds buttons with data-theme="neon" / data-theme="sunset"
and calls applyTheme() when you click.
🚧 Common Pitfalls
Section titled “🚧 Common Pitfalls”A few “gotchas” to warn the class about:
-
Forgetting the
--prefix
setProperty('accent', '#ff6ec7')silently fails. The name must start with--. -
Writing to the wrong element
If your CSS reads from:rootbut JS sets variables on.demo-shell,
the values won’t line up. Point both at the same scope on purpose. -
Mixing JS-applied styles with CSS rules
Still use CSS rules for layout/typography. Let JS tweak variables, not whole declaration blocks.
➡️ Next Up
Section titled “➡️ Next Up”Now that JS can grab the wand, we’ll build interactive controls that adjust variables live — sliders, toggles, and other UI for your variable-powered labs.