A hard day’s light: Adding a snazzy dark mode to the site

What it takes to give a site a dark mode, how your device decides which mode you can see, and why it's more complicated than inverting a colour palette.
The Categories page on this site has two pictures on it.
"Main Quest" is a papercraft hill in daylight, a green slope under a blue sky, an orange flag at the summit, and clouds hovering overhead.
"Side Quests" is the same scene but at night, the hills a darker shade of blue, a crescent moon and a scatter of stars, and now with several flags dotted around the hill.
I generated those images to convey the feeling of taking a main path or a detour, charting a course between the serious stuff and the fun stuff. Overlaying a day and night cycle helped make the point.
So I guess it was subconscious foreshadowing when I decided to actually build a dark mode: the site had been carrying a day scene and a night scene at its centre the whole time, waiting for the rest of the pages to catch up.
So this is the story of the rest catching up. What it takes to give a site a dark mode, how your device can decide which mode you get to see, and why the work required is more complicated than simply inverting a colour palette.
What is a dark mode?
A dark mode is a second version of a website or app with the colours reversed, light text on a dark background instead of dark text on a light one. People reach for it to cut glare when reading at night, to save battery on phone screens that light each pixel individually, or simply because they prefer it.
The idea is older than it looks. The earliest computer screens were dark by default, glowing green or amber text on black, and the bright white page only became the norm when computers started imitating paper.
The modern version arrived as an operating system feature. Apple added a system-wide dark appearance to the Mac in 2018, and phones followed the year after, with the setting built into iOS and Android.
Around the same time browsers gained a way for websites to detect that setting. Once a site can ask the operating system which appearance you chose, it can answer in kind...
The machine decides
That's the principle followed by this site. It follows whatever theme your operating system or your browser is set to. Set your laptop preferences to dark and the site is dark. Set it to light and it's light. And if you happen to have your OS follow a sunrise-to-sunset cycle, this site mirrors that too.
The mechanism is simple. Browsers expose the operating system's appearance setting through a media query called prefers-color-scheme, and a stylesheet can define one set of colours for light and another for dark. The browser applies the right set before it draws the first pixel. No JavaScript runs, no preference is stored, nothing flashes white while a script works out what you wanted.
There's a consequence, though. If your machine is set to light and you fancy reading this site in dark mode (or vice versa), you can't.
A toggle would give you that flexibility, but that'd require a button in the header, together with a stored preference and a script that has to run before paint to stop the wrong theme flashing on every page load. Plainly speaking, none of that is worth the baggage for a personal blog.
A second design, not a filter
Fun lesson for anyone thinking of doing the same: you can’t make a dark mode just by dimming the lights on your base theme. Every colour on this site was originally chosen against a warm paper background, a shade called #FAF5F1 that sits somewhere between cream and plaster. Swap that background for something dark and the rest of the palette doesn't politely adapt. It falls over, one domino at a time.
The crimson went first. Links on this site are a deep warm red, #A4243B, and against the pale page it reads clearly at a contrast ratio of about seven to one. Contrast ratio is the measure accessibility guidelines use for legibility, the luminance of the text against the luminance of whatever is behind it. The Web Content Accessibility Guidelines ask for at least 4.5 to 1 for body text.
Put that same crimson on a near-black page and it drops to roughly a third of the legibility it had. The colour didn't change. The maths behind it did. So dark mode gets its own crimson, #E0667A, the same family lifted several steps brighter, which lands at 5.65 to 1 against the dark page.
Then the lift caused its own failure. Some parts of the site put white text on a crimson background. On the deep light-mode crimson, white passes comfortably. On the brighter dark-mode crimson it drops to roughly half, 3.3 to 1, under the line. The fix was to flip those labels to dark ink. Which means dark mode contains small pieces of chrome that are lighter than their light-mode versions, with darker text on them.
The footer was especially tricky. In light mode the footer is a dark band, #241B1D, sitting under the pale page, and the contrast between the two is what tells you the page has ended and the footer has begun. Keep that footer colour and put it on a dark page and the two surfaces measure 1.11 to 1 against each other, which is a technical way of saying they're the same colour. The footer effectively vanished.
The fix inverts the original logic. In light mode the footer is darker than the page. In dark mode it has to be lighter than the page, a slightly raised #2E2420, so the band separates again. Same goal in both modes, just taking the opposite direction.
Thank heavens for design tokens
There were more examples of these. The muted grey used for dates and captions had to be lifted or it fell under the line. The code blocks kept their own dark colours in both modes, which meant checking they didn't glare in light and didn't disappear in dark. Every one of these changes is small, but the cumulative effect is to make a dark theme the equal of its lighter counterpart.
The plumbing that makes this manageable is the design token system this site already has, the one I wrote about when the header changed from crimson to blue. Every colour lives behind a named variable, brand ink, brand crimson, brand background, and the components only ever refer to the names.
Dark mode swaps the values behind the names and touches nothing else. Without that layer, this work would have meant hunting hex codes through every component. With it, the whole dark palette lives in one block of one file.
The overhead
Should a personal site have a dark mode at all? Let's not kid ourselves, it effectively doubles a chunk of your design surface. Permanently.
Every future colour decision now has to be made twice and checked twice. When I add anything to this site going forward, it has to be tested against two backgrounds, and the second test might fail in ways the first didn't predict.
That’s also why so many dark modes can turn out badly. If treated as a basic inversion, a filter you switch on, dark mode ships with unreadable links, vanished borders, and glaring images.
I do think it's worth it, though. There are people who like to browse with their systems set to dark, and before this update the site was overriding that choice on every visit, shining a pale page at those who'd asked their computers for the opposite. Now it respects their wishes.
But I'd still argue that a site with a dark mode that’s borked is much worse than one without. The only way to have the good kind is to accept that you're maintaining two designs from here on.
The light side of the moon
The covers on this site are gouache paintings, lit warm, made for the paper background.
On the dark page they needed one last touch, a hairline border around each image, because a bright painting on a black page floats loose without an edge to sit in. It's a one-pixel line at twelve percent opacity. You'd never consciously notice it, but now the pictures have stopped floating.
And the two papercraft hills on the Categories page are still there, the green one in daylight, the blue one under the moon. They no longer have to carry the site's whole day and night cycle all by themselves. Now the rest of the pages know how to do both.

