Dark Mode Toggle with Local Storage

November 25, 2025

Drop-In Dark Mode Toggle with CSS Variables & localStorage

Dark mode has gone from “nice extra” to “basic comfort feature” on modern sites. You don’t always want to rebuild your entire theme just to offer it, though. This snippet gives you a clean, copy-paste way to add a dark/light switch that feels native instead of like another heavy plugin.

It plugs into your existing styles by leaning on CSS variables, listens to the visitor’s system preference, and remembers their choice using localStorage. The toggle itself is a tiny floating button that lives in the corner and stays out of the way until someone wants it.

What this snippet does

Give any site a modern dark mode in one shot. This tiny snippet:

  • Uses CSS variables for colors (no redesign needed)
  • Respects the visitor’s system preference (light or dark)
  • Remembers their choice with localStorage
  • Adds a small ☾/☀ toggle button in the bottom-right corner

No frameworks, no build tools, no tracking — just a clean, vibe-coded switch your visitors will actually use. 🌙

How it works under the hood

The approach is simple:

  • Define base colors on :root using CSS custom properties like --bg-color and --text-color.
  • Add a [data-theme="dark"] rule that overrides those variables when the document element is in dark mode.
  • Apply those variables to body so your background and text pick up the right values automatically.
  • Use a small JavaScript helper to:
    • Check localStorage for a saved theme.
    • Fall back to prefers-color-scheme: dark if nothing is stored yet.
    • Switch the data-theme attribute and update the toggle label.

Because the colors live in variables, you can gradually update more parts of your design to reference them: buttons, cards, headers, and so on. Dark mode then becomes a single theme switch instead of a pile of separate overrides.

Copy-paste snippet

Drop this near the end of your <body> (or in your main template) on any page you want to support dark mode:

<style>
:root {
  --bg-color: #ffffff;
  --text-color: #111111;
}

[data-theme="dark"] {
  --bg-color: #050816;
  --text-color: #f9fafb;
}

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

/* Floating toggle button */
.dark-toggle {
  position: fixed;
  right: 16px;
  bottom: 16px;
  padding: 6px 12px;
  font-size: 13px;
  border-radius: 999px;
  border: 1px solid rgba(0, 0, 0, 0.2);
  background: rgba(255, 255, 255, 0.9);
  cursor: pointer;
  z-index: 9999;
}

[data-theme="dark"] .dark-toggle {
  background: rgba(15, 23, 42, 0.9);
  color: #f9fafb;
  border-color: rgba(148, 163, 184, 0.9);
}
</style>

<button type="button" class="dark-toggle" aria-label="Toggle dark mode">☾ Dark</button>

<script>
(function () {
  var storageKey = 'vibe-theme';
  var root = document.documentElement;
  var btn;

  function applyTheme(theme) {
    root.setAttribute('data-theme', theme);

    try {
      localStorage.setItem(storageKey, theme);
    } catch (e) {
      // localStorage disabled; ignore
    }

    if (btn) {
      btn.textContent = theme === 'dark' ? '☀ Light' : '☾ Dark';
    }
  }

  function getPreferredTheme() {
    var stored = null;

    try {
      stored = localStorage.getItem(storageKey);
    } catch (e) {
      stored = null;
    }

    if (stored === 'light' || stored === 'dark') {
      return stored;
    }

    if (window.matchMedia &&
        window.matchMedia('(prefers-color-scheme: dark)').matches) {
      return 'dark';
    }

    return 'light';
  }

  document.addEventListener('DOMContentLoaded', function () {
    btn = document.querySelector('.dark-toggle');
    if (!btn) return;

    applyTheme(getPreferredTheme());

    btn.addEventListener('click', function () {
      var current = root.getAttribute('data-theme');
      var next = current === 'dark' ? 'light' : 'dark';
      applyTheme(next);
    });
  });
})();
</script>

How to use it

  1. Paste the snippet above right before </body> on your page or layout.
  2. Optionally tweak the --bg-color and --text-color values for your own light/dark palette.
  3. Reload your site and click the toggle in the bottom-right to switch between ☾ Dark and ☀ Light. The choice will stick on refresh.

Customizing the palette

Out of the box, the snippet only changes the page background and base text color. To extend it:

  • Introduce more variables like --card-bg, --border-color, or --accent.
  • Use those variables in your existing CSS rules instead of hard-coded hex values.
  • In the [data-theme="dark"] block, override each variable with a darker-friendly value.

You can start small by just darkening the background and then gradually migrating other pieces of your design as you have time.

Accessibility & UX notes

The toggle uses an aria-label so screen readers can announce what it does, and the button text flips between “☾ Dark” and “☀ Light” so the state is always clear. To make it feel even more polished, consider:

  • Checking color contrast for both themes so text stays readable.
  • Keeping the transition quick and subtle so the change doesn’t feel jarring.
  • Placing the button where it doesn’t cover critical UI on small screens.

Because preferences are stored per browser using localStorage, visitors get a consistent experience on repeat visits, but you’re not setting any cookies or sending data anywhere else.

That’s it — a lightweight dark mode toggle that feels like a native feature, not another bulky widget.

Comments (1)

  • anonymous👌
    Dec 12, 2025 7:24 PM
    found this one useful

← Back to all scripts