Dark Mode Colors: Best Practices for Light-to-Dark
Embed This Widget
Add the script tag and a data attribute to embed this widget.
Embed via iframe for maximum compatibility.
<iframe src="https://colorfyi.com/iframe/entity//" width="420" height="400" frameborder="0" style="border:0;border-radius:10px;max-width:100%" loading="lazy"></iframe>
Paste this URL in WordPress, Medium, or any oEmbed-compatible platform.
https://colorfyi.com/entity//
Add a dynamic SVG badge to your README or docs.
[](https://colorfyi.com/entity//)
Use the native HTML custom element.
Dark mode is no longer optional. With operating systems, browsers, and applications all offering dark mode as a default choice, designers and developers face a genuine challenge: how do you create a color system that works beautifully in both light and dark contexts without maintaining two entirely separate visual languages? The answer lies in understanding how dark mode color works at the perceptual level â and in building your palette with darkness in mind from the beginning.
Why Dark Mode Matters
The case for dark mode has multiple dimensions:
User preference and comfort: A significant portion of users prefer dark interfaces, especially in low-light environments. The high contrast of white backgrounds against dark surroundings causes eye strain over time, while dark backgrounds reduce the luminance differential between screen and environment.
OLED power efficiency: On OLED and AMOLED screens â found in most modern smartphones and an increasing number of laptops â true black pixels are turned off entirely. A dark mode interface that uses near-black backgrounds like #0F172A can reduce screen power consumption by 30â60% compared to a white background.
Accessibility: Some users with photosensitivity, migraine disorders, or certain visual impairments find dark mode significantly more comfortable to use for extended periods.
Completeness: A product that only ships in light mode in 2026 signals that dark mode was an afterthought â which undermines trust in the product's overall quality.
The Central Mistake: Inverting Colors
The most common dark mode mistake is inverting the light palette. If your light mode surface is #FFFFFF and your text is #111827, simply swapping them to a black background with white text does not produce a good dark mode. Here is why:
Inversion Creates Harsh Contrast
Pure white on pure black â #FFFFFF on #000000 â has a contrast ratio of 21:1, far exceeding WCAG requirements. But this maximum contrast is fatiguing for reading-heavy interfaces. The eye is constantly adjusting to the extreme luminance difference. Most well-designed dark modes use a dark gray surface (typically in the 5â15% lightness range) rather than pure black, and a light gray text (90â95% lightness) rather than pure white.
GitHub's dark mode uses #0D1117 as the main background (not pure black) and #E6EDF3 for primary text (not pure white). The contrast ratio between them is approximately 15:1 â comfortable for extended reading without the harshness of maximum contrast.
Brand Colors Become Illegible
A primary blue of #3B82F6 that looks great on a white background is far too dark to use as link text on a dark background. Its contrast against #1E293B (a common dark surface) may fall below the 4.5:1 WCAG AA threshold. In dark mode, you need a lighter shade of your brand color â not the same shade.
Elevation Gets Inverted
In light mode, elevation is typically conveyed through shadows: elements higher in the z-stack cast darker shadows. In dark mode, this principle reverses: higher elevation is conveyed through lighter surfaces, not shadows. Inverting a light mode palette does not produce this hierarchy â you end up with the same surface color regardless of elevation.
The Right Approach: Reduce Lightness, Do Not Invert
The correct mental model for dark mode is that you are moving every surface color down the lightness scale â reducing the luminance of backgrounds and midtones â while making text colors lighter, not inverted.
Surface Colors
In light mode, your surface hierarchy might be:
| Surface Level | Light Mode Color | Lightness |
|---|---|---|
| Page background | #F8FAFC | 98% |
| Card background | #FFFFFF | 100% |
| Elevated card | #FFFFFF + shadow | 100% |
| Popover / modal | #FFFFFF + heavy shadow | 100% |
In dark mode, the same hierarchy uses lightness increases to convey elevation:
| Surface Level | Dark Mode Color | Lightness |
|---|---|---|
| Page background | #0F172A | 8% |
| Card background | #1E293B | 14% |
| Elevated card | #334155 | 25% |
| Popover / modal | #475569 | 35% |
Notice that page background is the darkest surface, and popover is lighter â the opposite of light mode where shadows signal elevation. This is the Material Design 3 approach, and it scales to any number of elevation levels simply by incrementing the lightness value.
The Shade Generator helps you build the complete lightness scale for any hue, letting you pick the right dark and mid-dark values for your surface hierarchy.
Text Readability in Dark Mode
Text is the most critical element to get right in dark mode because it appears on every screen and every view.
Primary Text
Primary text in dark mode should be light but not pure white. A value like #F1F5F9 or #E2E8F0 provides excellent contrast against a dark background without the harshness of #FFFFFF. On a page background of #0F172A, #F1F5F9 achieves a contrast ratio of approximately 16:1, well above both AA and AAA thresholds.
Secondary Text
Secondary text â captions, labels, metadata â should be visibly lighter than the background but distinguishably dimmer than primary text. A value like #94A3B8 on #0F172A achieves approximately 7:1 contrast, meeting WCAG AAA for normal text while clearly signaling secondary information.
Disabled Text
Disabled text in dark mode typically sits around 40â50% opacity of primary text, or uses a fixed color like #475569. Verify that disabled text still meets at least 3:1 contrast against its background for WCAG compliance even in a "disabled" state.
Checking Contrast
Use the Contrast Checker to verify every text-background combination in both modes. Dark mode interfaces are more likely to have contrast violations than light mode interfaces, because the natural tendency is to pick colors that "look right" on the dark surface â and colors that look vivid and readable visually may still fail the mathematical contrast test.
Adapting Brand Colors for Dark Mode
Your brand's primary color almost certainly needs a different shade in dark mode. The principle is simple: in dark mode, use a lighter shade of the brand color so it maintains sufficient contrast against dark backgrounds.
For example, if your brand primary is #2563EB (blue-600 in Tailwind terms):
- Light mode: Use #2563EB for buttons, links, and interactive elements against light backgrounds â contrast against white is approximately 5.9:1.
- Dark mode: Shift to #60A5FA (blue-400) for text links and outlined elements â contrast against #1E293B is approximately 7.2:1.
This is the consistent pattern: in dark mode, interactive text colors move from the 600â700 range to the 300â400 range. Background-colored interactive elements (filled buttons) can often keep the 500â600 range since they carry their own background.
The Exception: Filled Buttons
A filled primary button does not need its color lightened in dark mode. The button's background provides its own surface color, and the interaction is between the button background and the button text (typically white or a very light neutral). What changes is the button's hover and focus states â the hover color should be slightly lighter in dark mode, not darker.
/* Light mode: darken on hover */
.btn-primary { background: #2563EB; }
.btn-primary:hover { background: #1D4ED8; }
/* Dark mode: lighten on hover */
@media (prefers-color-scheme: dark) {
.btn-primary:hover { background: #3B82F6; }
}
Status Colors and Semantic Colors
Semantic colors â red for errors, green for success, yellow for warnings, blue for information â require the most careful handling in dark mode because they need to communicate meaning across a wide contrast range.
Error Red
Light mode error: #DC2626 on #FEF2F2 background. Dark mode error: #FCA5A5 on #450A0A background, or #F87171 text against a dark neutral surface.
Success Green
Light mode success: #16A34A on #F0FDF4 background. Dark mode success: #86EFAC text or #14532D background with light text.
Warning Yellow
Yellow is especially tricky in dark mode because saturated yellows on dark backgrounds can feel fluorescent and aggressive. Desaturating to amber â moving from #FACC15 toward #D97706 or using a muted background like #451A03 â tends to produce more comfortable results.
Always check semantic color combinations in the Contrast Checker in both light and dark configurations.
Implementing Dark Mode in CSS
CSS Custom Properties (Recommended)
The cleanest implementation uses CSS custom properties with a prefers-color-scheme media query:
:root {
--color-background: #F8FAFC;
--color-surface: #FFFFFF;
--color-surface-elevated: #FFFFFF;
--color-text-primary: #0F172A;
--color-text-secondary: #64748B;
--color-brand: #2563EB;
--color-brand-text: #2563EB;
}
@media (prefers-color-scheme: dark) {
:root {
--color-background: #0F172A;
--color-surface: #1E293B;
--color-surface-elevated: #334155;
--color-text-primary: #F1F5F9;
--color-text-secondary: #94A3B8;
--color-brand: #2563EB; /* Button bg stays the same */
--color-brand-text: #60A5FA; /* Text links use lighter shade */
}
}
This approach defines the semantic meaning once and switches the underlying values at the theme level. Components use var(--color-brand-text) and automatically adapt to both modes.
Tailwind CSS Dark Mode
Tailwind uses the dark: variant for dark mode utilities:
<div class="bg-slate-50 dark:bg-slate-900">
<h1 class="text-slate-900 dark:text-slate-100">Heading</h1>
<p class="text-slate-600 dark:text-slate-400">Body text</p>
<a class="text-blue-600 dark:text-blue-400">Link</a>
</div>
Configure the dark mode strategy in tailwind.config.js (v3) or your CSS file (v4):
// v3: class-based dark mode (manual toggle)
module.exports = {
darkMode: 'class',
// ...
}
/* v4: media-query based (system preference) */
@import "tailwindcss";
/* Dark mode uses prefers-color-scheme by default in v4 */
Testing Both Modes Simultaneously
Testing dark mode is often the most neglected step. Here is a systematic approach:
1. Browser DevTools simulation: Chrome and Firefox both let you simulate prefers-color-scheme: dark in the DevTools without changing your system preference. In Chrome: DevTools â Rendering â Emulate CSS media feature prefers-color-scheme.
2. Side-by-side comparison: Use a split-screen setup with one browser in light mode and one in dark mode to compare the visual weight and hierarchy simultaneously.
3. Systematic contrast verification: Run the Contrast Checker for every text-background combination in dark mode, not just light mode. Common failures in dark mode include: secondary text on elevated surfaces, placeholder text in form inputs, and disabled button text.
4. Real device testing: Desktop monitors and laptop screens handle dark backgrounds very differently. OLED phone screens make dark backgrounds appear truly black, while LCD monitors show them as dark gray. Test on at least one OLED device.
5. Color blindness in dark mode: Dark mode affects color blindness accessibility differently than light mode. Reds and greens that are distinguishable in light mode may become harder to differentiate in dark mode because reduced luminance narrows the perceived difference between hues. The Color Blindness Simulator helps identify these issues.
Dark Mode Anti-Patterns to Avoid
Absolute black backgrounds: #000000 creates harshness and makes images appear to float. Use #0A0A0A to #121212 at minimum.
Pure white text: #FFFFFF text on dark backgrounds has been shown to cause halation (a bloom effect) for users with astigmatism, making letters appear to bleed into the background. Use #F0F0F0 or lighter grays instead.
Unmodified saturated colors: Dropping a saturated brand color like #EF4444 directly onto a dark background without adjustment creates visual vibration and can be uncomfortable for extended viewing.
Forgetting non-text elements: Borders, dividers, icons, and decorative elements all need dark mode variants. A subtle #E2E8F0 border in light mode should become approximately #334155 in dark mode â significantly lighter than the page background but darker than cards.
Shadows in dark mode: Drop shadows are nearly invisible on dark backgrounds. Instead of relying on shadows for elevation, use slightly lighter background colors and subtle borders (border-color: rgba(255,255,255,0.08)).
Key Takeaways
- Dark mode should reduce lightness across surface and background colors â not invert them. Inversion produces harsh maximum-contrast combinations and reverses the elevation hierarchy.
- Use near-black (8â12% lightness) rather than pure black for page backgrounds; near-white (90â96% lightness) rather than pure white for primary text. This keeps contrast comfortable without being fatiguing.
- Elevation in dark mode is conveyed through lighter surfaces, not shadows. Assign progressively lighter shades of your neutral color to progressively higher z-levels.
- Brand colors need lighter shades in dark mode for text usage (e.g., shift from blue-600 to blue-400), but filled buttons can often keep the same shade.
- Semantic colors (error red, success green, warning yellow) need darker backgrounds and lighter text values in dark mode.
- Use the Contrast Checker to verify every text-background combination in both light and dark modes, and the Shade Generator to build the complete lightness scale for your neutral and brand colors.