Dark mode for SVG
SVG has a unique benefit over other image formats: CSS can be included within the markup of the image itself. For inline SVG, currentcolor
and CSS variables are simple ways to implement dark mode. When SVG is used like a typical image format, neither of those approaches work. The CSS light-dark()
function offers a solution. light-dark()
for non-inline SVG works in Firefox, Chrome and Edge. I opened a bug in Safari and support looks set to be landing soon.
Below are two SVG circles. One is a <div>
using the CSS background-image
property. The second is a HTML <img>
element. Their fill
color will depend on the users system preferences:
<div style="background-image: url('/circle.svg'); width: 40px; height: 40px;"></div>
<img width="40px" src="/circle.svg" alt="">
The contents of the circle.svg
file:
<svg style="color-scheme: light dark;" fill="light-dark(black, white)" viewBox="0 0 10 10" xmlns="http://www.w3.org/2000/svg"><circle cx="5" cy="5" r="5"/></svg>
The color-scheme
property inside the SVG is effectively saying “these are the color schemes I can support”, but the actual scheme used by the SVG can be controlled from outside, either by the color-scheme
CSS property, or by a color-scheme
meta tag in the <head>
of the HTML document. The code inside the SVG file defines which color themes the SVG is capable of and the code in the HTML document specifies which theme it wants to use.
<img width="40px" src="/circle.svg" alt="">
<img style="color-scheme: light;" width="40px" src="/circle.svg" alt="">
<img style="color-scheme: dark;" width="40px" src="/circle.svg" alt="">
The following code in the <head>
of a HTML document indicates that the document only uses dark mode, so the dark value specified in light-dark()
will be used by any SVG image that has indicated it can support dark mode via color-scheme
:
<meta name="color-scheme" content="dark" />
Other approaches
The prefers-color-scheme
media query
For SVG that are not inline, the prefers-color-scheme
media query works in Chrome/Edge and Firefox, but not in Safari. There is an open bug.
The prefers-color-scheme
media query can be used inside of a <style>
block inside an SVG:
<svg width="32" height="32" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
<style>
rect { fill: black; }
@media (prefers-color-scheme: dark) {
rect { fill: white; }
}
</style>
<rect width="32" height="32"/>
</svg>
The results of the query are impacted by both the color-scheme
CSS property and by the HTML color-scheme
meta tag.
<div style="color-scheme: light">
<img src="/square.svg">
</div>
<div style="color-scheme: dark">
<img src="/square.svg">
</div>
The <picture>
element
The <picture>
element can display one image for dark mode and another for light mode. This requires exporting the same SVG image as two different files. The <picture>
markup is more code than a simple <img>
tag, and needs to be repeated every time the icon is used. Unlike the <picture>
element, using light-dark()
within the markup of the SVG also works for CSS background images.