Dark Mode Done Right
Dark mode isn't just a trend — it's an accessibility feature. Here's how we built it properly in Project Distri.
The Challenge
Most dark mode implementations suffer from:
- Flash of wrong theme on page load
- Janky transitions between themes
- Inconsistent colors across components
- Missing system preference detection
Our Solution
We use a three-part approach:
1. CSS Custom Properties
All colors are defined as CSS custom properties in :root (light) and [data-theme="dark"] (dark):
:root {
--color-bg: #ffffff;
--color-text: #0f1729;
--color-primary: #6366f1;
}
[data-theme="dark"] {
--color-bg: #0b0f1a;
--color-text: #f1f5f9;
--color-primary: #818cf8;
}
2. Inline Script
A blocking script runs before React hydration to set the initial theme:
(function() {
var saved = localStorage.getItem('theme');
var preferred = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
document.documentElement.setAttribute('data-theme', saved || preferred);
})();
3. React Context
A lightweight ThemeProvider manages state and persistence without any external dependencies.
The Result
- Zero flicker on page load
- Smooth transitions via CSS
transitionon all color properties - System preference detection as default
- localStorage persistence across sessions
- < 1KB added to the bundle