Dark mode is expected in 2026 โ and with CSS variables it takes 20 lines total.
Step 1 โ tokens
:root { --bg: #ffffff; --text: #111827; --card: #f3f4f6; }
body.dark { --bg: #0a0a10; --text: #f4f4f6; --card: #16161f; }
body { background: var(--bg); color: var(--text); }Step 2 โ respect the OS by default
@media (prefers-color-scheme: dark) {
:root:not(.light-forced) { --bg: #0a0a10; --text: #f4f4f6; }
}Step 3 โ toggle that remembers
const saved = localStorage.getItem("theme");
if (saved === "dark") document.body.classList.add("dark");
btn.onclick = () => {
const dark = document.body.classList.toggle("dark");
localStorage.setItem("theme", dark ? "dark" : "light");
};Kill the flash of wrong theme
Put the localStorage check in a tiny inline <script> in the <head> โ before CSS paints โ so returning visitors never see a white flash.
Bonus polish: <meta name="color-scheme" content="dark light"> makes scrollbars and form controls match.