A Gulp-Based External SVG Symbol Sprite Icon System

January 26, 2016

Listen to this blog post:

I made a boilerplate for the system described in this post:

P.S. Please ignore the awful title of the blog post. I couldn’t figure out what to call it.

Icons! We see them all over the web, and they’re essential to most pattern libraries and web design systems. I recently needed to implement such a system. It had to be non-prescriptive, scalable, and dynamically editable via CSS. The icons were to be used by multiple teams in many different applications, built with various frameworks and techniques. They needed to have the ability to be restyled, get cached, and be updated quickly and easily as more icons are added. Basically, the icon system needed to be really, really flexible. Challenge accepted.

Why SVG Icons?

There are basically three options we have when it comes to icon systems:

SVG icons are superior. I’m (not) sorry — they are. SVG is scalable, widely-supported, and allows for much more flexibility and dynamic editing. This post isn’t going to go in depth on the benefits of SVG icons over the other systems because so many others have.

Font icons and data URI’s in CSS content are a hack, while SVG icons represent visualizations in the format we expect without weird workarounds for breaking accessibility due to re-defining native DOM elements.

SVG Icon Options

Okay, great. We’ve decided on using SVG icons! But there more than a few ways to implement SVG into the DOM, each with their own pros and cons. I initially outlined the pros and cons of each, but it made this article way too long and out of scope. Some of the methods I considered include:

  1. Inline SVG

Ultimately I went with Method 4: the SVG Symbol Sprite because of the following benefits:

I also decided to use an “external” SVG sprite. I put external in quotes because the SVG must still be within the same site as it is used in to avoid security issues and cross-origin errors (the bane of my existence right now). But when it works, it means the SVG does not need to live in the head of the same document on which the icons are being used.

cute icons
This is what you get Googling "cute icons"

Note: if you’ve got a templating system and don’t mind the post-render bloat, there’s nothing wrong with inlining the SVG and it even saves you an HTTP request, but I wanted to make this as agnostic as possible. Also, with external sprites, there is a compatibility issue for IE, but we’ll get into a polyfill for that later.

Making it Dynamic

Unfortunately, we can’t simply reference an SVG element and be able to edits its paths (i.e. <img src="apple.svg" style="fill:green"> will not let you edit the color of the apple to make it green).

But the <symbol> method allows us to reference our SVG’s as though they are inlined on the page! The <symbol> tag separates out icons, and the <use> tag allows access to them. oOoOoh this is getting good.

So when we’re making our SVG sprite sheet, it doesn’t look like an SVG CSS sprite sheet (where you are taking individual SVGs and using <view> to determine what portion of the larger image we see:

icons

With <symbol>, the icons are more symbolic (pun totally intended). If you preview a symbol-based SVG, it will look blank, though all the icons exist as individual <symbols>’s and are ready for <use>-age.

Making it Systematic

Alright. So this seemed to be the best approach, now how can we build it to be scale-able? Let’s make this a system that is easy to use and update.

Generating the Sprite

The first step is to generate an SVG sprite. Luckily, Joschi Kuphal made an awesome tool called SVG Sprite which he also ported into Gulp with gulp-svg-sprite. I really like Gulp as a build tool because I think it’s the most lucid in terms of what tasks are running.

The SVG Sprite tool is seriously awesome. It provides options for all of the sprite options mentioned above in this post and even some additional niceties such as cache busting and building a reference page for you. The reference page is fantastic! These are the options I set inside of my gulpfile.js to generate the <symbol> sprite I needed:

// SVG Config
var config = {
  mode: {
    symbol: { // symbol mode to build the SVG
      render: {
        css: false, // CSS output option for icon sizing
        scss: false // SCSS output option for icon sizing
      },
      dest: 'sprite', // destination folder
      prefix: '.svg--%s', // BEM-style prefix if styles rendered
      sprite: 'sprite.svg', //generated sprite name
      example: true // Build a sample page, please!
    }
  }
};

You can also set an option to render a stylesheet that helps size icons, but I removed those (shown above as set to false) because I like to style the icon individually and let it’s default take the size of it’s parent. These styles also correlate with the prefix option above.

Adding Icons

Every individual SVG icon is added into a base folder (I just used svg/). Another awesome advantage of SVG Sprite is that it respects a folder system for naming icons. What this means is, if my base folder is svg/ anything within that folder can be referenced via its name, and anything within a folder gets prepended. Here’s what I mean:

svg/
|-- bubbles.svg //sprite.svg#bubbles
|-- cog.svg     //sprite.svg#cog
|
|__ glasses/
    |-- martini.svg //sprite.svg#glasses--martini
    |__ wine.svg    //sprite.svg#glasses--wine

With that naming structure in mind, you can now teach your designers how to add icons to that folder and submit pull requests! Yay for efficiency! When new icons are added, they don’t affect existing icons, but instead grow the system’s icon options.

Reference Page

The auto-generated reference page helps you keep track of icon options and their name references:

Usage

In HTML, you need just 2 elements to reference your icons with:

<svg>
  <use xlink:href="sprite.svg#glasses--wine"></use>
</svg>

You may then style this SVG in CSS. Make sure to give it a width and height (default is 100% of parent). If you turned on the stylesheet rendering options above, you can include or link to the generated stylesheet. It is best to edit these icons in CSS after adding a class to the <svg>. For example:

HTML:

<svg class="icon--wineglass">
  <use xlink:href="sprite.svg#glasses--wine"></use>
</svg>

Sass:

.icon--wineglass {
  fill: red; // additional styling

  &:hover {
    fill: white; // styling on hover
  }
}

Caveats

There are a few things to note if implementing a system like this (particularly about the external SVG). Due to cross-origin reference issues (CORS) the SVG must be located on the same domain as your website. Also, every browser except for IE 9-11 that support inline SVG support external SVG links, though you can polyfill this capability with SVG4Everybody or SVGxUse.

Keep in mind, for performance purposes, since icons are presentational, you can likely defer the loading of the polyfill scripts to not disrupt the load time of the rest of your assets.

Another caveat is that, while you can edit the entire SVG, you can’t edit specific paths and groups within SVG’s when using <use>. This is because the browser treats <use> like the shadow DOM. Chris Coyier wrote a few excellent posts on CSS Tricks for further reference.

TL;DR: Here’s a boilerplate for a pretty good SVG Icon System.

Shoutout to Ally Palanzi for proofreading and Sara Soueidan for pointing out you don’t need <defs>!