Essays mature
This idea is complete, spare the occasional tending.

Following System Color Scheme Settings in CSS

While it’s nice that more websites are offering both dark-on-light and light-on-dark color schemes, something is off in many of their implementations: the color schemes are managed either through express per-website preference, typically via a button or toggle, or typically via some script that runs on pageload:

if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
	setDarkColors();
}

This is fine so long as one’s preference doesn’t change — but many systems now have an “auto” mode which will change the system’s mode from light to dark or vice-versa depending on the time of day, ambient light or other factors. This can happen while a page is loaded, and if you’re setting colors at page-load time via a script, or via an explicit user preference, they won’t change with the user’s system.

It’s very easy to have your website follow users’ system display mode transitions in pure CSS. While there are many ways do this I believe this example shows the most flexible and easily-maintainable method:

:root {	color-scheme: light; }

@media (prefers-color-scheme: dark) {
	:root { color-scheme: dark; }
}

html {
	background-color: light-dark(white, black);
	color: light-dark(black, white);
}

By using the prefers-color-scheme media query, setting the color-scheme property, and using the light-dark() function, your website will automatically change colors to follow changes in the user’s system.

If you’d like to provide a way to override the system color scheme, either because you want to allow users to set an explicit preference for your site, or you’d like to test the light & dark themes simultaneously, you can add these declarations to the above CSS, changing selectors to taste:

.dark-theme { color-scheme: dark; }
.light-theme { color-scheme: light; }
§

Much as how you shouldn’t repeat yourself with any kind of variable declaration, it is crucial to ensure you’re using light/dark colors properly everywhere in your CSS, to prevent a dark light-mode foreground color from accidentally being used in the foreground on a dark dark-mode background.

This is where CSS custom properties really shine. You might already use them for color definitions:

--gray-10: #1b1d1c;
--gray-15: #262626;
--gray-25: #3b3b3b; /* and so on */

You could use light-dark to make the scale itself responsive to the color-scheme:

--gray-10: light-dark(#1b1d1c, #f0f0e9);

But I think it’s better to keep the scale numbers absolute — I use them to refer to L*ab lightness — and instead introduce semantic color roles:

--bg-main: light-dark(var(--gray-10), var(--gray-95));

If you’re responsible for a website’s styles, providing both light- and dark-mode styles is a matter of accessibility: dark mode is problematic for people with astigmatism and the color schemes people come up with for dark-mode tend to be lower-contrast🞻, and some people find light-modes physically painful. Personally, while I do have a slight astigmatism, I switch between light and dark modes frequently based on the surrounding ambient light. Both modes can cause eye strain, and I’ve become more sensitive to that as I’ve gotten older.

Picking appropriately-contrasting colors for both light- and dark-modes is not without challenges, but that’s another topic. In the meantime, as someone who does frequently switch color schemes, please have your website follow my system’s preferences.