Build Your Own SVG Icons
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.
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.
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:
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:
- SVG W3C Recommendation which provides further details and all parameters list
- Better SVG Sprites With Fragment Identifiers
- An Overview of SVG Sprite Creation Techniques by Sara Soueidan which covers many other topics about SVG sprite techniques.
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:
- Simurai SVG stacks
- CSS sprite
- 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.
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">
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).
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.
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.
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:
- MDN: SVG
- A Compendium of SVG Information
- Styling And Animating SVGs With CSS
- Structuring, Grouping, and Referencing in SVG — The <g>, <use>, <defs> and <symbol> Elements
- Understanding SVG Coordinate Systems and Transformations
- Adobe Illustrator Workflow For SVG Production
- SVG Export Settings For Adobe Illustrator