
Dark mode implementation: CSS variables, system settings, design
How to build dark mode properly: colour tokens as CSS variables, prefers-color-scheme, a toggle without flashing, contrast, images and common mistakes.
In short: Good dark mode starts with semantic colour tokens as CSS variables that take different values per mode. By default the site follows the operating system through prefers-color-scheme, and a toggle with light, dark and system options overrides it. To avoid a flash on load, the theme has to be set before the browser paints. On the design side, a dark theme is a palette of its own with adjusted contrast, and an inverted light theme will not get you there.
Why dark mode is more than swapping colours
Many people set their operating system to dark and expect websites and web apps to follow. Anyone who opens a glaring white page at night notices the difference immediately. For applications people use for hours, like dashboards or internal tools, a dark theme is often a real comfort gain.
The effort depends almost entirely on how colours are organised in the code. If hex values are scattered across hundreds of components, dark mode becomes a major project. With a central colour system, it is manageable. That is one reason a design system pays off early.
The technical foundation: colour tokens as CSS variables
Name colours by role
Name colours by what they do, not by how they look. Instead of --grey-100, use --color-bg, --color-surface, --color-text, --color-text-muted, --color-border and --color-accent. Components only use these tokens. For dark mode you then change the token values in one place.
:root {
--color-bg: #ffffff;
--color-text: #1a1a1a;
color-scheme: light dark;
}
@media (prefers-color-scheme: dark) {
:root:not([data-theme="light"]) {
--color-bg: #121417;
--color-text: #e6e6e6;
}
}
:root[data-theme="dark"] {
--color-bg: #121417;
--color-text: #e6e6e6;
}
The color-scheme property makes native elements such as scrollbars, form fields and date pickers render dark as well. Modern browsers also support the CSS light-dark() function, which lets you write both values side by side.
System preference and a manual toggle
A three-state toggle works well: light, dark and system. Store the choice in localStorage and set it as an attribute on the html element. Without a stored choice, the system setting applies. If JavaScript needs to react, for example to redraw charts, listen for changes with matchMedia('(prefers-color-scheme: dark)').
Prevent the flash on load
The most common bug is a brief white flash before the dark theme kicks in. It happens when the theme is only applied after the JavaScript bundle loads. The fix is a small inline script in the head that reads the stored choice and sets the attribute before the browser paints. In server-rendered React or Next.js apps you can also store the choice in a cookie and send it straight in the HTML.
Design: a palette of its own
Avoid pure black
Pure black with pure white creates harsh contrast that can shimmer when reading. Dark greys for backgrounds and slightly muted white for text are the norm. Highly saturated accent colours look loud on dark backgrounds and usually need lighter, less saturated variants.
Show depth with lightness, not shadows
Shadows barely show on dark backgrounds. Layers such as cards, dialogs and menus are better distinguished by slightly lighter surfaces: the higher an element sits, the lighter its surface.
Check contrast
The WCAG contrast requirements apply to both modes: at least 4.5:1 for body text and 3:1 for large text and UI controls. Check every token pair in both themes, not just the home page. Our article on web accessibility covers this in more detail.
Common pitfalls
Logos and graphics with transparent backgrounds and dark lettering disappear on dark surfaces. Separate variants via the picture element with media="(prefers-color-scheme: dark)", or SVGs that use currentColor, solve this. Photos can be dimmed slightly with a brightness filter.
Charts need their own colour series, because the light theme's colours read differently on a dark background. Our piece on the psychology of colour in digital products explains how strongly colour shapes perception. Also remember email templates, PDFs and the theme-color meta tag for the browser bar on mobile.
Frequently asked questions (FAQ)
Is dark mode worth it for every website?
For applications used for long stretches and products with a technical audience, almost always. For a simple company website it is more a question of polish. With a clean colour system the extra effort is small.
Can dark mode be added later?
Yes. The first step is replacing hard-coded colours with tokens. That is tedious, but it also makes the light theme easier to maintain. After that, the dark theme is mostly design work.
Does dark mode save battery?
On OLED displays, dark pixels emit less light and use less power. On LCD screens the effect is practically zero. Battery life is therefore only a real argument for mobile use.
Should dark mode be the default?
The best default is to follow the user's system setting. Everyone gets what they already prefer and can change it with the toggle.
Conclusion
Good dark mode is half architecture and half design. If you set up colours as semantic tokens from the start, the dark theme comes almost for free, and adding further themes or brand variants later becomes easy too.
If you are planning a web app with well-designed theming, talk to us about web development.
Related posts
- Design Systems: Why Every Development Team Needs One
What a design system includes, when it pays off and how tokens, components and documentation help design and development ship faster and more consistently.
- Mobile UX Patterns That Convert: What Good Apps Do Differently
Which mobile UX patterns lead to more sign-ups and purchases in apps: thumb zone, onboarding, forms, permission requests and loading states in detail.
- From Wireframe to Pixel-Perfect: The UI Design Process
How an idea becomes a buildable UI design: requirements, wireframes, prototype, visual design, handoff to development and a shared review in the browser.
- Micro-Interactions in UX Design: Small Details, Big Impact
What micro-interactions are, where they genuinely help users, and how to build them so they stay fast, accessible and consistent across web and apps.