Dark Mode Implementation
Implementing theme switching with CSS custom properties, system preference detection, and persistence
Dark Mode Implementation
You are an AI agent that implements theme switching correctly. You use CSS custom properties for theming, detect system preferences, persist user choices, prevent flash of wrong theme, and ensure all visual elements adapt properly including images and icons.
Philosophy
Dark mode is not just inverting colors. It is a complete design system concern that touches every visual element in the application. A proper implementation respects user preferences, transitions smoothly, persists across sessions, and never flashes the wrong theme on load. It must work from the CSS level to the image level, with every component aware of the current theme.
Techniques
Use CSS Custom Properties for Theming
- Define all colors as CSS custom properties on
:rootor a theme class. - Switch themes by changing the values of these properties.
- Organize properties semantically:
--color-background,--color-text-primary, not--white,--black. - Include properties for surfaces, borders, shadows, and interactive states.
- Use a naming convention that describes purpose, not appearance.
:root {
--color-bg-primary: #ffffff;
--color-text-primary: #1a1a1a;
--color-border: #e0e0e0;
}
[data-theme="dark"] {
--color-bg-primary: #1a1a1a;
--color-text-primary: #e0e0e0;
--color-border: #333333;
}
Detect System Preferences
- Use the
prefers-color-schememedia query to detect the OS setting. - In CSS:
@media (prefers-color-scheme: dark) { ... }. - In JavaScript:
window.matchMedia('(prefers-color-scheme: dark)'). - Listen for changes with
matchMedia.addEventListener('change', callback). - Use the system preference as the default when no user preference is stored.
Persist Theme Choice
- Store the user's theme preference in localStorage.
- Check localStorage before rendering to apply the correct theme immediately.
- Provide three options: Light, Dark, System (follow OS preference).
- Sync the preference across tabs using the
storageevent. - Fall back to system preference when no stored preference exists.
Prevent Flash of Wrong Theme
- Apply the theme in a blocking script in the
<head>before any rendering. - Read localStorage synchronously and set the theme attribute on
<html>immediately. - Do not rely on React/framework hydration to set the theme, as it happens too late.
- For server-rendered pages, use cookies to send the theme preference with the request.
- This script must be inline, not in an external file that requires a network request.
Adapt Images and Icons for Themes
- Use
filter: invert(1)for simple icon color inversion. - Provide separate image assets for light and dark themes when inversion is not sufficient.
- Use
<picture>with<source media="(prefers-color-scheme: dark)">for theme-aware images. - SVG icons should use
currentColorto inherit the text color. - Adjust image brightness and contrast in dark mode to reduce eye strain.
Implement Smooth Theme Transitions
- Add a CSS transition on color properties:
transition: color 0.2s, background-color 0.2s. - Apply the transition class temporarily during theme switch, then remove it.
- Do not apply transitions on page load to prevent initial animation.
- Keep transitions short (150-200ms) to feel responsive.
- Transition only color-related properties to avoid layout thrashing.
Handle Component-Level Theme Awareness
- Third-party components may need explicit theme configuration.
- Code syntax highlighting themes must switch with the application theme.
- Charts and data visualizations need theme-aware color palettes.
- Map and embed components may have their own theme APIs.
- Test every component in both themes to catch hardcoded colors.
Best Practices
- Design the dark theme intentionally, not by just inverting the light theme.
- Use slightly lower contrast in dark mode to reduce eye strain (not pure white on pure black).
- Reduce shadow intensity in dark mode; use lighter borders or subtle glows instead.
- Test both themes at every stage of development, not as an afterthought.
- Ensure sufficient contrast ratios (WCAG AA: 4.5:1 for text) in both themes.
- Include the theme toggle in an easily accessible location.
- Test the theme toggle rapidly to verify transitions are smooth and state is correct.
Anti-Patterns
- Hardcoded colors: Using
color: #333instead ofcolor: var(--color-text-primary)throughout the codebase. - Flash of light theme: Page loads in light mode and then switches to dark, causing a white flash.
- Forgotten components: Most of the app supports dark mode, but some pages or modals do not.
- Pure black backgrounds: Using
#000000for dark mode backgrounds, which creates excessive contrast. - Opacity for darkening: Using opacity to darken elements, which also makes them transparent.
- Lost preference: User sets dark mode, refreshes, and sees light mode briefly before it switches.
- No system option: Forcing users to manually toggle instead of following their OS preference.
- Invisible elements: Icons or borders that disappear against the dark background because they were designed only for light mode.
Related Skills
Abstraction Control
Avoiding over-abstraction and unnecessary complexity by choosing the simplest solution that solves the actual problem
Accessibility Implementation
Making web content accessible through ARIA attributes, semantic HTML, keyboard navigation, screen reader support, color contrast, focus management, and WCAG compliance.
API Design Patterns
Designing and implementing clean APIs with proper REST conventions, pagination, versioning, authentication, and backward compatibility.
API Integration
Integrating with external APIs effectively — reading API docs, authentication patterns, error handling, rate limiting, retry with backoff, response validation, SDK vs raw HTTP decisions, and API versioning.
Assumption Validation
Detecting and validating assumptions before acting on them to prevent cascading errors from wrong guesses
Authentication Implementation
Implementing authentication flows correctly including OAuth 2.0/OIDC, JWT handling, session management, password hashing, MFA, token refresh, and CSRF protection.