Dark Mode Toggle Using JavaScript: Complete Implementation Guide

Santaji GadeJavaScript2 days ago5 Views

Dark Mode Toggle

A dark mode toggle using JavaScript needs to check localStorage, then OS preference, then default — and apply it before the page even renders.

Technical SEO Dark Mode JavaScript 2026

A dark mode toggle using JavaScript needs to solve three things at once: read the user's saved preference, respect their OS setting when no preference exists yet, and apply the theme before the page actually renders to avoid a jarring flash of the wrong color scheme.

01Dark Mode Toggle Using JavaScript: The Core Pattern

The pattern practitioners call the "preference cascade" checks three sources in order: localStorage first, since it reflects an explicit user choice, then the prefers-color-scheme media query as a fallback, then a default light theme if neither applies.

Getting the initial page load right matters more than the toggle button itself. Applying the theme class before the page renders prevents what's commonly called FOUC, a flash of unstyled or incorrectly themed content that briefly shows the wrong colors before JavaScript corrects it.

3
sources checked in order: localStorage, then OS preference, then default
1
inline script needed in the head to prevent a flash of the wrong theme
0
external libraries required for a complete vanilla JS implementation
Advertisement
Advertisement

02Preventing the Flash of Wrong Theme

Callum's guide on DEV Community gets the ordering right: this inline script needs to sit at the very top of the head, executed as early as possible, before any CSS or content renders.

Inline Head Script: Prevent Flash of Wrong Theme
<script>
  document.documentElement.classList.toggle(
    'dark',
    localStorage.theme === 'dark' ||
    (!('theme' in localStorage) &&
      window.matchMedia('(prefers-color-scheme: dark)').matches)
  );
</script>
🔎 Did you know?

Ollie Williams' guide highlights a simpler native option many implementations skip: setting a meta tag with name="color-scheme" and content="light dark" lets the browser handle native form controls, scrollbars, and other built-in UI elements automatically, without any custom CSS needed for those specific pieces.

Advertisement
Advertisement

03The Toggle Button Function

Once the initial theme is set correctly, the toggle itself just needs to flip the class and save the new preference. whitep4nth3r's guide calls this the "preference cascade" in action, with the stored user preference always taking priority once it exists.

Full Toggle Implementation With Persistence
function getInitialTheme() {
  const saved = localStorage.getItem('theme');
  if (saved) return saved;

  return window.matchMedia('(prefers-color-scheme: dark)').matches
    ? 'dark'
    : 'light';
}

function applyTheme(theme) {
  document.documentElement.classList.toggle('dark', theme === 'dark');
  localStorage.setItem('theme', theme);
}

// Set initial theme on page load
applyTheme(getInitialTheme());

// Wire up the toggle button
document.getElementById('theme-toggle').addEventListener('click', () => {
  const isDark = document.documentElement.classList.contains('dark');
  applyTheme(isDark ? 'light' : 'dark');
});

04Pairing the Toggle With CSS Custom Properties

The JavaScript only manages one class, all actual color values should live in CSS custom properties. This keeps the toggle logic simple and makes adding a third theme, or adjusting colors later, a CSS-only change.

CSS Custom Properties for Theming
:root {
  --bg-color: #ffffff;
  --text-color: #1a1a1a;
  --border-color: #e0e0e0;
  color-scheme: light dark;
}

html.dark {
  --bg-color: #1a1a1a;
  --text-color: #f0f0f0;
  --border-color: #3a3a3a;
}

body {
  background-color: var(--bg-color);
  color: var(--text-color);
  transition: background-color 0.2s ease, color 0.2s ease;
}
Advertisement
Advertisement

05Listening for Live OS Theme Changes

Some users switch their OS theme while your site is still open. Abbey Perini's guide covers listening for this properly, but only when the user hasn't explicitly overridden the setting themselves, otherwise a manual choice would get silently overwritten.

Respond to Live OS Preference Changes
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');

mediaQuery.addEventListener('change', (e) => {
  // Only respond to OS changes if the user hasn't set an explicit preference
  if (!localStorage.getItem('theme')) {
    applyTheme(e.matches ? 'dark' : 'light');
  }
});

💡 Quick Tip: test this behavior using Chrome DevTools' Rendering panel, which lets you emulate prefers-color-scheme without actually changing your OS settings, making it far faster to verify both branches of the logic.

06Storage Approach Comparison

A quick reference for the different ways to persist a theme preference.

Storage MethodPersists AcrossBest For
localStorageSessions, same browser onlyMost sites, no backend required
Cookie + server-sideDevices, if tied to a user accountLogged-in apps wanting cross-device sync
prefers-color-scheme onlyNothing explicit, follows OS alwaysSimple sites with no manual override needed

07Implementation Checklist

A short list to confirm before shipping a dark mode toggle to production.

Inline the initial theme script in the head, applying it after first paint causes a visible flash.

Check localStorage before OS preference, an explicit user choice should always win.

Keep all colors in CSS custom properties, never hardcode colors that need to change per theme.

Set color-scheme in CSS, this helps native browser UI like scrollbars and form controls match automatically.

Only listen for OS changes when no explicit choice exists, otherwise a manual toggle gets silently overridden.

08Common Questions

This is the flash of wrong theme (FOUC) problem. It happens when the theme is applied after the page renders. Fix it by inlining a small script at the very top of the head that runs before first paint.

Check localStorage first. If the user has explicitly chosen a theme before, that choice should always take priority over their current OS-level setting.

CSS alone with prefers-color-scheme works if you only want to follow the OS setting. JavaScript is needed if you want to let users manually override that setting and remember their choice.

It tells the browser to automatically theme native UI elements, like scrollbars, form inputs, and date pickers, to match the current color scheme without needing custom CSS for each one.

Chrome DevTools has a Rendering panel that lets you emulate prefers-color-scheme directly in the browser, letting you test both light and dark branches without touching your actual system settings.

What We Learn Today

Check localStorage, then OS preference, then default

Inline the initial theme script to prevent a visible flash

Keep colors in CSS custom properties, not JavaScript

Set color-scheme so native browser UI matches automatically

Only auto-follow OS changes when no explicit choice exists

DevTools can emulate prefers-color-scheme for easier testing

Build a Complete Front-End JavaScript Toolkit

Dark mode pairs well with other content UX features like scrollspy highlighting and auto-generated navigation. Explore both guides next.

0 Votes: 0 Upvotes, 0 Downvotes (0 Points)

Leave a reply

Loading Next Post...
Search
Popular Now
Loading

Signing-in 3 seconds...

Signing-up 3 seconds...