Dark mode favicons with one SVG and one media query

Make your favicon adapt to the user's system theme without JavaScript, without a second file, using the media query support built into SVG.

The tab bar is drawn in the user’s system theme. A black logo on a light tab is fine; the same black logo on a dark tab is invisible. An adaptive favicon solves this with a single SVG file and a prefers-color-scheme media query embedded inside it.

The pattern

Inside your favicon.svg, add a <style> block:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
  <style>
    path { fill: #0a0a0a; }
    @media (prefers-color-scheme: dark) {
      path { fill: #fafafa; }
    }
  </style>
  <path d="M12 2 L22 22 L2 22 Z" />
</svg>

Light tabs get the dark fill; dark tabs get the light fill. No JavaScript, no second file, no HTML change.

Why this works

SVG is a live document. The browser renders it, parses its internal CSS, and responds to media queries the same way it does for the main page. prefers-color-scheme is one of those queries. The favicon render re-evaluates when the system theme flips.

Chromium, Firefox, and Safari all honour this. Older browsers that cannot parse the media query fall through to the default fill, which is your light-mode icon. Worst case is the same as not shipping an adaptive favicon at all, which is fine.

Authoring tips

  • Use currentColor sparingly. It ties fills to the color property, which does not propagate from the tab strip. Set fills directly.
  • Aim for contrast, not inversion. A dark-mode version is not always a colour flip. A brand colour that reads well on both themes is often a better choice than swapping between pure black and pure white.
  • Test the 16 pixel render. Dark mode tab icons render at 16×16. Subtle colour shifts collapse at that size. Use strong contrast against both the light and dark tab background.

Multi-fill icons

If your icon has multiple paths with different fills, you can target each one:

<style>
  .mark { fill: #111; }
  .accent { fill: #e11d48; }
  @media (prefers-color-scheme: dark) {
    .mark { fill: #fafafa; }
    /* .accent stays, because red reads on both themes */
  }
</style>
<path class="mark" d="..." />
<path class="accent" d="..." />

Keep accent colours stable, flip only the neutrals.

Testing

Toggle your system theme. Most OS themes also propagate to browser chrome within a second. Firefox’s Inspector can simulate prefers-color-scheme: dark without changing system settings — handy for checking both fills in one session.

What not to do

  • Do not ship two SVG files and swap via JavaScript. Adds a request, adds complexity, breaks for users with JS disabled, and the media query already works.
  • Do not animate the transition. Favicons that pulse or morph are classic dark patterns and distract from the page content.
  • Do not rely on the ICO fallback to handle dark mode. Multi-res ICO files are rasterised, and you cannot embed media queries in a raster. If you need dark-mode behaviour and support Internet Explorer or other non-SVG browsers, accept they get the light version.

One source, two themes

Faviconry generates an adaptive favicon when you toggle the dark-mode option, embedding the prefers-color-scheme media query inside the generated SVG with colours you pick for each theme.

Framework-specific setup

The link tag is the same across frameworks; only the file location differs:

Frequently asked questions

Why does my favicon disappear in dark mode?

A dark-on-light logo becomes invisible against a dark browser tab. Safari and Chrome draw tab strips in whatever theme the OS or browser is set to, and a solid black mark on a dark grey tab reads as nothing. An adaptive favicon swaps colour based on prefers-color-scheme.

Does prefers-color-scheme in an SVG work in every browser?

Chromium, Firefox, and Safari all honour prefers-color-scheme inside an SVG favicon. The fallback for any browser that cannot parse it is simply the default fill, so worst case the user sees your light-mode icon.

Can I animate the colour change?

Yes, but do not. Favicon animation distracts users, some browsers cap frame rates aggressively, and a tab icon that pulses is a classic dark pattern. Use the media query for a static swap only.