Safari makes this really tricky.
In most browsers, using multiple favicons with media="(prefers-color-scheme: dark)"
or even a fancy SVG that changes with CSS works great. But Safari? Not so much.
Safari always picks the first favicon it sees in the HTML and ignores the rest, even if they have media queries.
It doesn’t support SVG favicons at all, so forget about using media queries inside SVG.
And even if you try switching the favicon with JavaScript, Safari often ignores those changes or just sticks with the original one — even across page loads, even if you try to bust the cache.
If your site is server-rendered, you could try to serve a different favicon based on the user’s system theme. This involves detecting their preference before the page loads (which is tricky but possible in some setups using headers or early JavaScript). But honestly, it’s not super reliable and pretty advanced to pull off cleanly.
You can try forcing a favicon reload using JavaScript like this:
const dark = window.matchMedia('(prefers-color-scheme: dark)').matches;
const link = document.createElement('link');
link.rel = 'icon';
link.href = dark ? 'dark-icon.png?v=' + Date.now() : 'light-icon.png?v=' + Date.now();
document.head.appendChild(link);
This sometimes works — by adding a timestamp, you trick the browser into thinking it's a fresh file. But again, Safari is stubborn and may ignore this too.
Honestly, the most reliable thing you can do is pick a single favicon that looks good in both light and dark mode. Something with a little border or contrast so it stands out no matter what.
Safari just doesn’t play nice with dynamic favicons. You can try some workarounds with JavaScript, but they’re hit-or-miss. If it’s important for your brand, go with a high-contrast favicon that works in both themes.