The eye perceives the same color differently on light and dark backgrounds — this is not a metaphor, but physiology of vision. A saturated color that on a white background is perceived as calm and statusful begins to vibrate and cause visual fatigue on a black background due to the effect of simultaneous contrast. Therefore, mechanical inversion (white→black, dark blue→light blue via a simple HSL shift) breaks the very idea of the brand: the color should evoke the same emotion in both themes, not the same code.
The correct logic is transformation through perception, not through pixel math. For example, Apple creates at least two distinct values for each brand color — for light and dark themes — and sometimes shifts not only brightness but also the hue itself. Their blue in light mode differs in value from the dark mode, but it is critical for readability and the feeling of "the same" color.
| Approach | What they do | Result |
|---|---|---|
| Color inversion | Change light to dark according to the formula 255-x | Acidic, "burning" shades, loss of brand identity |
| Simple darkening/lightening | Move only lightness, leaving hue and saturation unchanged | Color fades or "gets dirty", loses its prestige |
| Reassembly through perception (Apple approach) | Change lightness, saturation and slightly hue separately for each theme | The color feels "the same", but is comfortable for the eyes in both environments |
The main rule is: on a dark background, bright brand colors need to be made less saturated, not simply darker. A saturated accent color that works well on white, when transferred to a dark background, requires a saturation reduction of 10–20%—otherwise it will "shout" and create visual noise. At the same time, lightness (lightness) is often needed to be slightly raised so that the color does not sink on a dark background and remains readable.
The brand palette transformation algorithm looks like this:
The logo and key brand accents should be adapted separately from the interface's functional palette: if the logo is built on a saturated color, for a dark background often an alternative version with reduced saturation and increased lightness is needed, otherwise it will visually "jump" on the screen and distract from the content.
In the dark theme, the principle works as "surfaces are lifted by light, not shadow" — the higher the element is in the visual hierarchy (a card above the background, a modal window above a card), the lighter its surface is, not darker, as in the light theme. This is an opposite logic, and it is precisely on this that designers trip, accustomed to thinking in the paradigm of shadows.
A separate system is needed for text transparency — it sets visual priority without introducing new colors:
And one more practical rule about the background: do not use pure black #000000 and pure white #FFFFFF. The combination of background #121212–#1A1A1A with text #E0E0E0–#F0F0F0 gives a contrast of about 15:1 — that is more than enough for readability, but without the "halo" and burn-in on OLED screens, which provide pure black.
The eye cannot reliably assess contrast — brightness perception is nonlinear and depends on surrounding colors, monitor lighting, and even the viewer's fatigue. Green, which reads well on a white background, may not meet the WCAG norm on a black background — 4,5:1 for regular text and 3:1 for large text and interactive elements. Therefore, each "text-background" pair must be audited separately for each theme, and not transfer the results of the light theme check to the dark theme by analogy.
| Tool | For what |
|---|---|
| WebAIM Contrast Checker | Quick check of a color pair for compliance with WCAG AA/AAA |
| Figma plugin Contrast / Stark | Checking contrast directly in the layout, at the design stage |
| Figma Variables + modes | Storing semantic tokens and switching between themes without editing components |
| Chrome DevTools (Accessibility panel) | Checking actual contrast on a live website, taking rendering into account |
A good practical rule for quick self-checking without tools: there should be at least 4 steps between the text tone and the background tone on the palette scale — for example, if the background is taken as slate-100, the text should not be lighter than slate-700. If the difference is less — there is insufficient contrast, even if it "looks normal" to the eye.
Most dark theme failures boil down to a few repeated missteps:
The solution to all five problems is one — to build the palette not as a set of fixed hex codes, but as a system of tokens with meaningful names (--color-surface, --color-text-primary), where under the hood each theme has its own, separately calibrated color value. This is more expensive at the design stage, but it is precisely this approach that gives the brand the ability to be equally recognizable and equally readable regardless of which theme the product is opened in.
The eye perceives the same color differently on light and dark backgrounds — this is not a metaphor, but physiology of vision. A saturated color that on a white background is perceived as calm and statusful begins to vibrate and cause visual fatigue on a black background due to the effect of simultaneous contrast. Therefore, mechanical inversion (white→black, dark blue→light blue via a simple HSL shift) breaks the very idea of the brand: the color should evoke the same emotion in both themes, not the same code.
The correct logic is transformation through perception, not through pixel math. For example, Apple creates at least two distinct values for each brand color — for light and dark themes — and sometimes shifts not only brightness but also the hue itself. Their blue in light mode differs in value from the dark mode, but it is critical for readability and the feeling of "the same" color.
| Approach | What they do | Result |
|---|---|---|
| Color inversion | Change light to dark according to the formula 255-x | Acidic, "burning" shades, loss of brand identity |
| Simple darkening/lightening | Move only lightness, leaving hue and saturation unchanged | Color fades or "gets dirty", loses its prestige |
| Reassembly through perception (Apple approach) | Change lightness, saturation and slightly hue separately for each theme | The color feels "the same", but is comfortable for the eyes in both environments |
The main rule is: on a dark background, bright brand colors need to be made less saturated, not simply darker. A saturated accent color that works well on white, when transferred to a dark background, requires a saturation reduction of 10–20%—otherwise it will "shout" and create visual noise. At the same time, lightness (lightness) is often needed to be slightly raised so that the color does not sink on a dark background and remains readable.
The brand palette transformation algorithm looks like this:
The logo and key brand accents should be adapted separately from the interface's functional palette: if the logo is built on a saturated color, for a dark background often an alternative version with reduced saturation and increased lightness is needed, otherwise it will visually "jump" on the screen and distract from the content.
In the dark theme, the principle works as "surfaces are lifted by light, not shadow" — the higher the element is in the visual hierarchy (a card above the background, a modal window above a card), the lighter its surface is, not darker, as in the light theme. This is an opposite logic, and it is precisely on this that designers trip, accustomed to thinking in the paradigm of shadows.
A separate system is needed for text transparency — it sets visual priority without introducing new colors:
And one more practical rule about the background: do not use pure black #000000 and pure white #FFFFFF. The combination of background #121212–#1A1A1A with text #E0E0E0–#F0F0F0 gives a contrast of about 15:1 — that is more than enough for readability, but without the "halo" and burn-in on OLED screens, which provide pure black.
The eye cannot reliably assess contrast — brightness perception is nonlinear and depends on surrounding colors, monitor lighting, and even the viewer's fatigue. Green, which reads well on a white background, may not meet the WCAG norm on a black background — 4,5:1 for regular text and 3:1 for large text and interactive elements. Therefore, each "text-background" pair must be audited separately for each theme, and not transfer the results of the light theme check to the dark theme by analogy.
| Tool | For what |
|---|---|
| WebAIM Contrast Checker | Quick check of a color pair for compliance with WCAG AA/AAA |
| Figma plugin Contrast / Stark | Checking contrast directly in the layout, at the design stage |
| Figma Variables + modes | Storing semantic tokens and switching between themes without editing components |
| Chrome DevTools (Accessibility panel) | Checking actual contrast on a live website, taking rendering into account |
A good practical rule for quick self-checking without tools: there should be at least 4 steps between the text tone and the background tone on the palette scale — for example, if the background is taken as slate-100, the text should not be lighter than slate-700. If the difference is less — there is insufficient contrast, even if it "looks normal" to the eye.
Most dark theme failures boil down to a few repeated missteps:
The solution to all five problems is one — to build the palette not as a set of fixed hex codes, but as a system of tokens with meaningful names (--color-surface, --color-text-primary), where under the hood each theme has its own, separately calibrated color value. This is more expensive at the design stage, but it is precisely this approach that gives the brand the ability to be equally recognizable and equally readable regardless of which theme the product is opened in.