CSS Tutorial · Beyond the Basics · Lesson 17 of 19
CSS Variables and Dark Mode
Custom properties, theming and prefers-color-scheme.
Custom properties
CSS variables start with -- and are read with var(). Define them on :root to make them global, and override them anywhere:
<style>
:root {
--brand: #2563eb;
--radius: 10px;
--space: 12px;
}
body { font: 14px system-ui; }
.button { background: var(--brand); color: white; border: 0; border-radius: var(--radius); padding: var(--space); }
.danger { --brand: #dc2626; } /* override for one component */
.card { border: 2px solid var(--brand); border-radius: var(--radius); padding: var(--space); margin-top: 10px; }
</style>
<button class="button">Primary</button>
<button class="button danger">Danger</button>
<div class="card">Border uses --brand: <code>var(--brand, fallback)</code></div>
Unlike preprocessor variables, custom properties are live: change one (even from JavaScript with el.style.setProperty("--brand", "red")) and everything using it updates.
Dark mode
Redefine the variables for dark mode — the components don't change at all:
:root { --bg: #ffffff; --text: #0f172a; }
@media (prefers-color-scheme: dark) {
:root { --bg: #0f172a; --text: #e2e8f0; }
}
body { background: var(--bg); color: var(--text); }
<style>
.theme { --bg: #ffffff; --text: #0f172a; --muted: #64748b; --accent: #2563eb;
background: var(--bg); color: var(--text); padding: 14px; border-radius: 10px; font: 14px system-ui; margin-bottom: 8px; border: 1px solid #e2e8f0; }
.theme.dark { --bg: #0f172a; --text: #e2e8f0; --muted: #94a3b8; --accent: #60a5fa; }
.theme small { color: var(--muted); }
.theme a { color: var(--accent); }
</style>
<div class="theme"><strong>Light theme</strong><br><small>Muted text</small> · <a href="#">Link</a></div>
<div class="theme dark"><strong>Dark theme — same markup</strong><br><small>Muted text</small> · <a href="#">Link</a></div>
This site uses exactly this technique for its colour themes.
Test yourself
How do you read a CSS custom property named --brand?
$brandvar(--brand)--brand()@brand
Show answer
Answer: var(--brand)