SVG icon symbols or image masks?

May 8, 2022

I've wrote a blog post how to create an SVG symbol sprite and how to use it in a django project. I implemented the SVG sprite inline at the bottom of the html document. This was due to the fact that at that time we still needed to support IE11. Which doesn't support xlink with external reference.

Since we are (I think) past the need of support of IE11, you could start using an external SVG sprite and use xlink with external reference to load SVG symbol icon.

The nice thing about SVG symbols is that you can manipulate its dimensions and use 2 colors in your SVG.

The fill color and the currentColor:

<symbol id="chevron-left">
  <path d="M33.8 2h444.3c17.6 0 31.8 14.3 31.8 31.8v444.3c0 17.6-14.3 31.8-31.8 31.8H33.8C16.2 509.9 2 495.6 2 478.1V33.8C1.9 16.2 16.2 2 33.8 2z" />
  <path fill="currentColor" d="M349.3 353.5l-94.1-94.1 94.1-94.1-47.1-47-141.1 141.1 141.1 141.1z"/>
</symbol>
    
.chevron-left {
    width: 32px;
    height: 32px;
    fill: var(--fill-svg);
    color: var(--c-svg); /* currentColor */
    transition: fill 0.15s;
}

Disadvantages of using SVG Symbols

Performance issue

There is one thing you need to be aware of when using SVG symbols in general. Symbols are not cached. And every call to a SVG symbol is a request. If you have a table overview with a lot of repeating SVG icons, it can really affect the loading of your page.

Firefox CSP error

SVG symbols using external reference can cause a Firefox CSP error, see issue on bugzilla. SVGs loaded via xlink:href are not considered as images and are blocked if default-src directive is set to 'none' in your CSP. To bypass this issue you can set default-src to 'self'. Which means that all directives will implicitly be 'self'. For example, if you have default-src 'self' applied in your CSP, but not specified the font-src directive, the font-src directives' policy will also be 'self'.

Image mask using SVG icons

With CSS mask-image property you can also use SVG icons in your project and manipulate its color via CSS, using the background-color property. Therefore you can only use one-color icons. The advantage is that it has no performance issues, because the CSS is cached.

A way to setup a CSS structure to easily use mask-images you could create a mixin for the mask-image when you use SASS:

@mixin mask($img, $w:1.8rem, $h: auto) {
    -webkit-mask-image: $img;
    -webkit-mask-size: $w $h;
    mask-image: $img;
    mask-size: $w $h;
}

In your CSS you can define some icons and setup a default icon layout:

:root {
    --size-icon: 3.2rem;
    --c-icon: #2d2d2d;
}

.icon-chevron-left {
    @include mask($img: url(icon-chevron-left.svg), $w: var(--size-icon));
}
.icon-chevron-right {
    @include mask($img: url(icon-chevron-right.svg), $w: var(--size-icon));
}

[class^="icon-"] {
    display: block;
    width: var(--siz-icon);
    height: var(--size-icon);
    background-color: var(--c-icon);
}
I use the display:block property on the icon class, because most of the time I use the <span>-tag to apply the icon.

I use CSS custom properties for the size + color to easily change them when desired:

.toc .icon-chevron-left {
    --c-icon: blue;
    --size-icon: 2.2rem;
}

Wrapping up

You can use 4 svg icon techniques simultaneously in your project:

I use the mask-image approach in combination with svg symbols in several projects and I find it very easy to use and to manage. I hope this blog post has some value for other front end developers.