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 at page load:
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, causing issues for people low-vision abilities ✴︎
-
some people, myself included, can find light-modes physically painful, particularly in contrast to everything else on their screen using dark mode
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.
I’ve seen the argument going around: “don’t use @prefers-color-scheme because people might not actually want light/dark mode, they just have their computer setup that way”, and I have no polite words in response to this, because you are causing myself and many other people pain every time we open your webpages. There are literally dozens of workarounds to this sadism. The query is literally called prefers-color-scheme for a reason. There is also the prefers-contrast query, are you also arguing people aren’t making this choice deliberately either? If you are making this argument, you should wear a shock collar for a week in penance, make it trigger by bursts of radio signal or something or give the remote to a dog, because you need to understand what it’s like for external factors to cause you pain.
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’s colors follow my system’s preferences.