Skip to content

How to Implement Dark Mode in UI Design the Right Way

Dark Mode Done Right: A Practical UI Guide

Dark mode isn't a color swap. Here's how to implement it in UI design with tokens, WCAG contrast, and light-based elevation that actually holds up.

· · 5 min read

Updated: July 16, 2026

Sleek dark-themed app interface displayed on a device screen

Dark mode is a second set of color tokens for the same interface, tuned so text stays legible and depth still reads on a dark surface. It is not the light theme with the colors flipped. Invert your palette blindly and you get grey text nobody can read, brand colors that vibrate, and shadows that vanish. I've shipped dark themes that took a day and dark themes that took three weeks, and the difference was always whether the color system was built for it from the start.

So where do most teams go wrong? They treat dark mode as a filter you apply at the end. It isn't. It's a parallel palette, and the interface has to be built on tokens for it to work at all.

TL;DR: Dark mode isn't the light theme inverted. It's a second set of color tokens tuned for a dark surface. Build components on semantic tokens, recheck every text pair against WCAG 4.5:1, and show depth by making higher surfaces lighter grey instead of casting shadows the way you would in light mode.

Why Is Dark Mode More Than Inverting Colors?

Flip #ffffff to #000000 and #111111 to #eeeeee, and on paper you have dark mode. Load it and the problems start. Pure white body text on pure black causes halation, that smearing glow that makes text feel like it's buzzing. Your carefully tuned brand blue now looks radioactive. The subtle grey shadow that separated a card from the background is gone, because you can't cast a darker shadow on an already-dark surface.

Dark mode changes the physics of the screen. In light mode, depth comes from shadows getting darker. In dark mode, depth comes from surfaces getting lighter. That single inversion rewrites how you show hierarchy, and no automatic filter understands it.

Why Should You Start With Tokens, Not Colors?

Here's the rule that saves you: components should reference semantic tokens like color/surface and color/text-primary, never raw hex values. Each token holds two values, one per theme. Switch the theme and every component updates, because none of them know the actual color, only the role.

If your product doesn't have this yet, that's the real first task. Our step-by-step design system guide walks through building the token layer, and the design tokens Figma to code guide shows how those tokens survive the handoff to engineering. Retrofitting dark mode onto hard-coded colors means hunting through sixty components by hand. Tokens turn it into one variable swap.

How Do You Get the Contrast Right?

This is where dark themes quietly fail. WCAG 2.2 still asks for 4.5:1 on normal text and 3:1 on large text, and dark surfaces make it easy to miss. That soft grey caption that passed on white can drop well below 4.5:1 on a #121212 surface. Every text-on-surface pair needs rechecking, not assuming.

Hand holding a phone with colorful icons on a dark home screen
Photo by Georgiy Lyamin on Unsplash

Test each combination before you commit. Our free WCAG contrast checker gives you the live ratio with pass or fail badges as you tune the colors, which beats eyeballing it. Aim for AA on body text, and treat AAA as the target for anything read in long sessions. Contrast is also a hierarchy tool, not just a compliance box, the same way it works in the visual hierarchy principles guide.

How Do You Show Elevation With Light Instead of Shadow?

In light mode a modal floats above the page on a soft shadow. In dark mode that shadow disappears into the background. So you flip the model: the higher a surface sits, the lighter its grey. Your base might be #121212, a raised card #1e1e1e, a modal #242424, a menu #2c2c2c. Each step up in the stack gets a touch brighter, and the eye reads that as closer.

Dark-themed editing dashboard interface on a monitor
Photo by dlxmedia.hu on Unsplash
Surface levelExample greyReads as
Base background#121212Furthest back
Raised card#1e1e1eOne step up
Modal#242424Floating above
Menu#2c2c2cClosest to you

This is exactly the pattern that keeps dashboard interfaces legible in the dark, where dozens of panels compete for attention and shadow alone can't separate them. Layer with light. Reserve shadow for a subtle assist, not the whole job.

What Common Mistakes Should You Skip?

A few traps I keep seeing, and how to avoid them:

  • Pure black backgrounds. Start at #121212, not #000000. Black is harsh and gives you nowhere to go for elevation.
  • Reusing light-mode accent colors. Desaturate and lighten brand colors for the dark theme, or they'll vibrate.
  • The white flash. Set the background color before the CSS loads and honor prefers-color-scheme at the root, so a user in dark mode never gets blinded by a white flash on load.
  • Forgetting images and illustrations. A white-background PNG looks like a hole punched in your dark UI. Use transparent assets or swap them per theme.

Dark mode done well feels effortless, and that's the trap: it looks simple, so teams underestimate it. Build it on tokens, check every contrast pair, and let light carry your elevation. Do that, and the second theme stops being a retrofit and starts being what it should have been all along, one interface that reads well whatever the lights are doing.

Frequently Asked Questions

Should dark mode use pure black backgrounds?
No, and this is the mistake I see most often. Pure black (#000000) against pure white text hits a 21:1 contrast ratio that reads as harsh and causes halation, where light text smears against the black for people with astigmatism. Material Design recommends a dark grey around #121212 as the base surface instead. It softens the contrast, gives you room to show elevation with lighter greys, and looks far more premium than a black void. Save true black for OLED power-saving modes if you must, but even then, lift your surfaces off it.
How do I handle brand colors in dark mode?
Desaturate and lighten them. A brand blue that pops on a white background often turns muddy or vibrates painfully on a dark surface. The fix is to build a second token value for dark mode: take the saturated brand color, drop its saturation, and raise its lightness until it reads clearly at 4.5:1 against your dark surface. Do not reuse the exact light-mode hex. Material and Apple both ship separate accent values per theme for exactly this reason, and your design tokens should too.
Do I need to support both light and dark mode?
If you can only ship one theme well, ship light mode well. Dark mode is an expectation now, not a differentiator, and a broken dark theme that leaks white flashes or fails contrast does more harm than no dark theme at all. That said, respecting the user's prefers-color-scheme setting with a solid dark palette is worth the effort for any product people use at night or for long sessions. Build it on tokens from day one and the second theme costs you far less than retrofitting it later.