Custom properties (variables)
A custom property, usually called a CSS variable, stores a value under a name that starts with --. The var() function reads it:
<style>
:root {
--brand: #5a48f5;
--radius: 10px;
--space: 14px;
}
body { font-family: system-ui; }
.btn { padding: calc(var(--space) * .7) var(--space); border: 0; border-radius: var(--radius);
background: var(--brand); color: white; font-weight: 600; }
.card { margin-top: var(--space); padding: var(--space); border: 2px solid var(--brand); border-radius: var(--radius); }
</style>
<button class="btn">Button</button>
<div class="card">Change <code>--brand</code> once in <code>:root</code>, and every use of it updates.</div>:root is the <html> element, so variables declared there are available everywhere. Press Edit, change --brand to #e0446a and run it again.
Fallbacks
color: var(--accent, #0aa9cf); /* #0aa9cf if --accent isn't set */Overriding for part of the page
Custom properties are inherited, like color, and any element can set its own value for itself and its children:
<style>
:root { --tone: #5a48f5; }
body { display: flex; flex-wrap: wrap; gap: 12px; font-family: system-ui; }
.card { width: 150px; padding: 14px; border-radius: 10px; border-top: 6px solid var(--tone); background: #f8f8fb; }
.card h3 { margin: 0 0 6px; color: var(--tone); }
.card button { padding: 6px 12px; border: 0; border-radius: 6px; background: var(--tone); color: white; }
.python { --tone: #2f7ccf; }
.sql { --tone: #0f9d76; }
.react { --tone: #0aa9cf; }
</style>
<div class="card"><h3>Default</h3><button>Start</button></div>
<div class="card python"><h3>Python</h3><button>Start</button></div>
<div class="card sql"><h3>SQL</h3><button>Start</button></div>
<div class="card react"><h3>React</h3><button>Start</button></div>One set of rules gives four colour schemes, because each card changes only --tone. CodeMIRAI's own course cards work this way: each one sets a single colour variable, and every tint on the card is worked out from it.
Light and dark themes
<style>
:root { --bg: #ffffff; --text: #16181d; --muted: #5d6270; --surface: #f3f4f8; --accent: #5a48f5; }
.dark { --bg: #0f1117; --text: #e9ecf5; --muted: #9aa1b2; --surface: #1a1d27; --accent: #8577ff; }
body { margin: 0; }
.app { padding: 18px; background: var(--bg); color: var(--text); font-family: system-ui;
transition: background-color .3s, color .3s; }
.app p { color: var(--muted); }
.panel { padding: 14px; border-radius: 10px; background: var(--surface); }
.app button { padding: 8px 14px; border: 0; border-radius: 8px; background: var(--accent); color: white; }
</style>
<div class="app" id="app">
<h2>Theme switching</h2>
<div class="panel"><p>Every colour on this panel comes from a variable.</p></div>
<p><button id="toggle">Toggle dark mode</button></p>
</div>
<script>
document.querySelector("#toggle").addEventListener("click", () => {
document.querySelector("#app").classList.toggle("dark");
});
</script>The components never name a colour directly, only variables, so switching themes just swaps the variables' values.
Changing variables from JavaScript
<style>
:root { --hue: 250; --size: 80px; }
body { font-family: system-ui; }
.blob { width: var(--size); height: var(--size); margin: 14px 0; border-radius: 30%;
background: hsl(var(--hue) 80% 60%); transition: all .2s; }
</style>
<label>Hue <input type="range" min="0" max="360" value="250" id="hue"></label>
<label>Size <input type="range" min="40" max="160" value="80" id="size"></label>
<div class="blob"></div>
<script>
const root = document.documentElement;
document.querySelector("#hue").addEventListener("input", (e) => root.style.setProperty("--hue", e.target.value));
document.querySelector("#size").addEventListener("input", (e) => root.style.setProperty("--size", e.target.value + "px"));
</script>style.setProperty("--name", value) changes a variable, and every rule that uses it updates at once.