Troubleshooting & How-Tos 📡 🔍 Web Development

Auto-Switching a Website to Dark Mode

I usually prefer “light mode” when using a desktop in a well-lit room, but “dark mode” is popular for a lot of reasons. I use it on my phone to cut down on battery usage. (It takes more power to light up the screen than to leave it dark.) Lots of people find it reduces eyestrain. If you’re in a dark room, a bright screen is going to look awfully harsh.

But what about all those websites that are built with bright white backgrounds that mimic the look of text on paper? A dark site isn’t a problem on a light screen, but a searing bright rectangle in the middle of a bunch of dark gray or black window borders, controls, sidebars, etc. is just jarring.

Can a website detect dark mode? What level of JavaScript wizardry is needed to switch styles?

Yes. And none!

Pure CSS Using @media Queries

It turns out to be dead simple. You don’t even need JavaScript! You can do the whole thing in a @media query using prefers-color-scheme in your stylesheet. Depending on how well-ordered your CSS is, you’ll probably spend more time finding a color combination that works for you than implementing the code!

For example, on this site, I put this block after all the body and link colors are defined, but before the more specific colors for the header. Using the cascading part of Cascading Style Sheets, this will override just the colors on these elements, and won’t impact any colors defined later in the stylesheet.

@media (prefers-color-scheme:dark) {
	body {background: #222; color: #fff}
	h1, h2, h3, h4, footer, .categories {border-color: #666}
	a:link	{color: #0f0}
	a:visited	{color: #090}
	a:active	{color: #ccf}
	a:hover		{color: #88f}
	.shadowbox {border-color: #eee; box-shadow: 5px 5px 5px #0c0}

A modern web browser will know if it’s running in dark mode, and will either use or ignore that block. Older browsers will just ignore it.

Visiting this page in light mode? You’ll see the original design, with a white background and black text. Visiting in dark mode? You won’t get your eyes blasted out by a bright white rectangle. Instead you’ll see a dark gray page with white text.

All with 9 lines of CSS! No JavaScript needed!

Screenshot of this page displaying a white background with black text and green header.

Screenshot of this page displaying a black background with white text and green header.

Admittedly, that’s partly because I’m using a minimalist design that’s entirely built in CSS. And the header ended up working with both color schemes.

If your layout is more complicated, you might need to move things around in your stylesheet, or have several dark-scheme blocks for different parts of it.

System Colors Are Still Light?

Default-styled buttons, scrollbars, and so on are still assumed to only support the light scheme. You could set them all individually in the same media query…or you could use color-scheme to tell the browser that it’s OK to use light or dark system colors. The simplest approach is to just apply it to the entire page:

:root {color-scheme: light dark}

What About Images?

Transparency takes care of backgrounds, but that doesn’t help if you need a different color scheme for the image itself. I contemplated using classes to turn display on or off on two different images, but I wondered if the way HTML supports responsive images (load a larger or smaller image depending on window size and screen density) might also support color schemes. As it turns out, it does!

Take your default image, wrap it in a <picture>...</picture>, then add a <source> pointing to your alternate image and using the same media query, (prefers-color-scheme: dark).

Here’s some sample code on this page:

	media="(prefers-color-scheme: dark)">
  <img src="/tech-tips/images/sandiego-hilton-daytime.jpg"
	alt="A tall building and a shorter one..."
	width="640" height="480"
	class="center shadowbox">

If you’re viewing this page in light mode or an older browser, you should see a daytime scene below. If you’re using dark mode, you should see a nighttime scene.

A tall building and a shorter one along a shoreline, seen from above, with grass between them.

Of course, switching between day and night photos is probably not the way you’d use this. More likely you’d have a diagram, or a logo, or some layout component that you need to switch between a version that’s viewable against a dark background and a version that’s viewable against a light background.

How Do I Test This?

Well, you can change your system settings. But that can take time to redraw every program you have open. And what if you’re using an OS that doesn’t have a light/dark switch?

Firefox has this in its Web Developer Tools. In the Inspector tab, you’ll see a sun and moon, which you can click on to force either light or dark mode.

Screenshot of Firefox Development Tools with a light theme, but the page is displayed in a dark theme, with a red circle around the sun and moon buttons.

Chrome is a lot more hidden. It’s a lot easier to find out how to put Dev Tools into dark mode, not the page you’re testing, but it can be done: open Dev Tools in any Chromium browser and use keyboard commands Ctrl+Shift+P or Cmd+Shift+P and type Rendering to search.

Screenshot of Chromium Dev Tools with the search field open and render typed, with the Rendering Drawer option highlighted.

From there you can open the Rendering drawer with a lot of options in it, or go straight to light/dark mode from the search results. (Thanks, FiXato!)

Screenshot of Chromium Dev Tools with the Rendering drawer open showing Auto Dark Mode and prefers-color-scheme emulation.

Edge is Chromium under the hood, so you can use the same approach, but you can also find it in the More Tools menu under Rendering.

Safari ???

Extensions like Dark Reader usually create their own themes for a page, and even if they use your dark styles, they may miss some or add their own changes. So while they may be useful for viewing the site, they aren’t going to help with testing.


There you have it: Pure CSS to change your site’s color scheme, and pure HTML5 to swap images accordingly!

Let me know if you find any quirks with this approach, or if you have any tips. Or if you know how to test in Safari! I haven’t set up comments on this site yet, but you can email me or reply to the Mastodon post.