DEV Community

Cover image for Smart web design. Part I: light/dark mode favicon.
Paul Rumkin
Paul Rumkin

Posted on • Updated on

Smart web design. Part I: light/dark mode favicon.

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:

GitHub logo 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.

Credits

Photo by Linda Xu on Unsplash

Top comments (0)