Snippets
Dark Mode Toggle
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:
Automatic - Follows OS/browser preference (default)
Manual toggle - Switch via data-mode attribute
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.