Why your icons look broken the moment you switch to dark mode
You designed a clean icon set on a white canvas. Every glyph is crisp, balanced and perfectly aligned to the pixel grid. Then someone flips the theme switch and the whole set falls apart: thin strokes glow and blur, dark blue fills disappear into the background, and your carefully chosen grey secondary icons become invisible.
This is not a rendering bug. It is optics. Dark mode icon design is not a color inversion job, it is a re-tuning job. Light behaves differently when it sits on a dark surface, and the human eye reacts differently to a bright shape on black than to a dark shape on white.
This tutorial covers exactly what to change, by how much, and how to keep a single icon set maintainable across both themes without doubling your production work.
The three optical problems that break light-mode icons on dark backgrounds
1. Halation (the glow that eats your details)
Halation is the visual bleed that happens when a bright element sits on a dark field. The bright pixels appear to expand beyond their real boundaries, especially on OLED screens and for users with astigmatism or myopia. The practical consequences:
- Thin strokes look thicker and fuzzier than they do in light mode
- Small counters (the hole in a magnifier, the gap in a bell clapper) fill in and disappear
- Pure white on pure black produces the worst blur of all combinations
Fix: never use #FFFFFF on #000000 for icon work. Use an off-white foreground (around #E6E8EB to #F2F4F7) on an elevated dark surface (around #14161A to #1C1F24). You lose almost nothing in perceived contrast and you kill most of the glow.
2. Apparent stroke weight inversion
The same 1.5 px stroke reads heavier on a dark background than it does on a light one, because the light spills outward. This is the same reason light text on dark backgrounds feels bolder. If your set is built at 1.5 px or 2 px for a 24 px grid, the dark version will feel chunky and slightly out of focus.
Fix: reduce optical weight by roughly 5 to 10 percent in dark mode. In practice, that means either a slightly thinner stroke, a slightly dimmer foreground color, or both. Dimming the color is usually the smarter option because it costs no extra geometry.
3. Color collapse
Saturated mid-tone and dark brand colors lose contrast dramatically on dark surfaces. A brand blue like #1A4FD6 scores comfortably against white but drops below usable contrast against #16181D. Reds and purples suffer the most, greens and cyans the least.
Fix: build a dark-mode variant of every semantic color, lighter and slightly desaturated. Saturation is what makes bright colors vibrate on dark surfaces, so lifting lightness alone is not enough.

Contrast targets you should actually hit
Icons that carry meaning are treated as non-text content under WCAG 2.2, so the minimum is a 3:1 contrast ratio against the adjacent background. That is a floor, not a goal. Here is a practical scale we use for interface icon systems:
| Icon role | Light mode target | Dark mode target | Why |
|---|---|---|---|
| Primary action, standalone icon buttons | 7:1 or higher | 7:1 to 12:1 (avoid pure white) | No label to fall back on |
| Icons paired with a text label | 4.5:1 | 4.5:1 to 6:1 | Label carries the meaning |
| Decorative or state icons | 3:1 | 3:1 minimum | Never the only signal |
| Disabled icons | Below 3:1 allowed | Below 3:1 allowed, keep above 1.8:1 | Must still be perceivable as present |

Side by side: what goes wrong and how to correct it
The table below maps the most common failures we see in audits against the correction that fixes them. If you only apply five changes to your set, apply these.
| Poorly adapted version | What the user sees | Corrected version |
|---|---|---|
| #FFFFFF stroke on #000000 | Glowing, blurred edges, details fill in | #E8EAED stroke on #16181D surface |
| Same 2 px stroke in both themes | Dark icons feel bolder and heavier than the UI around them | 1.75 px stroke or a dimmed foreground token in dark |
| Brand blue #1A4FD6 reused as is | Icon sinks into the background, contrast under 3:1 | Lightened, desaturated variant such as #7FA6FF |
| Secondary icons at opacity 0.45 | Nearly invisible, opacity behaves asymmetrically on dark | Opacity 0.64 to 0.72, or a solid muted token |
| Two-tone icons with a pale tint fill | The tint becomes brighter than the main shape and inverts the hierarchy | Tint becomes darker than the stroke in dark mode |
| Filled logo mark with a white keyline | Keyline disappears, the mark merges with the surface | Add a subtle dark or light outline depending on the theme |
| Drop shadows carried over from light mode | Muddy halo, no perceived elevation | Replace shadow with a lighter surface or a 1 px light border |
The two-tone inversion rule
This one catches almost everyone. In light mode, a duotone icon typically uses a dark stroke plus a pale tinted fill. If you simply swap the stroke to white and keep the pale fill, the fill is now the brightest element and it steals attention from the shape that carries the meaning. In dark mode the relationship has to flip: the stroke stays the brightest element, and the fill becomes a darker, low-chroma version of the accent color, usually the accent at 12 to 20 percent opacity over the dark surface.
Adjusting fills, opacity and color the right way
Stop using opacity for hierarchy
Opacity is convenient in light mode because black at 60 percent over white is predictable. On dark surfaces, the same value composites toward the background much faster in perceived terms, and it also breaks the moment your icon sits on an elevated card, a colored banner or an image.
Better approach: define solid color tokens for each icon level and let the theme swap them.
| Token | Light value | Dark value |
|---|---|---|
| icon-primary | #1A1D21 | #E8EAED |
| icon-secondary | #5A6069 | #A6ADB6 |
| icon-disabled | #B4B9C0 | #585E67 |
| icon-accent | #1A4FD6 | #7FA6FF |
| icon-danger | #C42B1C | #FF8A7A |
| icon-success | #186A3B | #5FD08A |
How to derive a dark variant of any brand color
- Convert the color to HSL or OKLCH rather than editing RGB by hand.
- Raise lightness until you clear 4.5:1 against your darkest surface.
- Reduce saturation by 10 to 25 percent so the color stops vibrating.
- Nudge the hue if needed: reds and oranges tend to look washed out when lightened, a small shift toward the warmer side keeps them recognisable.
- Verify against your lightest dark surface too, not just the darkest one. Elevated cards are the trap.
When to change the geometry instead of the color
Color adjustments solve most cases, but some icons genuinely need a second drawing:
- Icons where black or white is part of the meaning: a moon, a sun, a printed page, a coffee cup with white steam
- Icons with very tight counters at 16 px, where halation closes the gap
- Brand marks and third party logos that come with a mandatory light or dark lockup
- Filled icons with white knockouts, where the knockout has to become the surface color rather than white
Apple’s own guidance makes the same point: design a separate interface icon for the dark appearance when the shape itself depends on tonality, for example a full moon that needs a subtle outline so it does not vanish.

App icons versus interface icons: do not mix the rules
These are two different problems and they get confused constantly.
| Interface icons (UI glyphs) | App icons (home screen) | |
|---|---|---|
| Typical size | 16 to 32 px | Rendered large, on a fixed shape |
| Color control | Recolored at runtime with currentColor or tokens | Exported as separate assets per appearance |
| Background | Transparent, inherits the surface | Dark variants usually get a dark or transparent background layer |
| Main risk | Halation and contrast loss | Losing brand recognition, glowing white plates |
For app icons, keep the silhouette and the core mark identical across appearances. Change the backdrop and the tonal balance, never the shape that people scan for on a crowded home screen.
A workflow for maintaining one icon set across both themes
The goal is one source of truth, two rendered results. Here is the process we use on production design systems. There’s a fuller breakdown if you want the detail.
Step 1: build every icon as a single-color, transparent SVG
Strip hardcoded fills from the source files. Use fill="currentColor" or stroke="currentColor" so the icon inherits the CSS color of its parent. One file, infinite themes.
<svg viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" aria-hidden="true">
<path d="M4 7h16M4 12h16M4 17h10"/>
</svg>
Step 2: drive color with CSS custom properties
:root {
--surface: #FFFFFF;
--icon-primary: #1A1D21;
--icon-secondary: #5A6069;
--icon-accent: #1A4FD6;
}
[data-theme="dark"] {
--surface: #16181D;
--icon-primary: #E8EAED;
--icon-secondary: #A6ADB6;
--icon-accent: #7FA6FF;
}
.icon { color: var(--icon-primary); }
.icon--muted { color: var(--icon-secondary); }
.icon--accent { color: var(--icon-accent); }
Add color-scheme: light dark; to your root element and respect prefers-color-scheme as the default, with a manual override stored in local storage. You can see the idea applied on xpenso.fr.
Step 3: add a stroke-weight compensation rule
If your set uses stroked icons and you want geometric compensation rather than tonal compensation, expose the stroke width as a variable too:
:root { --icon-stroke: 2; }
[data-theme="dark"] { --icon-stroke: 1.75; }
.icon { stroke-width: var(--icon-stroke); }
Apply this only to inline SVG, and only if your icons are drawn with strokes rather than converted outlines. If your icons are outlined paths, adjust tonally instead.
Step 4: handle the exceptions with a paired override
For the small handful of icons that genuinely need a second drawing, keep a naming convention such as icon-moon.svg and icon-moon-dark.svg, and resolve the pair in your icon component. Keep the exception list short and documented, otherwise your set silently doubles in size.
Step 5: build a two-column contrast sheet
Create one artboard with your full set on the light surface and the same set on the dark surface, side by side, at 16 px, 20 px and 24 px. This is where you catch the failures no automated check will find:
- Counters that close up at 16 px
- Icons that suddenly look heavier than their neighbours
- Accent colors that shout on dark but whisper on light
Step 6: test in real conditions
- On an OLED phone at low brightness, in a dark room. This is where halation is worst.
- On an LCD monitor in a bright office, where dark mode contrast is often too low.
- Through a blur filter at 2 px to simulate low visual acuity.
- With forced colors mode and Windows high contrast themes, which strip your colors entirely.
- With an automated contrast check across every icon token and surface pair.
Step 7: document the rules, not just the assets
Ship a short page in your design system that states the foreground values, the surface values, the stroke policy, and the list of icons with dedicated dark variants. Without it, the next designer will drop a #FFFFFF icon into the set within a month.

Quick checklist before you ship
- No pure white foreground on pure black surfaces
- Every meaningful icon clears 3:1, standalone icon buttons clear 4.5:1 or more
- Opacity replaced by solid tokens for hierarchy
- Duotone fills inverted so the stroke stays brightest in dark mode
- Accent colors lightened and desaturated, verified on every dark surface level
- Shadows replaced by surface elevation or subtle borders
- Icons tested at 16 px, not only at 24 px
- Theme switching does not cause a flash of the wrong icon color on first paint
- Decorative icons marked
aria-hidden="true", meaningful icons given accessible labels
FAQ
How do I make my icons dark mode ready?
Export them as single-color transparent SVGs using currentColor, then define light and dark color tokens and swap them with CSS custom properties. Use an off-white foreground instead of pure white, reduce optical stroke weight by around 5 to 10 percent, and redraw only the few icons whose meaning depends on black or white areas.
How do I design for dark mode in general?
Start from a dark grey surface rather than pure black, build an elevation scale of lighter greys instead of relying on shadows, lighten and desaturate every accent color, and check contrast against each surface level. Dark mode is a separate tonal system that shares the same layout, not an inverted copy of the light theme.
Is there a dark mode for logos?
Yes, and most mature brand systems include one. The usual approach is a monochrome or reversed lockup for dark backgrounds, sometimes with a subtle keyline so filled shapes do not merge into the surface. Keep the silhouette identical so recognition is not affected, and never simply invert the colors of a multi-color mark.
Should icons be thinner or thicker in dark mode?
Optically thinner. Light shapes on dark backgrounds spread visually, so identical geometry reads heavier. Either shave a small amount off the stroke width or dim the foreground color slightly. Both approaches work, dimming the color is easier to maintain.
Is dark mode healthy for your eyes?
It depends on the environment and the person. In dim light, dark interfaces reduce the brightness difference between the screen and the room, which many people find more comfortable. In bright environments, dark mode can actually reduce readability, and users with astigmatism often experience more blur on dark backgrounds. That is precisely why the contrast and halation controls in this guide matter: they make dark mode comfortable rather than merely fashionable. The right answer is always to offer both themes and follow the system preference by default.
Do I need two separate icon sets?
No, and you should actively avoid it. One tokenised set covers roughly 90 to 95 percent of interface icons. Reserve dedicated dark variants for app icons, brand marks, and glyphs where tonality carries meaning.
Wrapping up
Good dark mode icon design is mostly about restraint. Pull white back to off-white, pull saturated accents up in lightness and down in chroma, replace opacity with real tokens, and let one SVG source inherit its color from the theme. Do that and your icon set will feel deliberate in both appearances instead of looking like a light-mode set that someone flipped at the last minute. lmnt.me published something useful on the subject.
If you want a second pair of eyes on an existing icon library or a design system audit, our team at osilly.cz works with product teams on exactly this kind of tuning.

0 Comments