One toggle inverts the whole page from light to dark. Centralizes colors as CSS custom properties so a single class flip swaps everything.
This whole frame swaps when you toggle. In a real site, the same toggle flips the entire page by adding .dark to <html>.
/* Define your colors as CSS custom properties */
:root {
--bg: #fff;
--text: #111;
--border: #e7e4dd;
--muted: #6b6862;
--accent: #d97757;
}
html.dark {
--bg: #0f1115;
--text: #e8e9ed;
--border: #2a2f3a;
--muted: #9098a4;
--accent: #f0a37b;
}
/* All elements consume the variables — nothing hardcoded */
body { background: var(--bg); color: var(--text); transition: background 0.3s, color 0.3s; }
const root = document.documentElement;
const btn = document.querySelector('#dark-toggle');
/* On load: respect saved choice OR system preference */
const saved = localStorage.getItem('theme');
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
if (saved === 'dark' || (!saved && prefersDark)) root.classList.add('dark');
btn.addEventListener('click', () => {
root.classList.toggle('dark');
localStorage.setItem('theme', root.classList.contains('dark') ? 'dark' : 'light');
});
Jono's masterclass suggests defaulting dark mode at night, light during the day:
const hour = new Date().getHours();
const isDayTime = hour >= 7 && hour < 19;
if (!localStorage.getItem('theme') && !isDayTime) root.classList.add('dark');
Your design system MUST use CSS custom properties for colors. If colors are hardcoded throughout the stylesheet, this is a massive refactor. If they're centralized at :root, this is a 10-line addition.
Source: Jono Catliff masterclass, 2026-05-19. See masterclass notes.