SVG Sprites 2016

Back in 2012 I've been experimenting with scalable SVG sprites but was unable to get a reliable, cross-browser solution. Four years later, thanks to browser support for inline SVGs, it's much more feasible to use SVG sprites. In this article I describe a technique of building and embedding SVG sprites that can be individually scaled and coloured from within css. Furthermore I present an argument for obsoleting spritesheets in near future. Finally I showcase few neat tricks possible with the inline SVGs.

Symbols-based SVG sprite

HTML5 spec allows for a SVG file to be embedded directly inline the html. This feature can be used to embed entire SVGs but that would make these images non-cachable. Instead we can embed a minimal svg file that will only contain an use tag to reference a symbol in an external SVG file, i.e. the spritesheet. The spritesheet should contain all icons individually viewboxed and wrapped within a symbol tag with an id so it can be referenced. A quick example below uses this spritesheet (see source, it contains paths with no color):

King - a chess piece Queen - a chess piece

And the following code:

<svg style="width:120px; fill: #ffbdbd" aria-labelledby="title" role="img">
	<title id="title">King - a chess piece</title>
	<use xlink:href="/gfx/2016-10-02-chess-spritesheet.svg#king"></use>
</svg>

<svg style="width:120px; fill: #c9c9ff" aria-labelledby="title" role="img">
	<title id="title">Queen - a chess piece</title>
	<use xlink:href="/gfx/2016-10-02-chess-spritesheet.svg#queen"></use>
</svg>

This will work in all the modern browsers except for the IE and older versions of Edge (i.e. before version 25, EdgeHTML 13).

I've prepared the above spritesheet by hand (using icons from game-icons.net), however this step can be automated with tools like gulp-svg-icons or similar.

Compared to other methods

Two common methods for including icons on the web are png spritesheets and symbol fonts. SVG spritesheets have many advantages over these methods but are by no means a golden-bullet solution, here's an overview comparison:

PNG SpritesSymbol FontsSVG Sprites
Scalable
Multi-colour
Use in CSS
Colour from CSSEntire icon onlySelected areas
File sizeLargeSmallSmall
  • Scalable - whether icons can be scaled without quality loss, true for vectors. For bitmaps the solution is to use screen-specific sprites/spritesheets but that adds complexity and file size/download time.
  • Multi-colour - whether a single icon can have multiple colours
  • Use in CSS - whether it's possible to display an icon using only CSS with no dedicated HTML code. Useful for decorative list items, marking external links etc.
  • Colour from CSS - whether it's possible colour icons individually from within CSS
  • File size - differs from case to case, PNG spritesheets are usually heavier because multiple sizes of the same icon need to be included.

Are spritesheets still required?

Main purpose behind spritesheets is to reduce the amount of HTTP requests the browser needs to do in order to fetch icons, reducing the communication overhead and thus speeding up page load time. However nowadays, with HTTP 2, where multiple files can be requested and sent over the same connection, spritesheets will offer minimal (if any) speed up. HTTP 2 support is decent, however you might still need a backup if you care for old Androids or that old Windows old IE combo.

With HTTP 2 it's possible to deliver individual svg files without any significant performance loss. These files can be prepared so that it's possible to include them via img tag, as an inline SVG (with all the benefits of it) or via CSS as presented in the example below from left to right. Consider the following example:

Knight - a chess piece

And code used to render above section:

<style>
	.example-knight {
		color: #ff69b4;
	}
	.example-span-svg-knight {
		display: inline-block;
		width: 120px;
		height: 150px;
	}

	.example-span-svg-knight:after {
		display: inline-block;
		content:'';
		background-image: url('/gfx/2016-10-02-chess-knight.svg');
		width: 100%;
		height: 100%;
	}
</style>
<img src="/gfx/2016-10-02-chess-knight.svg" style="width: 120px;" />
<svg class="example-knight" style="width:120px;" aria-labelledby="title" role="img">
	<title id="title">Knight - a chess piece</title>
	<use xlink:href="/gfx/2016-10-02-chess-knight.svg#knight" viewBox="0 0 512 512"></use>
</svg>
<span class="example-span-svg-knight"></span>

Summary

With except for some old MS browsers, cross-browser SVG spritesheets are now possible, however using sprites has no benefits on HTTP 2 websites. That being said inline SVGs are cool because of additional features such as ability to apply colours to individual instances, ability to animate parts of the icon etc.

Bonus 1

It's possible to define two fill (or stroke) colours within an svg that are controlled via CSS using currentColor CSS value within the SVG file. Consider the following example (also see source svg).

Bishop - a chess piece

And code used to render above section:

<style>
	.example-bishop {
		color: red;
		fill: blue;
		stroke-width: 8px;
		stroke: yellow;
	}
</style>
<svg class="example-bishop" style="width:120px;" aria-labelledby="title" role="img">
	<title id="title">Bishop - a chess piece</title>
	<use xlink:href="/gfx/2016-10-02-chess-bishop.svg#bishop" viewBox="0 0 512 512"></use>
</svg>

Bonus 2

It's possible to use multiple use tags within a single inline svg and target each one with separate fill color etc. What is even cooler, parts of the SVG can be animated using css! The animation part works well in all modern browsers except for Edge.

World

And code used to render above section:

<style>
	@keyframes spin { 100% { transform:rotate(360deg); } }
	.example-world-planet {
		fill: #77dd77;
		stroke-width: 5px;
		stroke: #333;
	}

	.example-world-orbit {
		fill: #aec6cf;
		animation:spin 4s linear infinite;
		transform-origin: 60px 75px;
		stroke-width: 5px;
		stroke: #333;
	}
</style>
<svg class="example-world" style="width:120px;" aria-labelledby="title" role="img" >
	<title id="title">World</title>
	<use class="example-world-planet" xlink:href="/gfx/2016-10-02-world.svg#planet" viewBox="0 0 512 512" />
	<use class="example-world-orbit" xlink:href="/gfx/2016-10-02-world.svg#orbit" viewBox="0 0 512 512" />
</svg>