DEV Community

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

Posted on • Edited on

34 9

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
})
Enter fullscreen mode Exit fullscreen mode

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" />
Enter fullscreen mode Exit fullscreen mode

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)
})
Enter fullscreen mode Exit fullscreen mode

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

AWS Q Developer image

Your AI Code Assistant

Automate your code reviews. Catch bugs before your coworkers. Fix security issues in your code. Built to handle large projects, Amazon Q Developer works alongside you from idea to production code.

Get started free in your IDE

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

Instrument, monitor, fix: a hands-on debugging session

Join Lazar for a hands-on session where you’ll build it, break it, debug it, and fix it. You’ll set up Sentry, track errors, use Session Replay and Tracing, and leverage some good ol’ AI to find and fix issues fast.

Tune in to the full event

DEV is partnering to bring live events to the community. Join us or dismiss this billboard if you're not interested. ❤️