Dark mode is expected on modern sites, and CSS variables make it surprisingly easy. The trick is to theme with variables, not hard-coded colours — then switching themes is just swapping variable values.
Theme with variables, not fixed colours
The foundation: define every colour as a CSS variable on <code>:root</code>, and use <code>var()</code> everywhere. Now the whole site's colours live in one place, ready to swap.
<div class="box"><h3>Themed card</h3><p>Colours come from variables.</p></div>:root{--bg:#fff;--text:#0f172a;--muted:#64748b}
body{background:var(--bg)}
.box{background:var(--bg);color:var(--text);border:1px solid #e2e8f0;border-radius:12px;padding:18px}
.box p{color:var(--muted)}Respecting the system preference
Many users set a system-wide dark preference. The <code>prefers-color-scheme</code> media query lets you honour it automatically — override your variables inside it.
@media (prefers-color-scheme: dark) {
:root {
--bg: #0f172a;
--text: #f1f5f9;
--muted: #94a3b8;
}
}A manual toggle
To let users choose, override the variables on a data attribute or class set on the root element (e.g. <code>data-theme="dark"</code>). A tiny bit of JavaScript flips the attribute; CSS does the rest.
<div data-theme="dark" class="demo"><p>Dark themed via data-theme</p></div>.demo{--bg:#fff;--text:#0f172a;background:var(--bg);color:var(--text);padding:18px;border-radius:10px}
.demo[data-theme=dark]{--bg:#0f172a;--text:#f1f5f9}.card { background: #fff; color: #000; }.card { background: var(--bg); color: var(--text); }Hard-coding colours throughout, then trying to add dark mode later — you would have to rewrite every rule. Theme with variables from the start.
Theme with semantic variables (--bg, --text, --surface) rather than literal colours, honour prefers-color-scheme for first load, and offer a manual toggle that persists the choice. Always check contrast in both themes.
Define colour variables on :root, build a card that uses them, then create a dark override on [data-theme="dark"]. Toggle the attribute in your head to see it switch.
1. What makes theming easy?
2. Which respects the system dark preference?
3. How does a manual toggle usually work?
- Theme with CSS variables, not hard-coded colours.
- prefers-color-scheme honours the system dark setting.
- A manual toggle overrides variables on a root attribute/class.
- Check contrast in both light and dark themes.