Implementing a modular, automated, icon system, using SVG sprites
Most web UI’s use some form of icon system. Often, managing these systems can be time consuming and frustrating. An icon-font approach can result in a decent, vector solution, but they can also be slightly inconsistent and are challenging to manage.
Here at World Nomads Group we’ve implemented an approach that achieves a similar result to an icon-font and lacks many of the downsides. There are numerous, excellent, tutorials on how to implement SVG sprites with build tasks. Here we will take those learnings a step further by combining sprite build tasks with modular CSS practices. This will let us easily manage and style our icons, even and perhaps especially on large-scale web applications.
You can also skip the tutorial and look at the example project here.
Step 1: Using a task runner to create our sprite
Step 1 and 2 of this tutorial will assume that you have a basic understanding of task runners such as Grunt or Gulp. If not, you may want to up-skill on those tools first.
This project will be using Gulp, though Grunt can be used for very similar results. You will need:
- A set of SVG icons.
- A project with Gulp, using a CSS pre-processor task for bundling CSS (SASS or LESS). Here’s an example project setup for SASS.
First, we need a set of icons that have been prepared for purpose. I won’t go into too much detail around working with SVG markup, but we will need to ensure a few things:
- Our icons need to be proportionally scaled to each other and within a matching SVG
viewbox
, i.e. 100 wide * 100 high. The numbers shouldn’t matter, although square is easier. Each icon does need to be the same though. This is so that we can scale and manipulate them consistently with CSS. - Tidy the markup of the SVG’s. SVG’s can be bloated with all sorts of unnecessary junk. Flattening, minimising paths / strokes, stripping out unnecessary tags, etc, will help prevent the sprite from ballooning in size. This can be automated via tasks such as SVGGO, otherwise it can be done manually with a little practice.
- Within our SVG markup we need to add currentColor to a
fill=""
attribute on all paths where we want to control the color via CSS. This will allow the icons to inherit thecolor
property of a parent element, just as you would with an icon font.
We then need to configure a task to compile the sprite. For this we will use svg-sprite. This task supports a number of sprite implementation “modes”, but in this instance we will focus on the “symbol” mode. svg-sprite will take a folder of individual icons (as SVG files), and create a single SVG with each icon in a seperate <symbol>
layer. We can then reference those layers by an id=""
on a <use> element, within the DOM. Example Gulp config for this:
Step 2: Automating sprite integration with builds
In order for the SVG layers to be referenced, we need the SVG XML to be injected into our DOM. A better solution might be to reference the sprite “externally” as a static asset, which would allow for better caching, but that introduces further issues with IE as well as the need to reference the full path to the sprite in the markup.
There are numerous approaches to inline sprites into the DOM but in this instance we will use gulp-inject to take our compiled SVG sprite and inject it as XML within our template. It will take our SVG as a source, then inject that into tag’s in our page:
With that we need the starttag
in our template for gulp-inject to insert the XML into:
Now, displaying an icon within the same template becomes simple with the following markup:
Now, if we want to add a new icon or change an existing icon in the sprite, we simply run the “inject” task again. At this point we can also take the “inject” task and incorporate it with the rest of our build pipeline. For example, adding it to watch and browsersync tasking for local development.
Step 3: Modularising icon styles
This step uses both modular CSS, very similar to BEM or SMACSS, as well as SCSS. Again, you may like to familiarise yourself with those first.
Typically, for brands that have well defined style-guides, icons have various restrictions on how they can be used. For instance, we might have a fixed range of icon sizes or several colour options. This restriction helps keep the UI consistent and on brand. Modular CSS helps us enforce those restrictions. Additionally, we can change icon styles globally via the CSS.
From here we just need to apply theIcon
module class, along with any sub-module classes, to the <svg>
element of each icon:
Thanks to currentColor
our icons will inherit the color
from a parent. By using 1em for the width and height, they will also inherit the font-size
from a parent. We would want to change those ratios if the SVG’s viewbox
is not square. This is useful if we wish to use the icons within text, or to inherit the global styles for text.
Our sub-modules then become trivial to apply as needed, as we’re simply changing font-colors or sizes:
We can go one step further and create additional sub-modules with further aesthetic enhancements, such as the following Icon--circle
sub-module:
The added power of this modular approach to CSS comes from the ability to mix-and match sub-module classes. Thus we can create style combinations such as:
Downsides and other considerations
This approach is not perfect. You may want to consider the following:
- Inlining the XML in the DOM isn’t ideal from a performance perspective. This is necessary at this stage thanks to IE, as per usual. It’s best to only include the icons that are actually used, to prevent bloat. Aim to keep the sprite as small as possible.
- This approach won’t allow you to reference the icons via the
content
of pseudo elements. I do have a solution to this which uses the CSS sprite “mode,” but this adds quite a lot of complexity to the implementation (too much for this tutorial). - Some might say that referencing each icon with two HTML elements is not pretty. I agree but it’s an acceptable compromise until something better presents itself.
- SVG 2.0 deprecates xlink in favour of simply using href.
Conclusion
We’ve come a long way from applying icons as individual images. It’s fair to say that there is still plenty of scope for icon solutions to improve. That being said, the above solution is tried and tested within contemporary commercial settings. Once it’s set up, it’s a breeze to maintain and extend. I hope you’ve found some value here.
Once again, you can download a full implementation of the project here.