Most dark-mode tutorials reach for a library or a theme provider. You don't need one. CSS variables already do the whole job: define your colours once, swap the values when a toggle flips, and let every element follow.

Here is the complete theme from a working demo page I built:

:root{ --bg:#ffffff; --text:#16202e; --card:#eef2f7; --accent:#c9a227; } body.dark{ --bg:#0b1220; --text:#f5f0e1; --card:#16223a; --accent:#e7c34a; } 

That's the theme itself. Everything else on the page just uses the variables:

body{ background:var(--bg); color:var(--text); transition:background .3s, color .3s; } .card{ background:var(--card); } button{ background:var(--accent); } 

The toggle

One button, one class on <body>, and localStorage so the visitor's choice survives the next visit:

const b = document.body; if (localStorage.getItem('demo-theme') === 'dark') b.classList.add('dark'); document.getElementById('t').onclick = () => { b.classList.toggle('dark'); localStorage.setItem('demo-theme', b.classList.contains('dark') ? 'dark' : 'light'); }; 

Why this works so well

  • One source of truth. Backgrounds, text, cards and buttons all read the same few variables, so the dark theme can't drift out of sync with the light one.
  • No flash of the wrong theme is handled by applying the saved class before the page settles — the check runs immediately.
  • Zero dependencies. Nothing to install, nothing to break on the next framework update. It works in a static HTML file, a WordPress theme, or inside any framework's stylesheet.

Dark mode isn't just a trend — visitors stay longer on pages that are easy on their eyes at night. With variables, adding it is an afternoon's small change, not a project.

I'm Mamad Mirza, a full-stack developer in the UK. I build a small working project every day — follow along here, or work with me on LaborX.