JavaScript

Dark / Light Mode

Add a theme switch to any site in four small steps. You'll use CSS variables for the colors, one line of JavaScript to flip them, localStorage to remember the choice, and the OS setting as a smart default.

Why

Why offer a dark mode?

Eye comfort

Dark UIs are easier on the eyes in low light and reduce glare at night.

Battery

On OLED screens, dark pixels use less power — real savings on phones.

Preference

Lots of people simply prefer it. Letting them choose feels considerate.

Respect the OS

Phones and laptops already have a system setting — a good site honors it.

See it work

Try the finished toggle

Here's exactly what we're building — a mini page with a theme button. Click the moon/sun to flip it. Every color comes from a CSS variable, so the whole UI changes at once.

Beautiful in any light.

One toggle, two themes, zero fuss.

Card

This surface, the text, and the button all read from variables.

Get started
Click the circle in the top-right
Step 1

Put your colors in variables

The secret to dark mode: never hard-code colors. Define them once as CSS variables on :root (that's the light theme), then redefine the same variables under a [data-theme="dark"] selector. Your components read var(--…) and never need to know which theme is on.

CSS
:root {
  --bg:      #ffffff;
  --text:    #1d1d1f;
  --surface: #f4f4f7;
  --accent:  #6366f1;
}

/* dark mode = the same names, different values */
:root[data-theme="dark"] {
  --bg:      #0f172a;
  --text:    #f1f5f9;
  --surface: #1e293b;
  --accent:  #818cf8;
}

body  { background: var(--bg); color: var(--text); }
.card { background: var(--surface); }
.btn  { background: var(--accent); color: #fff; }
New to variables? They're called design tokens. See the Design Tokens lesson for the full idea.
Step 2

Add the toggle button

Switching themes is just adding or removing data-theme="dark" on the <html> element. One button, one line of JavaScript.

HTML
<button id="themeBtn" aria-label="Toggle theme">🌙</button>
JavaScript
const root = document.documentElement;   // the <html> tag

document.getElementById('themeBtn').addEventListener('click', () => {
  const isDark = root.getAttribute('data-theme') === 'dark';
  root.setAttribute('data-theme', isDark ? 'light' : 'dark');
});
That's the whole switch. Because every color is a variable, flipping one attribute re-themes the entire page instantly.
Step 3

Remember the choice

Right now the theme resets on every reload. Save it to localStorage when it changes, and read it back when the page loads.

JavaScript
const root = document.documentElement;

// 1. on load, apply whatever was saved last time
const saved = localStorage.getItem('theme');
if (saved) root.setAttribute('data-theme', saved);

// 2. on click, flip it AND save it
document.getElementById('themeBtn').addEventListener('click', () => {
  const next = root.getAttribute('data-theme') === 'dark' ? 'light' : 'dark';
  root.setAttribute('data-theme', next);
  localStorage.setItem('theme', next);   // remember it
});
Step 4

Respect the system setting

If someone has never picked a theme, fall back to whatever their device is set to. The prefers-color-scheme media query tells you, and you can read it from JavaScript with matchMedia.

JavaScript
const root  = document.documentElement;
const saved = localStorage.getItem('theme');

// saved choice wins; otherwise follow the operating system
const systemDark = matchMedia('(prefers-color-scheme: dark)').matches;
root.setAttribute('data-theme', saved || (systemDark ? 'dark' : 'light'));

You can also let pure CSS follow the system with no JavaScript at all — handy as a baseline:

CSS
@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) {   /* only if the user hasn't chosen */
    --bg: #0f172a; --text: #f1f5f9;
  }
}
All together

The complete, copy-paste version

Drop this into any page. It applies the saved or system theme on load, toggles on click, remembers the choice, and swaps the button icon.

HTML
<!-- HTML -->
<button id="themeBtn" aria-label="Toggle theme">
  <span class="moon">🌙</span><span class="sun">☀️</span>
</button>
CSS
/* CSS */
:root            { --bg:#fff;    --text:#1d1d1f; --accent:#6366f1; }
:root[data-theme="dark"] { --bg:#0f172a; --text:#f1f5f9; --accent:#818cf8; }
body { background: var(--bg); color: var(--text); transition: background .3s, color .3s; }
.sun { display: none; }
[data-theme="dark"] .moon { display: none; }
[data-theme="dark"] .sun  { display: inline; }
JavaScript
// JS — run it as early as possible to avoid a flash
const root = document.documentElement;
const saved = localStorage.getItem('theme');
const systemDark = matchMedia('(prefers-color-scheme: dark)').matches;
root.setAttribute('data-theme', saved || (systemDark ? 'dark' : 'light'));

document.getElementById('themeBtn').addEventListener('click', () => {
  const next = root.getAttribute('data-theme') === 'dark' ? 'light' : 'dark';
  root.setAttribute('data-theme', next);
  localStorage.setItem('theme', next);
});
Polish

Make it feel right

Related: Design Tokens for the variables idea, and Color for picking palettes that work both ways.