Setting up the SVG icon system

Purely decorative icons

Icons are purely decorative and the HTML should have either an aria-label (if the element has a native role) or a visually hidden class="vh" text description.

<svg class="c-icon" aria-hidden="true" focusable="false">
    <use xlink:href="/assets/icons/symbols.svg#icon--test"></use>
</svg>

SVG’s preferred over IconFonts

Iconfonts are outdated: they have poor accessibility, display with FOUC (Flash Of Unstyled Content), are fragile (users can easily block third party CSS fonts, including iconfonts), and from a developer point of view a pain to size and position within containers. Which is why we use SVGs.

Symbols.svg

If neccessary, we can have multiple symbol files if there are many icons, lazy loaded / dynamically.

We store all the icons inside a single master SVG file, inside which we categorise them under <defs> - so they are stored but not shown. Additionally the svg is aria-hidden="true" with style="position: absolute; width: 0; height: 0; overflow: hidden;" to hide its content.

<svg aria-hidden="true" style="position: absolute; width: 0; height: 0; overflow: hidden;" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<symbol id="icon--test" viewBox="0 0 32 32">
<path d="M25.313 4c1.438 0 2.688 1.25 2.688 2.688v18.625c0 1.438-1.25 2.688-2.688 2.688h-18.625c-1.438 0-2.688-1.25-2.688-2.688v-18.625c0-1.438 1.25-2.688 2.688-2.688h18.625zM25.313 6.688h-18.625v18.625h18.625v-18.625z"></path>
</symbol>
</defs>
</svg>

<symbol> has an id that should start with icon-- (this is referenced by the x-link href). This is not the component icon name (.c-icon {}). The component name should be applied to the use or svg element.

In the HTML, the use element can have a class that is used to perform limited CSS animations.

<use class="" xlink:href="/assets/icons/symbols.svg#icon--test"></use>

One symbol can even reference another symbol or part of a symbol. So you can have a generic SVG shape that is reused, styled, sized, rotated, colourised, filled by another symbol.

Viewbox

Each icon can have completely different viewbox sizes. But where there are common icons that will be shown together, use a common viewbox size. 32x32 is recommended.

Easy pre-loading

We can preload the symbols.svg file with a simple meta tag in the header. Beware that the path and url is case sensitive - it will Error404 the preload-file if it can’t find it and warn you in the console. If the url is different and correct in the content then it will delay the loading of the icons until it sees the first SVG element that links to it.

<link rel="preload" href="/assets/icons/symbols.svg" as="image" type="image/svg+xml">

We could also add all or part of the SVG symbols file inline in the HTML itself. Especially useful for high-priority icons.

I’d argue that fallback text (should) perform the same job in case the SVG file doesn’t load, and the symbol file should be small anyway (it’s all text and MIME-type should be cacheable and gzipped). If the symbols file is big, consider breaking it into multiple symbol files, eg, public and logged-in.

Versioned Caching

Add ?v=20190822 to the SVG url and link it to a common global caching variable string.

<use xlink:href="/assets/icons/symbols.svg?v=20190822#icon--test"></use>

Sass

We have a generic component for icons c-icon, which ensures all SVG’s have 1em x 1em dimension.

.c-icon {
    display: inline-block;
    width: 1em;
    height: 1em;
    zoom: 1;
    stroke-width: 0;
    stroke: currentColor;
    fill: currentColor;
}

It’s useful to put an inline style in master-layout, else the browser default size (300x150) will apply until your CSS file is loaded.

<style>
    svg {
        width: 1em;
        height: 1em;
    }
</style>