Inline SVG Icons

I’ve been using SVG for illustrations in my post for quite some time and wanted to convert all the little icons littered across the site from icon fonts to SVGs. I’ve managed to do this to my satisfaction and thought I should share my methods.1

Final icon effect

I want all the icons to be ‘inline’ i.e. just before some piece of descriptive text and of the same colour as the text. You can use icons in other ways but I’ll stick to this case.

I wrote a little note about a previous failed attempt2. Have a look at the updated demo which has the examples I’ll describe next. I use the Entypo icon set by Daniel Bruce throughout the site and will use it in these examples too.

Icon Fonts

The first time I implemented icons, I used what are called icon fonts — fonts that replace certain characters of the alphabet with pictures of icons. So all you have to do is, include the correct fonts and use the correct characters for each icon to show up. This is how the first example works.

Here is the HTML for the text:

<span class="icon-font">Icon</span>

and the CSS to put the icon before the text:

@font-face {
	font-family: 'Entypo';
	src: url('entypo-webfont.woff') format('woff');
}

.icon-font::before{
	font-family: "Entypo";
	content: "\2712";
	font-size: 1.5em;
}

A good thing about using icon fonts is that, since they are fonts, they automatically inherit the font colour correctly. A drawback is that your font absolutely has to load. If you mistype the font name (as I did in the second example) or the browser does not support your font type, you’ll end up with strange Unicode characters. Of course you could use some javascript to detect the browser support and so on… but ideally this should just fail gracefully.

Another little annoyance is that you have to know the Unicode character (the "\2712") for the particular icon you want. The Entypo site has the Unicode and HTML character codes for each of its icons.

SVG Sprites

The icon font way is clearly a ‘hack’, icons are not letters. Icons are pictures or images, so why not just use images? One trouble with using images directly is that you’d need to download multiple images, one for each icon on the site. Apart from all the data transmitted over the connection, it also has the overhead of your browser independently requesting each image. To avoid this we can put all our icons in one image — called a sprite — and then use the correct parts of the sprite for each icon.3

You can use your favourite vector graphics editor (I use Inkscape) to create your own SVG sprites, but creating good icons is hard. So I downloaded a SVG sprite version of Entypo from IcoMoon. For this demo I downloaded just the ‘paperplane’ and ‘feather’ icons.

Icomoon SVG sprite download

Leave all the default options intact while downloading. We’ll be getting rid of most of them, though make a note of the ‘Color’ and ‘Icon Height’ options.

Once you download the zip file the SVG sprite will be in /sprites/sprites.svg. You can directly use this sprite to create your icon with the following HTML and CSS as in the third example:

<span><svg class="icon" viewBox="0 0 32 32">
<use xlink:href="sprites.svg#icon-feather"></use>
</svg>Icon</span>
.icon {
	fill: currentColor;
	width: 0.8em;
	height: 0.8em;
}

The xlink:href="sprites.svg#icon-feather" attribute links to the icon that will be inserted before the text.

The main magic, I think, is in the fill: currentColor — it allows the SVG icons to inherit the color of the span and so the text and icon always have the same colour regardless of the ‘Color’ you chose while downloading the sprite.

Now that we have that setup, what actually happens if a browser fails to download the sprite file? I’ve tried to replicate that in the fourth example by trying to refer to a non-exising icon. You just get a blank space, no funny Unicode characters. Pretty neat I think!


One drawback of this code is that the viewBox attribute on the svg element must be specified and set to match the height and width of the icon. In the fifth example I skip the viewBox attribute and get only a part of the icon is visible. It would be hard to get this right everytime, and so the best way is to put the viewBox directly into the icon. Unfortunately IcoMoon does not do that yet, so we’ll have to go into the SVG file and add this ourselves. So open the SVG file in a text-editor (yes, a SVG image is actually a text document with instructions to draw the image!). Each icon will be a symbol4, for instance this is what Entypo’s ‘feather’ looks like:

<symbol  id="icon-feather">
	<title>feather</title>
	<path class="path1" d="M6.806 31.619c0.49-1.602 1.232-3.858 2.226-7.122 4.33-0.699 6.122 0.558 8.872-4.454-2.232 0.698-4.923-1.294-4.778-2.157 0.144-0.864 6.261 0.622 10.264-5.181-5.046 1.134-6.662-1.365-6.011-1.742 1.502-0.872 5.963-0.362 8.341-2.725 1.226-1.216 1.798-4.174 1.301-5.23-0.6-1.274-4.251-3.174-6.264-2.997-2.013 0.179-5.17 7.822-6.106 7.762s-1.123-3.422 0.51-6.549c-1.723 0.778-4.88 3.198-5.87 5.267-1.845 3.85 0.173 12.677-0.474 12.992-0.648 0.314-2.826-4.053-3.475-6.032-0.888 3.034-0.909 6.074 1.686 10.112-0.979 2.65-1.514 5.699-1.595 7.25-0.038 1.242 1.157 1.507 1.373 0.806z"></path>
</symbol>

Note the id attribute is what you use to point to the actual icon. Next to it just insert the viewBox attribute like so:

<symbol  id="icon-feather" viewBox="0 0 32 32">

Be sure to insert the correct width and height of your icon, most icons you get from IcoMoon will be square and so use the ‘Icon Height’ that you used while downloading the sprite. So now you can skip the viewBox when you actually insert the icon into your HTML as it is built into the icon itself! DRY at its finest!

So there you have it, inline icons which inherit the colour of the text. I’ve tested this in the latest versions of Firefox, Chrome and Opera. Let me know if this fails (not gracefully) somewhere. Alternatively, you could use Jonathan Neal’s SVG for Everybody — a Javascript polyfill to add SVG sprite support to older non-supporting browsers.

Send me (Learn more)

Replies

Mentions

Kartik Prabhu on kartikprabhu.com Jayson Harshbarger on Twitter

Reposts

Jonathan Neal on Twitter Philippe Brouard on Twitter