Design & UX
Article

Build Your Own SVG Icons

By Massimo Cassandro

Web fonts are an easy way to integrate a flexible icon system into a web project without sacrificing compatibility with legacy browsers. The technical process of building web fonts has also become much simpler since the arrival of online tools like Icomoon or Fontello.

But HTML5 has brought us an even more versatile way to use icons on web pages: SVG – Scalable Vector Graphics.

SVG basic features are now widely supported by almost all browsers (take a look at caniuse.com), so there are now far fewer reasons not to use it.

And if you need to give support to Explorer 8, there are some very easy fallbacks you can apply.

Drawing icons

This step is exactly the same you can read in my article about web fonts, take a look at it for more detail on preparing your icons.

A collection of SVG files

When your SVG files are ready, you'll need a method to include them in your project.

Let's look at the options we have available.

Using the <img> tag

This is a very basic solution, and although it isn't really suited for our scope, it brings us some interesting considerations.

It requires only the simple img tag you've probably used with a thousand JPGs, GIFs and PNGs.

Loading an SVG with the HTML IMG tag

This is a very easy way to use SVG files; you can control the image size as required using width and/or height attributes, but you can't control color.

This technique is fully compatible with all modern browsers except IE8 (and older IEs) and early model Android phones. In some cases both width and height are required for a correct display.

IE8 fallbacks

There are many ways to provide legacy browser compatibility, if you need it. In each case, you'll need to provide PNG files to replace your SVGs.

Personally, I prefer a gracefully degrading technique that requires that all files use the same names and share the same path. It also requires Modernizr.

A few lines of javascript will do the trick:

if(!Modernizr.svg) {
	var imgs = document.getElementsByTagName('img');
	for(var i = 0; i < imgs.length; i++) {
		var  this_src = imgs[i].src;	
		if(this_src.match(/(\.svg)$/)) {
			imgs[i].src = this_src.replace(/(\.svg)$/, '.png');
		}
	}
}

This technique – and some variations – are covered in this article: SVG Fallback with PNG Images.

Another very clever solution has been described by Alexey Ten, and also addressed by Chris Coyier in his SVG Fallbacks article.

You can also load an SVG image using the object or iframe tags. Each provides a markup-only way to load fallback images:

<object data="svg_image.svg" type="image/svg+xml">
	<!-- the following image is displayed if browser can't load svg image -->
	<img src="fallback.png" alt="fallback image">
</object>

Take a look at my embedding SVG codepen for an overview of all current embedding methods.

Enhancing img tag with SVG linking

The basic usage of img for SVG files can be enhanced using fragment identifiers.

Fragment identifiers are a built-in SVG property: we can reference a SVG portion using a SVG view specification or addressing an SVG element by its <view> ID.

The first method consists in specifying a viewBox when loading a SVG file.

<img src="svg_file.svg#svgView(viewBox(x,y,w,h))">

Where x,y,w,h represent fragment coordinates from file origins (x and y) and fragment width and height (w and h).

For example, to show only the black horse from this chess pieces sprite (from Wikipedia Commons) you have to detect values just like in the scheme below:

Viewbox coords for Black horse

It might remind you of the 'CSS image sprite' techniques that some sites use for their UI elements.

You can see the result here:

See the Pen svg img + fragment identifier by Massimo Cassandro (@massimo-cassandro) on CodePen.

The second way requires that the SVG contains a specific <view> of our fragment.

If our Chess Pieces file contained something like this:

<view id="black-horse" viewBox="140 52 34 33" />

We could obtain the same result using:

<img src="Chess_Pieces_Sprite.svg#black-horse">

Both methods are compatible with all major browsers (including IE9 +), except IOS devices that don't correctly display the “view” method fragments. Happily IOS is fine with the first method.

All browsers require that the image is explicitly sized (with width and height attributes or CSS rules) to be displayed correctly.

For more info on SVG linking take a look at:

More sprites

Until now, we've seen nothing that makes SVG an obvious replacement for web fonts – but we can do much more with SVG. We can arrange our SVG icon files to construct a much more flexible and powerful tool.

There are three main ways (that I know) to do this:

  1. Simurai SVG stacks
  2. CSS sprite
  3. Using SVG <defs> and <symbol>

Simurai SVG stacks

This is the first method that I became aware of, and I think it's fair to say that it's relatively outdated today. Nevertheless, I do want to mention it, if only for the record.

This technique was first described by Simurai at his blog in 2012, though I read of it at SitePoint too.

As you can see in the screenshot below, this technique requires that all your symbols stack on top of each other – not unlike Photoshop layers. Each of them has the same class (icon) and a unique ID.

SVG stacks in Brackets

The SVG contains the following CSS.

<defs>
    <style>
        .icon { display: none; }
        .icon:target { display: inline; }
    </style>
</defs>

All icons will disappear until they're invoked using the fragment identifier syntax:

<img src="icon_file.svg#spriteID">

SVG stack file

Using the img tag, this technique works well with Firefox, Chrome, Safari (desktop) and Opera. However it doesn't work with any IE browsers or IOS devices.

You can use SVG stacks with background images too (adding some CSS to your page) but this unfortunately only works with Firefox.

CSS sprite

This technique has been refined by Filament Group and consists in a web application (or a Grunt plugin if you prefer).

Grumpicon web app

As you can see at the Grumpicon page, “the tool processes a set of SVG files, generates PNG fallback images for legacy browsers, and exports a demo page showing how to use the final icons”.

I've created a demo on Codepen (removing the fallback script), and this is the result:

See the Pen Grumpicon demo by Massimo Cassandro (@massimo-cassandro) on CodePen.

This is a production quality, ready-to-use technique. I've tested it in all major browsers (including IE8) and it works flawlessly in all them.

We can also use CSS sprites with an external SVG file, as we'll see below.

Using SVG <defs> and <symbol>

The last method is the one I personally prefer, and it's well explained in a CSS-Tricks article.

It uses the svg tag as a wrapper in which every icon is described into a <defs> or <symbol> tag.

<svg>
	<defs>
		<g id="icon1">
			<!-- icon shape here -->  
		</g>
	</defs>
	<symbol id="icon2" viewBox="0 0 100 100">
		<!-- icon shape here -->  
	</symbol>
</svg>

Icons are displayed by the <use> tag:

<svg viewBox="0 0 100 100">
	<use xlink:href="#icon1" />
</svg>
<svg>
	<use xlink:href="#icon2" />
</svg>

You can see that, since the symbol tag has its own viewBox attribute, you don't need to set it every time the icon is displayed, which makes this method very easy to use.

The collection of symbol tags can be built manually, using some Grunt plugin (google for Grunt SVG merge) or, once again, with Icomoon.

Icomoon app

After your icons have been uploaded to the Icomoon app, choose the glyphs you need and, instead of generating a font, click the Generate SVG/PNG button.

You can also set some preferences to generate PNG fallback files too and even an old-style CSS sprite (both for PNG and SVG files), just like in the CSS sprite chapter. Note that the sprite files generated by the Icomoon app, make the creation of an almost pure-CSS fallback very easy.

Icomoon preferences

The download button will then deliver a collection of files, including a ready-to-use SVG file with perfect <symbol> definitions built into it.

Here's the result:

See the Pen SVG symbols demo by Massimo Cassandro (@massimo-cassandro) on CodePen.

This method works fine in all major browser including IE9+, but, since the combined SVG is embedded in the page, there's no way of caching it in the browser.

Luckily, we can reference the SVG symbols from an external file. In this case the syntax becomes:

<svg>
	<use xlink:href="external_file.svg#iconID" />
</svg>

This method works in all major browsers, except for IE9+. However Jonathan Neal's SVG4everybody script can even solve this problem, as is clearly explained in SVG use with External Source.

Resources

I think there are lots of exciting possibilities around these ideas. If you're interested in learning more, here are some resources I've found very useful:

More:
Comments
KeeganReed

They look really nice, thanks for this, you never know, maybe I'll need it some day smile

omnicity

Would you mind giving an explanation of what the "CSS sprite" technique actually does? Hard to evaluate it's efficacy if all you do is say "upload, hit the button, download" ...

RyanReese

CSS sprites is when you combine multiple images into one file, and then, by doing that, you only have that one HTTP request for that image file (instead of 50 or however many images yo ucombined)

Then to reference a particular image in that combined image file, you mess with background-position to only show the part you want.

alexmwalker

The graphic below is the image sprite that Youtube has used.

As @RyanReese said, it means the user only needs to download a single graphic on first page load. Often it's not the downloading that slows a page load, but the server-browser negotiation over delivering hundreds of tiny files. Sprites get around that bottleneck.

Capsule

Sprites will hopefully disappear when HTTP/2 is widely adopted.
In the meantime, SVG sprites are actually a good alternative because you don't need to use the background-position property to determine which sprite you want, thus being able to use that property again as intended.

omnicity

So if you meant "Image Sprite" why did you give it a new name?

No wonder I was puzzled, I was trying to work out what was different between "Image Sprite" and "CSS Sprite" and there isn't one!

RyanReese

It's not a new name. More people call it CSS sprites (that I've seen) than image sprites.

Recommended
Sponsors
Because We Like You
Free Ebooks!

Grab SitePoint's top 10 web dev and design ebooks, completely free!

Get the latest in Design, once a week, for free.