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

Google Chrome 73 has been released, and it adds "dark mode" support to the browser. I notice that a lot of favicons look bad now.

Dark mode Wikimedia Foundation tab screenshot

Dark mode Codecademy tab screenshot

Is there a way to detect if the user is using dark mode and change the favicon?

40607 次浏览

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

The easiest option is to change the source when you change the mode on your computer.

var element = document.querySelector("link[rel='icon']");
const darkModeListener = (event) => {
if (event.matches) {
console.log("dark");
element.setAttribute("href","img/favicon-dark.svg");
} else {
console.log("light");
element.setAttribute("href","img/favicon-light.svg");
}
};


// Update favicon on Mode change
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', darkModeListener);


// Check Mode on load
if (window.matchMedia && window.matchMedia('(prefers-color-scheme:      dark)').matches) {
element.setAttribute("href","img/favicon-dark.svg");
} else {
element.setAttribute("href","img/favicon-light.svg");
}

But if you have a multi-device favicon then you need to do something like this ...

// Switch Favicon on Dark/Light Mode
let colorSchemeQueryList = window.matchMedia('(prefers-color-scheme: dark)');
const setColorScheme = e => {
if (e.matches) {
// Dark
var favicon = document.querySelectorAll('link[data-type="favicon"]');
var i = favicon.length;
while (i--) {
favicon[i].setAttribute('href', favicon[i].dataset.dark);
}
} else {
// Light
var favicon = document.querySelectorAll('link[data-type="favicon"]');
var i = favicon.length;
while (i--) {
favicon[i].setAttribute("href", favicon[i].dataset.light);
}
}
}
           

setColorScheme(colorSchemeQueryList);
colorSchemeQueryList.addListener(setColorScheme);
<link rel="apple-touch-icon" sizes="180x180" href="Images/favicon/light/apple-touch-icon.png" data-type="favicon" data-light="Images/favicon/light/apple-touch-icon.png" data-dark="Images/favicon/dark/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="Images/favicon/light/favicon-32x32.png" data-type="favicon" data-light="Images/favicon/light/favicon-32x32.png" data-dark="Images/favicon/dark/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="Images/favicon/light/favicon-16x16.png" data-type="favicon" data-light="Images/favicon/light/favicon-16x16.png" data-dark="Images/favicon/dark/favicon-16x16.png">
<link rel="favicon" href="Images/favicon/light/site.webfavicon" data-type="favicon" data-light="Images/favicon/light/site.webfavicon" data-dark="Images/favicon/dark/site.webfavicon">
<link rel="mask-icon" href="Images/favicon/light/safari-pinned-tab.svg" color="#000000" data-type="favicon" data-light="Images/favicon/light/safari-pinned-tab.svg" data-dark="Images/favicon/dark/safari-pinned-tab.svg">