The biggest headache when implementing dark mode is the "white flash"—that split second where the screen blasts bright white before finally switching to dark as the page loads. It’s jarring; it feels like someone just flashed a flashlight in your eyes while you're in a dark room.
Getting dark mode right isn't just about flipping the background to black. If you take the lazy route and use CSS filters to invert colors, you'll end up ruining the look of your images, videos, and third-party elements. A much better approach is using design tokens to swap out color variables semantically.
You have to handle three specific scenarios cleanly: respecting the OS preference, honoring the user's manual choice, and actually saving that choice.
The logic flow needs to be strict. First, check if there's a manual theme stored
in localStorage. If not, check the system preference via
prefers-color-scheme. Only if both are missing do you fall back to the default
theme. Don't add extra, arbitrary rules just because a design team wants
something without a solid technical reason.
Timing is everything. If you wait until your React components mount to apply the
theme, the user is definitely going to see that annoying color flicker. The fix
is to put a tiny script in the HTML <head> that runs before the page even
paints. This script needs to read from localStorage and immediately apply the
theme attribute to the <html> element so the colors are already set by the
time the content appears.
Beyond the colors, don't ignore accessibility. Make sure your text contrast stays readable in dark mode and that your focus rings don't disappear. Also, if a user has "reduced motion" enabled on their device, don't force them to sit through loud, flashy color transitions.
Getting these technical details right from the start prevents visual bugs down the road. At the end of the day, just give the user full control and make sure the app respects their settings without being intrusive.