Building an adaptive favicon

A foundational overview of how to build an adaptive favicon.

In this post I want to share thinking on how to build an adaptive favicon with SVG. Try the demo.

Browser tabs shown adapting to MacOS system light and dark theme changes. Try the Demo

If you prefer video, here's a YouTube version of this post:

Overview

A custom favicon is a great way to polish a web project. It displays on desktop browser tabs, and also inside "save for later" readers, other blog posts linking to your site, and more. Traditionally this has been done with the .ico file type, but recently browsers have allowed use of SVG, a vector format. Using progressive enhancement you can serve well supported .ico favicons, and upgrade to an .svg if available.

SVG is able to scale up and down without quality loss, and can potentially be very small in size, they can also have embedded CSS, even embedded media queries. This means if an SVG favicon is used in a reader app or bookmarks bars, there's a chance the user could get a theme relevant (light or dark) icon due to dark preference styles provided inside the SVG. The SVG then adapts using its embedded styling for light and dark user preferences.

Large and easy to distinguish light and dark favicon examples.

Light and dark tabs in each browser providing an 
    overview of the adaptive icon from top to bottom: 
    Safari, Firefox, Chrome.
Light and dark tabs in each browser providing an overview of the adaptive icon from top to bottom: Safari, Firefox, Chrome.

Markup

SVG markup is XML using an .svg file type extension which allows it to hold more dynamic types of code.

Start by making favicon.svg

Make a new file called favicon.svg and add the following:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 400">

</svg>

Here's my SVG file, I've sized the viewBox relevant to my artwork:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 447 428">

</svg>

Adding shapes and paths

Next, add SVG path code. Often this means opening the SVG in a code editor, but that code is generally not human friendly. Here's a great guide that guides you through exporting and optimizing SVG from design tools.

The artwork for this GUI Challenge came from a designer who made it in Adobe Illustrator. I heavily optimized it. I ran it through SVGOMG and then hand-edited the cruft away.

Here's an example of the skull artwork path group from mine, after cleaning it:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 447 428">
  <g id="skull">
    <path id="skull-outline" class="favicon-stroke" stroke-linejoin="round" d="M19.62 188.39A166.62 166.62 0 0 1 186.24 21.77c115.25 0 166.61 74.59 166.61 166.62 0 1.83-.08 3.64-.13 5.46h.13s.68 175.09.68 178.65c0 30.11-16.26 41.67-36.32 41.67-12.7 0-35.22-3.93-36.22-32.69h-.2c-1 28.76-16.81 32.69-36.22 32.69-18 0-32.87-6.78-35.77-32.53-2.9 25.75-17.8 32.53-35.8 32.53-20.06 0-36.32-11.56-36.32-41.67 0-2.48.36-24.88.36-24.88A166.68 166.68 0 0 1 19.62 188.39Z" />
    <path id="eyes-and-nose" d="M180.77 205.76c0 23.64 12.84 42.81 28.68 42.81s28.68-19.17 28.68-42.81-12.84-42.82-28.68-42.82-28.68 19.17-28.68 42.82M275 205.76c0 23.64 12.84 42.81 28.68 42.81s28.68-19.17 28.68-42.81-12.84-42.82-28.68-42.82S275 182.11 275 205.76M264.51 276.85s-29.26 43.53-20.12 49.23c7.07 4.41 20.49-16.71 20.49-16.71s12.82 22.58 16.76 20c16.24-10.71-17.13-52.5-17.13-52.5"/>
    <path id="jawline" class="favicon-stroke" fill="none" stroke-linecap="round" d="M114.92 284.33c22.54-1 22 7 22 62.48" />
  </g>
</svg>

Notice the human readable ID selectors like #eyes-and-nose and classes like .favicon-stroke. These are from my hand editing, in preparation for CSS. Adding classes and ID's is not required for your SVG to be an adaptive favicon.

In the <head> tag of your HTML, after the .ico favicon, add the following:

<link rel="icon" href="/favicon.ico" sizes="any">
<link rel="icon" href="/favicon.svg" type="image/svg+xml">

As the new icon may look identical to the .ico version verify that it is being used. Open the Network panel of DevTools. Filter by images and search for favicon:

Screenshot of the Network pane from DevTools with a filter searched for
favicon and the favicon.svg resource highlighted.

Styles

Like HTML, you can add a <style> tag to the markup for use against that document scope:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 447 428">
  <style>
    
  </style>
  …
</svg>

The light theme version will be the default coloring of my favicon SVG. The styles I wrote for that were mostly stroke and fill colors:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 447 428">
  <style>
    .favicon-stroke {
      stroke-width: 8px;
      stroke: #8929ff;
    }
    #skull-outline { fill: white }
    #eyes-and-nose, #hat-outline { fill: #8929ff }
    #hat-fill, #hat-bill { fill: #e662e6 }
  </style>
  …
</svg>

Example light theme favicon preview.

Next, the most fun part, styling the dark theme version of your favicon. The styles for that will go into a media query within the style tag:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 447 428">
  <style>
    …
    @media (prefers-color-scheme: dark) {
      /* dark theme styles */
    }
  </style>
  …
</svg>

Screenshot of DevTools showing the dark theme media query overwriting the
fill color of the eyes and nose of the SVG.

Mine ended up like this:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 447 428">
  <style>
    …
    @media (prefers-color-scheme: dark) {
      .favicon-stroke { stroke: #343a40 }
      #skull-outline { fill: #adb5bd }
      #hat-outline { fill: #343a40 }
      #eyes-and-nose { fill: #343a40 }
    }
  </style>
  …
</svg>

Example dark theme favicon preview.

I chose to swap the bright purple borders for a nice cool dark gray (#343a40), changed the skull bone color from white to a light cool gray (#adb5bd), but left the pink accent hat.

Preview of both light and dark favicons side by side.

Conclusion

Now that you know how I did it, how would you‽ 🙂

Let's diversify our approaches and learn all the ways to build on the web. Create a demo, tweet me links, and I'll add it to the community remixes section below!

Community remixes