How to change an HTML image URL in dark mode

By

Learn how to swap an HTML image in dark mode without CSS or JavaScript, using the picture tag with a source and the prefers-color-scheme dark media feature.

~~~

To change an image in dark mode, wrap the img tag in a picture element and add a source element with the media attribute set to (prefers-color-scheme: dark). It’s plain HTML. No CSS or JavaScript needed.

Using CSS it’s pretty easy to apply changes when the system is in dark mode, thanks to the prefers-color-scheme media feature. Check my blog post on dark mode if you want to learn more about it.

But I ran into a problem: how do you change an image defined in the HTML, rather than in a CSS rule? Think of a logo with dark text that becomes invisible on a dark background.

Here’s the solution:

<picture>
  <source 
    srcset="dark.png" 
    media="(prefers-color-scheme: dark)">
  <img src="light.png" alt="The site logo">
</picture>

If dark mode is enabled, the browser picks dark.png as the source for the img tag. Otherwise, it falls back to light.png.

How does the browser pick the image?

The browser goes through the source elements in order. It uses the first one whose media query matches. If none match, it uses the src of the img tag.

The img tag is required inside picture. It’s what actually renders the image, and it’s where the alt text goes.

The picture tag is very well supported. Old browsers that don’t implement it, or don’t support dark mode, fall back to displaying light.png.

The browser does not download both images. In dark mode it only downloads dark.png, in light mode only light.png. No wasted bandwidth.

One more nice thing: the swap is live. If the user switches the OS theme while the page is open, the browser re-evaluates the media query and shows the other image, without a reload.

Watch out for src vs srcset

One thing to be careful with: on the source element you must use srcset, not src.

If you write src="dark.png" on the source tag, the browser ignores it and always shows the light image. There’s no error in the console, the dark image just never appears. Switch the attribute to srcset and it works.

Tagged: HTML · All topics
~~~

Related posts about html: