Dark mode arrives in the backlog as a checkbox. It behaves like a fork. From the moment you ship it you have two colour systems, two sets of contrast decisions, two screenshot sets in your marketing, and every future component has to be born twice.
We are not against it. This site is dark. We are against treating it as a toggle someone can add in a sprint.
Inverting is not designing
The naive version flips the palette and calls it done. Then the problems show up: pure white text on pure black vibrates, shadows stop conveying elevation because there is nothing left to darken, brand colours that sang on white turn muddy, and every illustration with a transparent background disappears.
- Elevation has to be rebuilt. Light themes raise surfaces with shadow. Dark themes raise them with lightness — the higher the surface, the lighter it gets.
- Brand colours usually need a second tint. The accent that passes contrast on white rarely passes on near-black.
- Pure black and pure white are both traps. Maximum contrast produces halation, where light text bleeds into dark background. It is more pronounced for people with astigmatism.
- Images need a plan. Transparent PNGs, screenshots and logos all assume a background colour somewhere.
The honest cost
Roughly a third more design time on any new component, and a permanent second column in every visual QA pass. That is the real price, and it is worth paying when your product is used at night, for long sessions, or by developers — audiences who will genuinely ask for it.
Ship one theme you have thought about rather than two you have inverted.
If you do commit, build it from semantic tokens on day one — `surface`, `text-primary`, `border-subtle` — never raw hex values in components. Retrofitting tokens into a codebase that hardcoded colours is a far bigger job than the theme itself.

