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.
Add two link rel=icon elements with ids for later:
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.)
$(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!
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:
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);