# Dark mode support for HTML pictures
Sizing native HTML images is easier compared to CSS backgrounds thanks to flexbox and Bootstrap-style grid layouts.
HTML's `<picture>` elements allowing for various `<source srcset="">` children is great for supporting modern (avif, webp) as well as fallback image standards (png). It even supports media queries.
Unfortunately, the media queries for `prefers-color-scheme: dark` is not enough to allow the browser to choose light-vs-dark mode versions of an image. This is due this value expressing the UserAgent's current preference, instead of the actual selected Theme.
**Declaring the image with dark/light mode and various formats:**
```HTML
<picture>
<source srcset="image-dark.png" media="none and (prefers-color-scheme: dark)" />
<source srcset="image-light.png" media="none and (prefers-color-scheme: light)" />
<img src="light-image.png" alt="" />
</picture>
```
**Note:** including `none and` prevents flickering where the foreground and background color are momentarily out of sync.
**Custom JS that shows/hides sources depending on current theme:**
```js
/**
* Make <picture> <source> elements with media="(prefers-color-scheme:)"
* respect custom theme preference overrides.
* Otherwise the `media` preference will only respond to the OS-level setting
*/
const updateSourceMedia = (theme) => {
const pictures = document.querySelectorAll('picture')
pictures.forEach((picture) => {
const sources =
picture.querySelectorAll(`
source[media*="prefers-color-scheme"],
source[data-media*="prefers-color-scheme"]
`);
sources.forEach((source) => {
// Preserve the source `media` as a data-attribute
// to be able to switch between preferences
if (source?.media.includes('prefers-color-scheme')) {
source.dataset.media = source.media
}
// If the source element `media` target is the `preference`,
// override it to 'all' to show
// or set it to 'none' to hide
if (source?.dataset.media.includes(theme)) {
source.media = 'all'
} else if (source) {
source.media = 'none'
}
})
})
}
```
This needs to be called onload (`<body onload="foo()">`) and added to the Theme Button.
**To get the new theme value:**
```js
/**
* Gets the new theme based on the current theme.
* @returns {string} The new theme.
*/
const getNewTheme = () => {
let currentTheme = document.documentElement.getAttribute("data-theme");
return currentTheme === "light" ? "dark" : "light";
}
```
Source: https://larsmagnus.co/blog/how-to-make-images-react-to-light-and-dark-mode
#webdev #darkmode #html5 #image