Design & Front-End
Dark mode has quietly gone from a novelty to an expectation — a lot of visitors will toggle it if it's there, and some will bounce off a site that fights their OS-level preference entirely. The trouble is that most "dark mode" implementations are just the light theme with the colours inverted or a CSS filter thrown over the top, and it shows: washed-out photography, crushed contrast, and a general sense that dark mode was an afterthought rather than a design decision.
filter: invert(1) and similar tricks are tempting because they're one line of CSS. The problem is that inversion doesn't understand what it's looking at — it flips a carefully chosen shadow into a glow, turns a subtle brand colour into something jarring, and inverts photography into something that looks like a photo negative unless you carve out exceptions for every image on the page. It's a shortcut that creates more special cases than it saves.
A proper dark theme starts from the same design principles as the light one — contrast, hierarchy, brand personality — but reaches its own conclusions about colour. A few things that don't just "carry over" from a light palette:
#000000) tends to feel more considered and causes less halation around light text.The cleanest way to implement this is a set of semantic custom properties — --color-surface, --color-text, --color-accent — rather than hard-coded hex values scattered through your stylesheet. Each theme is then just a different set of values assigned to the same variable names:
:root {
--color-surface: #ffffff;
--color-text: #1a1a1a;
}
[data-theme="dark"] {
--color-surface: #16131a;
--color-text: #ede7f2;
}
Every component then just references var(--color-surface) and var(--color-text) once, and the theme switch becomes a single attribute change on the root element rather than a maintenance headache spread across every file.
Photography and product shots often need their own dark-mode treatment, not just a darker background around them. Depending on the project, that can mean a genuinely separate image asset shot or edited for the dark context, a subtle overlay to knock back brightness, or simply making sure images sit inside a card with enough padding that the harsh edge between a bright photo and a dark background isn't the first thing anyone notices.
Default to the visitor's OS-level preference via the prefers-color-scheme media query on first visit, then let them override it with a visible toggle — and remember that choice for next time, typically in localStorage. Applying the theme before the page paints (rather than after JavaScript loads) avoids a jarring flash of the wrong theme on every page load.
Don't assume a contrast ratio that passes in light mode automatically passes in dark mode — check both independently. It's common for a colour pairing to pass comfortably in one theme and sit right on the edge of failing in the other.
It takes a bit more planning than a CSS filter, but it's worth it — happy to talk through what that looks like for your site.