This generator builds complete dark-mode palettes with each color assigned a UI role — background, surfaces, accent, and text — and verifies the text-on- background contrast against WCAG so your dark theme is actually readable.
How it works
A single base hue is chosen, then five role colors are derived in HSL:
- Background: the base hue at very low lightness (about 8–12%) with low saturation — a near-black that is not pure black.
- Surface 1 and 2: the same hue a few lightness steps higher, giving two elevation layers for cards and modals.
- Accent: a vivid color at higher saturation and mid lightness for buttons and links.
- Text: a near-white at very high lightness.
The text and background are then run through the WCAG contrast formula:
ratio = (Llighter + 0.05) / (Ldarker + 0.05)
where L is relative luminance. AA requires 4.5, AAA requires 7.
Why dark UI design is harder than it looks
Pure black causes halation. A pure black #000000 background with pure white #ffffff text has a contrast ratio of 21:1, which is technically the maximum. But in practice, reading bright white text on pure black causes visual fatigue and a “halation” effect where bright text appears to bleed into the dark background. Most successful dark UIs use a very dark gray (lightness 7–12%) rather than pure black, and an off-white (lightness 88–95%) rather than pure white.
Layering creates depth without color. In a light theme, shadows create elevation. In dark themes, shadows against a dark background are nearly invisible, so elevation is communicated by making raised surfaces lighter. A component sitting on the page background should be 2–4% lighter; a modal above that should be another 2–4% lighter. This is why the generator produces two surface colors rather than one.
Accent colors need adjustment for dark backgrounds. A vivid blue that passes WCAG contrast on a white background often does not pass on a dark background, because the relative luminance calculation changes significantly. Always verify the accent color on the dark background specifically, not just on white.
Applying the palette in CSS
Once you have the five hex values, map them to CSS custom properties for easy theming:
:root[data-theme="dark"] {
--color-background: #0e1117; /* background */
--color-surface-1: #161b22; /* cards, panels */
--color-surface-2: #1c2128; /* modals, dropdowns */
--color-accent: #58a6ff; /* buttons, links */
--color-text: #e6edf3; /* body text */
}
Using tokens like this means switching between light and dark modes only requires toggling the data-theme attribute on the root element, while all component styles reference the same token names.
Tips and notes
Use the two surface colors for elevation rather than relying only on shadows. The accent should be reserved for interactive elements so it stands out against the muted background. If the reported ratio dips below 4.5, regenerate — the text lightness will land higher and pass. Click any swatch to copy its hex.