Guides / Prompting and shipping
How to Add Dark Mode to Your Website or App (Without the Flash)
How to add dark mode: follow the system setting or add a toggle, use colour tokens, avoid the white flash on load, and check contrast. Prompts included.
Mythex Team · · 5 min read
To add dark mode, define your colours as named tokens (background, text, surface, border, accent) instead of hard-coded values, give each token a light and a dark value, and switch between them with the CSS prefers-color-scheme media feature so the site follows the visitor's system setting. If you want a manual toggle too, store the choice in the browser and apply it with a tiny inline script in the page head so there's no white flash on load. Then check contrast, images and charts in both themes. In an AI app builder, one clear prompt can do most of this; the checking is where the quality comes from.
Two approaches
| Approach | How it works | Good for | Trade-offs |
|---|---|---|---|
| Follow the system | CSS media feature prefers-color-scheme picks light or dark | Simple sites, zero UI | Visitors can't choose differently from their device |
| Toggle + system default | Starts from the system setting; a Light / Dark / System switch overrides it and is remembered | Apps people use a lot | A little JavaScript, and the flash-on-load problem to solve |
Most apps do best with the second: respect the system by default, let people override it.
Start with colour tokens
Dark mode is painful when colours are scattered through the code as white, #333 and gray-100. It's easy when every colour comes from a small set of named tokens:
| Token | Light | Dark |
|---|---|---|
background | near-white | very dark grey |
surface (cards, menus) | white | slightly lighter dark grey |
text | near-black | off-white |
text-muted | mid grey | lighter mid grey |
border | light grey | dark grey with a little contrast |
accent | your brand colour | often a lighter or less saturated version |
Each component uses the token names. Switching themes then means swapping one set of values, not editing every component.
Tips for the dark palette:
- Avoid pure black and pure white for large areas; very dark grey and off-white are easier to read.
- Show depth with lighter surfaces, not shadows — shadows barely show on dark backgrounds.
- Desaturate bright brand colours slightly; they can glow uncomfortably on dark backgrounds.
- Keep contrast readable. WCAG's AA level asks for at least 4.5:1 contrast for normal text. Muted grey text often fails this in dark themes.
With Tailwind CSS
Tailwind is the default styling tool in many AI-built apps. According to Tailwind's dark mode docs, the dark: variant uses prefers-color-scheme by default, so classes like bg-white dark:bg-gray-900 follow the system setting with no extra work.
For a manual toggle in Tailwind v4, the docs show switching the variant to a class on a parent element:
@import "tailwindcss";
@custom-variant dark (&:where(.dark, .dark *));
Then the app adds or removes the dark class on the root element. The docs also suggest an inline script in the head that applies the saved choice (or the system setting) before the page paints — which is exactly how you avoid the flash.
Avoiding the white flash
The flash happens because the page paints in light mode, then JavaScript loads, reads the saved choice, and switches to dark. The fix:
- Put a tiny inline script in the page head that reads the saved theme and sets the class on the root element before anything renders.
- Set the
color-schemeCSS property for each theme so browser-drawn parts (scrollbars, form controls) match. - Give the page an explicit background colour in both themes.
- If the app is server-rendered, make sure the server and the browser agree on the initial theme, or the page may re-render and flicker.
Example prompts
A full setup:
Add dark mode. First replace hard-coded colours with theme tokens (background, surface, text, muted text, border, accent) with light and dark values. Default to the visitor's system setting. Add a theme switch in the header with Light, Dark and System options, remember the choice in localStorage, and apply it with an inline script in the head so there's no white flash on load. Set color-scheme for each theme. Keep the current light design exactly as it is.
Fixing what's left:
Check every page in dark mode and fix: text with low contrast (aim for at least 4.5:1), hard-coded white backgrounds, borders that disappear, charts with dark text on dark backgrounds, and logos that vanish. Don't change the light theme.
Images and charts:
Use a light version of the logo in dark mode. Make chart axes, gridlines and labels use theme tokens. Give screenshots a subtle border in dark mode so they don't blend into the background.
Step by step
- Audit colours. Find hard-coded colours and replace them with tokens. This is most of the work.
- Define dark values for each token.
- Follow the system with
prefers-color-scheme(or Tailwind's defaultdark:). - Add a toggle if you want one: Light, Dark, System. Store the choice in the browser; for logged-in apps you can also save it to the user's profile.
- Prevent the flash with the inline head script and
color-scheme. - Check the tricky parts: logos, icons, images with white backgrounds, charts, code blocks, maps, embeds, emails.
- Test both themes on desktop and phone, with the system set to light and to dark, and after a hard refresh.
Common mistakes
- Inverting everything. A filter that inverts colours ruins photos and brand colours.
- Hard-coded colours left behind. One white card in a dark page stands out immediately.
- Low-contrast grey text. Looks stylish, fails readability.
- White flash on load. Especially noticeable on phones at night.
- Forgetting third-party embeds. Maps, videos and widgets may stay light; check whether they have a dark option.
- Toggle with only two states. Once someone clicks it, they can't go back to following the system.
- Breaking the light theme. Say explicitly in your prompt that the light design should stay the same.
Checklist
- All colours come from theme tokens
- Follows the system setting by default
- Optional toggle with Light, Dark and System, remembered
- No white flash on load or refresh
-
color-schemeset so form controls and scrollbars match - Text contrast at least 4.5:1 in both themes
- Logos, images, charts and embeds checked in dark mode
- Light theme unchanged
- Tested on desktop and phone
Dark mode with Mythex
Apps built on Mythex use Vite, React, TypeScript and Tailwind by default, so the Tailwind approach above applies directly: paste the full-setup prompt into chat, then check Preview in both themes and at phone size. Mythex's design prompting advice is to name what to change and what to keep, which matters here — say the light theme must stay as it is. See Design & UI prompts, and roll back with a checkpoint if a pass goes wrong.
For more on getting the look right, see how to design a good-looking app with AI and how to write prompts for AI app builders.
Questions
How do I make my website follow the device's dark mode setting?
Use the CSS prefers-color-scheme media feature, which tells you whether the visitor's system is set to light or dark. Tailwind's dark: variant uses it by default.
Why does my site flash white before switching to dark mode?
The saved theme is applied by JavaScript after the page has already painted. Fix it with a tiny inline script in the page head that sets the theme class before anything renders, and set an explicit background colour for both themes.
Should dark mode be pure black?
Usually not. Very dark greys are easier on the eyes for long reading and let you show depth with slightly lighter surfaces. Pure black can suit OLED-focused or media-heavy designs.
Do I need a dark mode toggle if I follow the system setting?
Not strictly. Following the system is a good default, but a toggle with Light, Dark and System options helps people who want your site to differ from their device setting.