Detect if the browser is using dark mode and use a different favicon Detect if the browser is using dark mode and use a different favicon google-chrome google-chrome

Detect if the browser is using dark mode and use a different favicon


Adding and removing an icon from the document’s head works in Firefox but not Safari:

Chrome is still implementing (prefers-color-scheme: dark), so the jury’s still out. https://crbug.com/889087. In Chrome 76 with --enable-blink-features=MediaQueryPrefersColorScheme, this correctly sets the icon when the page is loaded, but does not respond dynamically to changes in dark mode.

Safari adds a grey background to dark icons in dark mode (for example, Wikimedia Foundation, Github), so this workaround isn't necessary for legibility.

  1. Add two link rel=icon elements with ids for later:

    <link rel="icon" href="a.png" id="light-scheme-icon"><link rel="icon" href="b.png" id="dark-scheme-icon">
  2. Create a CSS media matcher:

    matcher = window.matchMedia('(prefers-color-scheme: dark)');matcher.addListener(onUpdate);onUpdate();
  3. Add/remove the elements from the document's head:

    lightSchemeIcon = document.querySelector('link#light-scheme-icon');darkSchemeIcon = document.querySelector('link#dark-scheme-icon');function onUpdate() {  if (matcher.matches) {    lightSchemeIcon.remove();    document.head.append(darkSchemeIcon);  } else {    document.head.append(lightSchemeIcon);    darkSchemeIcon.remove();  }}


To make it a little more generic than Josh's answer, try this whilst the browsers still get around to implementing media natively. (Notice no hardcoded number of themes, ids, or media-queries in the JS; it's all kept in the HTML.)

<link rel="icon" href="/favicon.ico?light" media="(prefers-color-scheme:no-preference)"><link rel="icon" href="/favicon.ico?dark"  media="(prefers-color-scheme:dark)"><link rel="icon" href="/favicon.ico?light" media="(prefers-color-scheme:light)">
$(document).ready(function() {    if (!window.matchMedia)        return;    var current = $('head > link[rel="icon"][media]');    $.each(current, function(i, icon) {        var match = window.matchMedia(icon.media);        function swap() {            if (match.matches) {                current.remove();                current = $(icon).appendTo('head');            }        }        match.addListener(swap);        swap();    });});

The upshot is that once that attribute is supported, you just need to remove the Javascript and it'll still work.

I deliberately split /favicon.ico?light into two tags instead of a single one with media="(prefers-color-scheme: no-preference), (prefers-color-scheme:light)" because some browsers that don't support media permanently pick the first rel="icon" they see… and others pick the last!


CSS has a theme mode detection using prefers-color-scheme media feature:

@media (prefers-color-scheme: dark) {  ...}

With that in mind, nowadays you can use an SVG as a favicon for your website:

<link rel="icon" href="/favicon.svg" type="image/svg+xml">

Then you can update the SVG favicon design using the CSS prefers-color-scheme media feature. Below is an SVG rectangle with rounded corners, which has a different color, depending on the active theme:

<svg width="50" height="50" viewBox="0 0 50 50" xmlns="http://www.w3.org/2000/svg">  <style>    rect {      fill: green;    }    @media (prefers-color-scheme: dark) {      rect {        fill: red;      }    }  </style>  <rect width="50" height="50" rx="5"/></svg>

Now, considering the current browser support for the SVG favicon, a fallback is required for the older browsers:

<link rel="icon" href="/favicon.svg" type="image/svg+xml"><link rel="icon" href="/favicon.png" type="image/png"><!-- favicon.ico in the root -->

From https://catalin.red/svg-favicon-light-dark-theme/

Here's a demo too: https://codepen.io/catalinred/pen/vYOERwL