Dark Mode Toggle — Free HTML CSS JS Snippet

Dark Mode Toggle · Animations · Plain HTML, CSS & JS · Live preview

Share & Support

What's included

Features

classList.toggle(".dark") on the root element — one JS call, all rules respond
transition: background 0.3s + color 0.3s on themed elements — smooth theme switch
.dark selector prefix — predictable, extendable dark-mode CSS pattern
Toggle button with sun/moon icon swap via .dark .icon-dark display none/block
Card background, text, borders all theme-aware
One-line JS function: function switchTheme() { element.classList.toggle("dark"); }
Export as HTML file, React JSX, or React + Tailwind CSS
Mobile (375px), Tablet (768px), Desktop device preview buttons
Live split-pane editor — preview updates as you type
Compatible with localStorage persistence and @media prefers-color-scheme

About this UI Snippet

Dark Mode Toggle — .dark Class on Root, CSS Transitions & localStorage

Screenshot of the Dark Mode Toggle snippet rendered live

A dark mode toggle lets users switch between light and dark colour schemes — see also the icon-based color mode toggle and the multi-theme color theme switcher. It is a standard feature on every modern web product — documentation sites, dashboards, developer tools, and any product used in low-light environments. This snippet demonstrates the most common implementation pattern: a .dark class on the root element, CSS selectors for both themes, and smooth transitions between states.

The .dark class pattern

function switchTheme() { document.getElementById('page').classList.toggle('dark'); } toggles the .dark class on the root container (drive it from a styled toggle switch). All theme-aware CSS rules use the .dark prefix selector: .dark .card { background: #1e293b; }. This is clean, predictable, and easy to extend — any new element gets dark mode support by adding a .dark .element rule.

CSS transitions between themes

All themed elements have transition: background 0.3s and transition: color 0.3s. When .dark is toggled, all property changes animate simultaneously over 300ms. This produces a smooth blend between light and dark rather than an instant snap.

Saving preference with localStorage

To persist the user's choice, add localStorage.setItem('theme', 'dark') when switching to dark and localStorage.setItem('theme', 'light') when switching back. On page load, read localStorage.getItem('theme') and apply the class if it equals 'dark'. This restores the theme preference across sessions without a database.

Using CSS custom properties

For a larger project, replace hardcoded colours with CSS custom properties: :root { --bg: #f1f5f9; --surface: #fff; } and :root.dark { --bg: #0f172a; --surface: #1e293b; }. All elements reference var(--bg) and var(--surface). Toggling .dark on :root changes all variables at once.

Respecting OS preference

Use @media (prefers-color-scheme: dark) to apply dark mode automatically based on the OS setting. Combine with the localStorage preference: if the user has explicitly set a preference, honour it; otherwise, follow the OS.

The :root CSS variable swap

When the toggle switches to dark mode, a .dark class is added to the body or :root element. CSS rules under .dark override the default CSS variable values: .dark { --bg: #0f172a; --text: #f1f5f9; --surface: #1e293b; }. All components that use these variables automatically update — no component-specific dark mode logic needed. This is why CSS custom properties are the recommended approach for theming.

localStorage persistence

The toggle reads the user's preference from localStorage on page load: const saved = localStorage.getItem('color-scheme'). On toggle, it writes the new value. To prevent a flash of wrong theme on load, add an inline script in the document <head> (before any CSS) that reads localStorage and adds the .dark class immediately: <script>if(localStorage.getItem('color-scheme')==='dark')document.documentElement.classList.add('dark')</script>.

The CSS knob animation

The toggle knob uses position: absolute and transition: left 0.2s. In light mode, left: 3px places it on the left; in dark mode, left: calc(100% - 23px) places it on the right. The toggle background transitions from grey to indigo simultaneously. Both transitions have the same 0.2s duration so they move in sync.

Build with AI

Build, Understand, Optimize, and Extend It With AI

You don't have to guess why this toggle scales cleanly to a whole app. Paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain exactly how the single classList.toggle call on the root element cascades through every .dark-prefixed selector, and why that pattern scales better than toggling inline styles on individual elements. The same assistant can help optimize it — ask whether migrating the hardcoded hex colors to CSS custom properties on :root would reduce the number of selectors that need a .dark override, and how to avoid a flash of the wrong theme on page load when reading a saved preference from localStorage. It's also useful for extending the toggle: ask it to add a three-way switch for light, dark, and system-following modes, sync the choice across browser tabs using the storage event, or drive the color-scheme meta tag so native form controls and scrollbars match the chosen theme too. Treat the code less like a finished artifact and more like a starting point for a conversation.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a dark mode toggle in plain HTML, CSS, and JavaScript that persists the user's choice and avoids a flash of the wrong theme — no framework, no library.

Requirements:
- A single toggle button that calls classList.toggle("dark") on one root container element, with every themed element's dark styling written as a ".dark .element" prefixed CSS rule rather than duplicating full styles.
- Add transition: background 0.3s, color 0.3s (or similar) on every themed element so switching themes animates smoothly rather than snapping instantly.
- A sliding knob inside the toggle button that moves via a CSS transform (not a left/top position change) when the .dark class is present, with a synchronized-duration transition so the knob and the track background finish animating at the same time.
- Swap between a sun icon and a moon icon inside the knob based on the .dark class, using CSS display toggling driven purely by the class, not JavaScript re-rendering the icon.
- Persist the chosen theme to localStorage on every toggle, and on page load read that stored value and apply the .dark class before the first paint — using an inline script placed in the document head, before any stylesheet — so returning visitors never see a flash of the opposite theme.
- Add a fallback for first-time visitors with no stored preference: check window.matchMedia("(prefers-color-scheme: dark)") and apply .dark automatically if the OS is set to dark, while still letting the manual toggle override it afterward.

Step by step

How to Use

  1. 1
    Click the toggle in the previewClick the toggle button to switch between light and dark. All elements transition simultaneously over 300ms.
  2. 2
    Update the colour valuesIn the CSS panel, update the light-mode colours on elements and the .dark overrides to match your palette.
  3. 3
    Add more dark-mode rulesFor each new element, add a .dark .element { } rule with dark-mode colour values. The JS toggle applies to all of them.
  4. 4
    Persist preferenceIn the JS panel, add localStorage.setItem("theme", isDark ? "dark" : "light") after the toggle. On load, read it and apply the class.
  5. 5
    Move to CSS custom propertiesReplace hardcoded colours with CSS variables. Define :root { --bg: #f1f5f9 } and :root.dark { --bg: #0f172a }. Reference var(--bg) everywhere.
  6. 6
    Export in your formatClick "HTML" for a standalone file, "JSX" for a React component, or "Tailwind" for a React + Tailwind version.

Real-world uses

Common Use Cases

Dashboard and productivity apps
Add dark mode to any dashboard. The .dark class pattern scales to unlimited elements without changing the JS.
Documentation and developer tool sites
Developer tools universally offer dark mode. This snippet provides the full UI — toggle button, icon swap, smooth transition.
Learn .dark class pattern and CSS transitions
Edit the .dark CSS rules and transition durations in the CSS panel to understand how theme switching and smooth transitions work together.
Portfolio and personal sites
Add a dark mode toggle to a developer portfolio. Persist the preference with localStorage so returning visitors see the theme they chose.
Upgrade to CSS custom properties
Use this snippet as a starting point and refactor to CSS variables. The .dark class on :root drives all variables — one toggle, entire theme change.
OS-aware dark mode with manual override
Combine with @media (prefers-color-scheme: dark) for automatic OS detection and localStorage for manual override. Users get their preferred default with the option to change.

Got questions?

Frequently Asked Questions

function switchTheme() toggles the .dark class on the root container element. All CSS dark-mode rules use .dark as a prefix selector — .dark .card { background: #1e293b; }. When the class is added, the browser applies the dark overrides; when removed, it reverts to the light values.

background and color transitions are set on all themed elements: transition: background 0.3s, color 0.3s. When .dark is toggled, all matching properties animate from their current value to the new value over 300ms simultaneously.

Add localStorage.setItem("theme", "dark") when switching to dark mode and "light" when switching back. On page load, read localStorage.getItem("theme") and add the .dark class if it equals "dark". This restores the preference without a server-side cookie.

Define :root { --bg: #f1f5f9; --surface: #fff; --text: #1e293b; } and :root.dark { --bg: #0f172a; --surface: #1e293b; --text: #f1f5f9; }. Reference var(--bg) on elements. Toggle .dark on :root — all variables update at once.

Add @media (prefers-color-scheme: dark) { .page { /* dark styles */ } } in the CSS. On page load in JS, check !localStorage.getItem("theme") && window.matchMedia("(prefers-color-scheme: dark)").matches and apply .dark if true.

Yes. Click "JSX" for a React component. In React, use useState for the dark/light boolean, useEffect to persist to localStorage on change, and useEffect on mount to read localStorage and apply the initial theme.