Implementing Dark Mode with CSS Variables and React
Dark mode improves readability in lowâlight environments and reduces eye strain, making it a valued feature in financial dashboards, eâcommerce sites, and support portals. The most maintainable approach uses CSS custom properties (variables) to define color values and a class toggle on the <html> or <body> element to switch themes. By defining light and dark palettes as variables, you avoid duplicating styles and keep the CSS concise. Pair this with a prefersâcolorâscheme media query to respect the userâs system setting on first visit, and persist the choice in localStorage so the preference survives reloads. This method works with any CSS methodologyâplain CSS, CSS modules, or CSSâinâJSâand requires minimal JavaScript to toggle the class and store the choice. It also integrates smoothly with accessibility tools, as you can ensure sufficient contrast in both palettes. Treating theming as a designâsystem concern keeps component code clean and makes future theme adjustments (e.g., adding a highâcontrast mode) straightforward.
đ»Source Code
/* styles.css â define colors as CSS variables */
:root {
/* Light theme (default) */
--bg-primary: #ffffff;
--bg-secondary: #f8fafc;
--text-primary: #111827;
--text-secondary: #6b7280;
--accent: #2563eb;
}
/* Dark theme â applied when .dark class is on root */
.dark {
--bg-primary: #111827;
--bg-secondary: #1f2937;
--text-primary: #f9fafb;
--text-secondary: #d1d5db;
--accent: #3b82f6;
}
/* Use variables throughout your styles */
body {
background: var(--bg-primary);
color: var(--text-primary);
transition: background 0.2s, color 0.2s;
}
.container {
background: var(--bg-secondary);
border: 1px solid var(--text-secondary);
}
.button {
background: var(--accent);
color: white;
border: none;
padding: 0.5rem 1rem;
border-radius: 0.375rem;
}
.button:hover {
background: var(--accent);
opacity: 0.9;
}
/* Optional: respect system preference on first load */
@media (prefers-color-scheme: dark) {
:root:not(.dark) {
--bg-primary: #111827;
--bg-secondary: #1f2937;
--text-primary: #f9fafb;
--text-secondary: #d1d5db;
--accent: #3b82f6;
}
}đRelated Snippets
Similar code snippets you might find interesting
Using Intersection Observer for Lazy Loading and Scroll-Based Animations
Leveraging Astro Islands for Interactive Content-Heavy Sites
Maintaining a Simple Global State Store in React Without Extra Libraries
đŹComments (0)
đPlease login to post comments
No comments yet. Be the first to share your thoughts!
âĄActions
Share this snippet:
đ€About the Author
admin
Active contributor
