• mina86.com
  • Categories
  • Code
  • Contact
  • Having fun with SVG favicons

    Introduced last century, favicons help users identify tabs in a crowded browser. Originally limited to the ICO format, they can now leverage Scalable Vector Graphics (SVG). This vector format not only scales perfectly to different sizes but also unlocks dynamic features which this article explores, namely:

    Adapting to dark/light mode

    Thanks to the use of Cascading Style Sheets (CSS), an SVG image can detect whether the user prefers a light or dark colour scheme. Just as it’s done in HTML documents, SVG images can utilise the prefers-color-scheme media query to affect the appearance. For example, the following is an image containing a square whose colour depends on the user’s colour scheme choices:

    <svg xmlns="http://www.w3.org/2000/svg"
         viewBox="-10 -10 20 20">
      <style>
        path { fill: #c8dff8; }
        @media (prefers-color-scheme: dark) {
          path { fill: #092131 }
        }
      </style>
      <path d="M-9,-9H9V9H-9Z"/>
    </svg>
    Codeberg favicon as rendered in light and dark mode.

    Because colour scheme preference dictates the colour of the browser’s interface, detecting it may be used to decide between versions of the favicon which show well on a light or dark background. Without such adjustment, the image may be illegible.1 For a real-world example, Codeberg implements this (in contrast, GitHub uses separate favicons and uses JavaScript to point to one of them).

    Localising by language

    Another feature available in SVG is user language detection. Normally it’s done on client side via JavaScript,2 but scripting is not available in favicons so a different method is necessary. Instead, SVG switch element can be used as demonstrated below:

    <svg xmlns="http://www.w3.org/2000/svg"
         viewBox="-24 -24 48 48">
      <path d="M-24-16H24V16H-24Z"
            fill="#092131"/>
      <switch dominant-baseline="middle"
              text-anchor="middle"
              fill="#c8dff8">
        <text systemLanguage="pl">Cześć</text>
        <text>Hello</text>
      </switch>
    </svg>

    If the user’s system language is Polish, the image will display a Polish greeting ‘Cześć.’ Otherwise, it’ll default to English ‘Hello.’3 While favicons are too small to include any text, the feature may be used to alter the icon’s colours or shapes — either as an easter egg or to match region-specific branding.

    Adding animations

    The final feature I’d like to highlight is SVG’s support for animations. For example, the following code creates a rotating square (demonstrated on the right):

    <svg xmlns="http://www.w3.org/2000/svg"
         viewBox="-10 -10 20 20">
      <rect width="14" height="14"
            x="-7" y="-7">
        <animateTransform
          attributeName="transform"
          type="rotate" by="90"
          repeatCount="indefinite"
          dur="2s"/>
      </rect>
    </svg>

    Unfortunately, animated favicons aren’t supported by Chromium-based browsers. Furthermore, animations can be distracting so the effect should be used with care. However, I’m sure there are some creative ways to take advantage of the animation.

    See demonstration of all those features.

    Implementing an SVG favicon

    Having presented what’s possible, how does one use an SVG favicon? It’s no different from using any other format. What’s needed is a link element pointing to the SVG image. Critically, Chromium-based browsers require the image be served with the image/svg+xml content type.

    Secondly, SVG favicons are supported by only 88.5% of browsers thus it’s important to provide a fallback. This can be done by including a second link element, but some care needs to be taken. For example, specifying sizes=any for the SVG image and sizes=32x32 for the fallback image makes Chromium-based browsers ignore the preferred SVG entry. The code that works in Chrome is one where the SVG link element has no sizes attribute and the fallback specifies just one size, i.e.:

    <link rel=icon href=favicon.svg type=image/svg+xml>
    <link rel=icon href=favicon.ico type=image/x-icon sizes=32x32>

    This is the code used on the demonstration page, which you can see to test the effects and your browser’s support.

    Conclusion

    SVG favicons modernise a classic web feature, offering vector scaling often at file sizes smaller than corresponding raster images. That by itself is reason enough to use them. In addition, the ability to detect theme preference helps create favicons which naturally fit with browser’s interface regardless of the user’s customisation. Further creativity is unlocked with system language detection and animations.

    Yet, not all browsers handle SVG favicons; an ICO or PNG fallback is still necessary to guarantee compatibility. Furthermore, with myriad interfaces, other types of website icons have emerged (mobile phone icons, maskable icons etc). For a comprehensive guide describing how to add favicons to a website, I can safely recommend the How to Favicon guide.

    1 Other media queries are less useful for favicons. In particular, width and height queries are inconsistent between browsers and don’t always reflect the rendered dimensions of the icon. 

    2 There are a handful of locale-aware JavaScript methods — e.g., Date’s toLocaleString — plus Navigator object can be used to get the list of user’s accepted languages with the following code: navigator.languages || [navigator.language || navigator.userLanguage]

    3 As an aside, this is how I localised the graph in Stop Killing Games article. While content negotiation is used to serve different HTML code of the page, the graph is the same SVG file in both versions. Despite that, the image is translated thanks to the switch element.