Dark mode implementation
Dark Mode Implementation
Dark mode has become a standard feature in modern web design, reducing eye strain by lowering screen brightness. CSS3 offers multiple implementation approaches, ranging from basic color variables to media queries combined with system preferences.
Basic Color Variable Solution
Using CSS custom properties is the most flexible way to implement theme switching. Define two sets of color variables and toggle them via class names:
:root {
--bg-primary: #ffffff;
--text-primary: #333333;
--accent-color: #0066cc;
}
[data-theme="dark"] {
--bg-primary: #121212;
--text-primary: #e0e0e0;
--accent-color: #4dabf5;
}
body {
background-color: var(--bg-primary);
color: var(--text-primary);
transition: background 0.3s ease, color 0.3s ease;
}
JavaScript toggle logic example:
const toggle = document.querySelector('#theme-toggle');
toggle.addEventListener('click', () => {
document.documentElement.setAttribute(
'data-theme',
document.documentElement.getAttribute('data-theme') === 'dark' ? 'light' : 'dark'
);
});
Integrating System Preferences
CSS media queries can automatically match user system theme preferences:
@media (prefers-color-scheme: dark) {
:root {
--bg-primary: #121212;
--text-primary: #e0e0e0;
}
}
A more robust implementation should consider both manual user selection and system preferences:
// Detect system preference and set initial theme
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
document.documentElement.setAttribute('data-theme', 'dark');
}
// Listen for system theme changes
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => {
document.documentElement.setAttribute('data-theme', e.matches ? 'dark' : 'light');
});
Image and SVG Adaptation
Media assets require special handling in dark mode. Use CSS filters to adjust image brightness:
[data-theme="dark"] img:not(.no-filter) {
filter: brightness(0.8) contrast(1.2);
}
/* SVG icon color adaptation */
[data-theme="dark"] .icon {
fill: #9e9e9e;
}
For background images, use blend modes:
.hero {
background-image: url('day-image.jpg');
background-blend-mode: multiply;
}
[data-theme="dark"] .hero {
background-image: url('night-image.jpg');
}
Theming Complex Components
Form elements require special attention. Here's a custom checkbox adapted for dark mode:
.checkbox {
--checkbox-border: #757575;
--checkbox-bg: transparent;
}
[data-theme="dark"] .checkbox {
--checkbox-border: #9e9e9e;
--checkbox-bg: #424242;
}
.checkbox-input:checked + .checkbox-label::before {
background-color: var(--accent-color);
border-color: var(--accent-color);
}
Animation and Transition Optimization
Smooth transitions during theme switching enhance user experience:
:root {
--transition-duration: 0.4s;
--transition-easing: cubic-bezier(0.645, 0.045, 0.355, 1);
}
* {
transition: background-color var(--transition-duration) var(--transition-easing),
color var(--transition-duration) var(--transition-easing),
border-color var(--transition-duration) var(--transition-easing);
}
/* Exclude performance-sensitive elements */
img, svg, iframe {
transition: none !important;
}
Storing User Preferences
Use localStorage to persist user choices:
// Read stored preference during initialization
const savedTheme = localStorage.getItem('theme');
if (savedTheme) {
document.documentElement.setAttribute('data-theme', savedTheme);
}
// Save when toggling
function toggleTheme() {
const newTheme = document.documentElement.getAttribute('data-theme') === 'dark' ? 'light' : 'dark';
document.documentElement.setAttribute('data-theme', newTheme);
localStorage.setItem('theme', newTheme);
}
Advanced Color Schemes
Consider using HSL color space for more flexible theme adjustments:
:root {
--hue: 210;
--sat: 100%;
--light: 98%;
--text-hue: 0;
--text-sat: 0%;
--text-light: 20%;
}
[data-theme="dark"] {
--light: 15%;
--text-light: 90%;
}
.button {
background: hsl(var(--hue), var(--sat), var(--light));
color: hsl(var(--text-hue), var(--text-sat), var(--text-light));
}
Performance Optimization Techniques
Reduce repaint scope to improve switching performance:
/* Limit transitions to necessary elements */
body, .card, input, button {
transition: background-color 0.3s, color 0.3s;
}
/* Disable transitions for certain elements */
.no-transition, .no-transition * {
transition: none !important;
}
Accessibility Considerations
Ensure dark mode contrast meets WCAG standards:
[data-theme="dark"] {
--text-primary: #e0e0e0; /* At least 4.5:1 contrast on dark gray background */
}
/* Support for high contrast mode */
@media (prefers-contrast: more) {
:root {
--text-primary: #000000;
--bg-primary: #ffffff;
}
[data-theme="dark"] {
--text-primary: #ffffff;
--bg-primary: #000000;
}
}
Framework Integration Example
Implementing a theme toggle component in React:
import { useEffect, useState } from 'react';
function ThemeToggle() {
const [theme, setTheme] = useState(() => {
if (typeof window !== 'undefined') {
return localStorage.getItem('theme') ||
(window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
}
return 'light';
});
useEffect(() => {
document.documentElement.setAttribute('data-theme', theme);
localStorage.setItem('theme', theme);
}, [theme]);
return (
<button onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}>
{theme === 'dark' ? '☀️' : '🌙'}
</button>
);
}
Server-Side Rendering Handling
For SSR applications, inject the correct theme class during initial HTML rendering:
// Express middleware example
app.use((req, res, next) => {
const theme = req.cookies.theme ||
(req.headers['sec-ch-prefers-color-scheme'] === 'dark' ? 'dark' : 'light');
res.locals.theme = theme;
next();
});
// Usage in template engine
<html data-theme="<%= theme %>">
Gradient and Shadow Adjustments
Re-adjust shadow effects in dark mode:
.card {
--shadow-color: rgba(0, 0, 0, 0.1);
box-shadow: 0 2px 8px var(--shadow-color);
}
[data-theme="dark"] .card {
--shadow-color: rgba(0, 0, 0, 0.4);
border: 1px solid #333;
}
Dynamic Theme Generation
Generate complete theme schemes based on base colors:
function generateTheme(baseColor) {
const root = document.documentElement;
const hsl = hexToHSL(baseColor);
root.style.setProperty('--primary-hue', hsl.h);
root.style.setProperty('--primary-sat', `${hsl.s}%`);
root.style.setProperty('--primary-light', `${hsl.l}%`);
// Generate derivative colors
for (let i = 0; i < 10; i++) {
const lightness = 90 - (i * 8);
root.style.setProperty(`--primary-${i}`,
`hsl(${hsl.h}, ${hsl.s}%, ${lightness}%)`);
}
}
Testing and Validation
Use automated tools to verify theme switching:
describe('Dark Mode Toggle', () => {
before(() => {
cy.visit('/');
});
it('should switch themes', () => {
cy.get('html').should('have.attr', 'data-theme', 'light');
cy.get('#theme-toggle').click();
cy.get('html').should('have.attr', 'data-theme', 'dark');
cy.window().then(win => {
expect(win.localStorage.getItem('theme')).to.equal('dark');
});
});
});
Browser Compatibility Handling
Fallback solutions for older browsers:
/* Fallback for browsers without CSS variable support */
body {
background: #ffffff;
color: #333333;
}
/* Override for modern browsers */
@supports (--css: variables) {
body {
background: var(--bg-primary);
color: var(--text-primary);
}
}
Design System Integration
Integrate dark mode variables into design systems:
// _variables.scss
$themes: (
light: (
bg-primary: #ffffff,
text-primary: #333333,
border-color: #e0e0e0
),
dark: (
bg-primary: #121212,
text-primary: #e0e0e0,
border-color: #424242
)
);
// Theme mixer
@mixin theme() {
@each $theme, $map in $themes {
[data-theme="#{$theme}"] & {
$theme-map: $map !global;
@content;
$theme-map: null !global;
}
}
}
// Variable access function
@function themed($key) {
@return map-get($theme-map, $key);
}
// Usage example
.card {
@include theme {
background: themed('bg-primary');
border: 1px solid themed('border-color');
}
}
Mobile-Specific Handling
Optimize transitions for mobile devices:
/* Disable transitions on mobile to reduce performance overhead */
@media (hover: none) and (pointer: coarse) {
* {
transition-duration: 0.01ms !important;
}
}
Theme Synchronization Strategy
Sync theme state across multiple tabs:
// Main tab
window.addEventListener('storage', (event) => {
if (event.key === 'theme') {
document.documentElement.setAttribute('data-theme', event.newValue);
}
});
// Broadcast theme changes to other tabs
function broadcastTheme(theme) {
localStorage.setItem('theme', theme);
if (navigator.broadcastChannel) {
const bc = new BroadcastChannel('theme_channel');
bc.postMessage({ theme });
setTimeout(() => bc.close(), 500);
}
}
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn