Dark Mode Toggle

Theme switching styles using CSS light-dark() and the data-mode attribute. Supports automatic, light-only, and dark-only modes.

Overview

Vanilla Breeze uses the CSS light-dark() function for automatic theme switching. This snippet shows three approaches:

  1. Automatic - Follows OS/browser preference (default)
  2. Manual toggle - Switch via data-mode attribute
  3. Three-way - Light / System / Dark selector

Live Demo

Try the toggle button and three-way selector:

Base Setup

Enable light-dark support and define mode overrides:

:root { /* Enable automatic light/dark switching */ color-scheme: light dark; } /* Force light mode */ :root[data-mode="light"] { color-scheme: light; } /* Force dark mode */ :root[data-mode="dark"] { color-scheme: dark; /* Adjust colors that need dark-mode tuning */ --color-primary: oklch(65% 0.18 var(--hue-primary)); /* Darker shadows for dark backgrounds */ --shadow-sm: 0 1px 2px oklch(0% 0 0 / 0.3); --shadow-md: 0 4px 6px oklch(0% 0 0 / 0.4); --shadow-lg: 0 10px 15px oklch(0% 0 0 / 0.4); }

Toggle Button Styles

Style a theme toggle button with icon swapping:

.theme-toggle { --toggle-size: var(--size-xl); display: inline-flex; align-items: center; justify-content: center; padding: var(--size-xs); background: transparent; border: none; border-radius: var(--radius-m); cursor: pointer; color: var(--color-text-muted); transition: color var(--duration-fast) var(--ease-out), background var(--duration-fast) var(--ease-out); } .theme-toggle:hover { color: var(--color-text); background: var(--color-surface-raised); } /* Show sun in dark mode, moon in light mode */ .theme-toggle .icon-sun { display: none; } .theme-toggle .icon-moon { display: block; } :root[data-mode="dark"] .theme-toggle .icon-sun { display: block; } :root[data-mode="dark"] .theme-toggle .icon-moon { display: none; } /* Auto-detect system preference */ @media (prefers-color-scheme: dark) { :root:not([data-mode]) .theme-toggle .icon-sun { display: block; } :root:not([data-mode]) .theme-toggle .icon-moon { display: none; } }

JavaScript

Minimal JavaScript for toggle functionality with localStorage persistence:

// Check for saved preference or system preference const getTheme = () => { const saved = localStorage.getItem('theme'); if (saved) return saved; return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'; }; // Apply theme const setTheme = (theme) => { document.documentElement.dataset.mode = theme; localStorage.setItem('theme', theme); }; // Initialize setTheme(getTheme()); // Toggle handler document.querySelector('.theme-toggle')?.addEventListener('click', () => { const current = document.documentElement.dataset.mode; setTheme(current === 'dark' ? 'light' : 'dark'); });

Three-Way Selector

For users who want explicit Light / System / Dark options:

.theme-switcher { display: flex; gap: var(--size-2xs); padding: var(--size-2xs); background: var(--color-surface-sunken); border-radius: var(--radius-m); } .theme-switcher button { padding: var(--size-xs) var(--size-s); background: transparent; border: none; border-radius: var(--radius-s); cursor: pointer; color: var(--color-text-muted); font-size: var(--font-size-sm); } .theme-switcher button:hover { color: var(--color-text); } .theme-switcher button[aria-pressed="true"] { background: var(--color-surface); color: var(--color-text); box-shadow: var(--shadow-sm); } <div class="theme-switcher" role="group" aria-label="Theme"> <button aria-pressed="false">Light</button> <button aria-pressed="true">System</button> <button aria-pressed="false">Dark</button> </div>

How light-dark() Works

The CSS light-dark() function returns different values based on the computed color-scheme:

:root { color-scheme: light dark; /* First value for light mode, second for dark */ --color-background: light-dark(white, #1a1a1a); --color-text: light-dark(#1a1a1a, white); } /* Usage */ body { background: var(--color-background); color: var(--color-text); }

This is simpler than media queries because it automatically responds to both OS preferences AND manual color-scheme overrides.

Related

Tokens Starter

Color tokens with light-dark() setup

theme-picker Component

Built-in theme switching component

MDN: light-dark()

CSS light-dark() function reference