Smart web design. Part I: light/dark mode favicon.
Tags: javascript, web, html, css
Published: 2019-09-14 10:53:02

---
title: Smart web design. Part I: light/dark mode favicon.
published: true
description: How to switch favicon according to current UI color scheme
tags: javascript, web, html, css
cover_image: https://thepracticaldev.s3.amazonaws.com/i/r0ptfbdyr1god5ohb5po.jpg
series: Smart web design
---

**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](https://rumkin.github.io/favicon-switcher/) of how it could work.

> **TL;DR** For those who want ready solution, use the library:
> {% github rumkin/favicon-switcher no-readme %}


## 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](https://developer.mozilla.org/en-US/docs/Web/API/MediaQueryList), which allows listen changes and we will use it:

```js
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:

```html
  <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.

```js
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](https://github.com/rumkin/favicon-switcher) which covers more use-cases and supports other media-queries, like `max-width`, `min-width`, etc. 


## Credits

Photo by [Linda Xu](https://unsplash.com/@rhindaxu?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) on [Unsplash](https://unsplash.com/?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)