Today we have new super cool ability to detect OS UI theme and change the site view according to it. It makes us to use new techniques to write themeable and easy to customize css and html. In this series of posts I'm going to tell you how to create simple themeable design for your web apps.
While we can change the page visualization with CSS and HTML, there is one element which still has no such ability. And you already have know what I'm talking about. Yes, it's a favicon.
If you take a look at favicons of Dev.to or Github in dark mode, you'll see they became almost invisible. We need to change it and make favicon to react on theme switch. The most logical way to do so is media
attribute of the link element which allows favicon to react on CSS media query passed into attribute value. But, unfortunately, the list of media queries supported by link's media attribute doesn't include prefers-color-scheme
.
And, fortunately, we could make it works using JavaScript. Well let's do it.
Here it is the live preview of how it could work.
TL;DR For those who want ready solution, use the library:
rumkin / favicon-switcher
Make favicon react on media queries
Listen for theme switch
We need to collect all link elements from the page head, get media
attribute and match it using window.matchMedia()
method. This method returns MediaQueryList, which allows listen changes and we will use it:
window.matchMedia('(prefers-color-scheme:light)').addListener((e) => {
e.matches // Determine wether query matched or unmatched
})
Add icons
Now we need to insert icons for different themes into a page body:
<link rel="icon" media="(prefers-color-scheme:dark)" href="favicon-dark.png" type="image/png" />
<link rel="icon" media="(prefers-color-scheme:light)" href="favicon-light.png" type="image/png" />
Switch the icon
To make browser switch a tab's icon it's enough to make <link>
element to be the last <link>
element inside of the <head>
. This works fine, but Chrome currently has a bug which breaks such icon switching in some conditions. To avoid this bug, we need to create new <link>
and append it to the head children list after other links.
const favicon = document.createElement('link')
link.setAttribute('rel', 'favicon icon')
head.appendChild(link)
// Listen media change
window.matchMedia('(prefers-color-scheme:light)')
.addListener((e) => {
if (! e.matches) {
return
}
// Apply new favicon source
const source = document.querySelector('link[rel*="icon",media="(prefers-color-scheme:light)"]')
if (source === null) {
return
}
link.setAttribute('type', source.type)
link.setAttribute('href', source.href)
})
Just duplicate the last expression and replace light
with dark
to enable dark theme icon.
Note! We check wether source is
null
due to possible DOM mutations.
Conclusion
Now you know how to make a page favicon to react on theme switching.
Thanks for reading. Use favicon-switcher which covers more use-cases and supports other media-queries, like max-width
, min-width
, etc.
Top comments (0)