Usage of SVG for web icons continues to grow given more widespread browser support and the advantages over other approaches: font libraries require an additional external resource full of icons you’ll never use while raster images don’t scale to different resolutions and have larger file sizes than SVG. There are several approaches to implementing an SVG icon system, each with its own benefits and drawbacks.
This post demonstrates a pure CSS/Sass approach that works in IE10 and greater and other major browsers and allows for reusable SVG icons that can be styled for color and size:
For the sake of brevity the examples use a simple circle element but the approach works with any sort of SVG content; most icons will likely be path elements. See the end of this post for code examples to create the icons displayed above.
The goal is to end up with compiled CSS that looks something like the following:
Note that the “, #, <, and > characters are encoded as required by Firefox and IE (don’t worry, we do the encoding with functions). Applying the icon-circle class to an HTML element (e.g., <div class=”icon-circle”></div>) yields a 32×32 orange circle:
This tutorial focuses on how to generate the background-image property value using Sass functions. The raw Sass file that outputs the above CSS will be as follows:
Defining utility functions
From the above we know that we need to define a function get-icon-circle that takes a fill color as its single argument and returns a background-image property value. Typically I’ll create a file _icons.scss that includes all icon-related functions though the following can obviously be included anywhere in your project to suit your needs:
Line 2$encodings is a map of characters to their encoded values.
Line 10 The str-replace function replaces all occurrences of $search in $string with $replace. This function was developed by Hugo Giraudel; the original is available here.
Line 19 The get-icon function returns a background-image property value. It accepts two arguments: $content, any SVG content string and $viewBox, the value for the SVG’s viewBox attribute. The function wraps the provided content in <svg> tags, encodes the result and packages it up into a valid URL.
Defining icon functions
With the utility functions defined we can now define the get-icon-circle function that we used above:
The get-icon-circle function takes a $fill color that’s inserted into the icon’s SVG definition and returns a URL for use as a background-image property value. From here we can follow the same template to define functions to generate other icons. By way of example we’ll define the functions used to generate the @, cog, and anchor icons shown at the beginning of the post:
Using the icon functions
Again assuming that we’ve defined our icon functions in a file _icons.scss we can use them in other .scss files as follows:
Applying these classes to HTML elements yields the following:
This approach can also be used to create circular and square icons as seen in the examples at top by rendering the icons in white (or any color really) and then applying a different background color:
Applying these classes to HTML elements results in the following:
So far we’ve only specified the fill attribute for the icons, but you can provide additional arguments in your get-icon-x functions to style other attributes such as stroke or stroke-width. Here’s an example of a simple square icon with optional stroke and stroke-width attribute values:
Similarly, if you have icons composed of multiple SVG elements you can provide arguments to style each, yielding more complex multi-colored icons.
Limitations of the approach
One potential limitation of the approach is the inability to easily adjust the SVG attributes, e.g., on hover. Instead you have to redefine the entire SVG which could bloat the size of your CSS files if used excessively:
When you hover over the cog the SVG background is swapped out like an old-school image swap. There also doesn’t appear to be a way to apply transitions. This is not a problem with the circular and square icons described above as you can simply change the background-color property value.
CSS file sizes
This could be an issue in some applications, though in general can probably be mitigated. If you’re using SVG icons the actual SVG has to make it to the browser one way or the other, may as well be in a CSS file that will likely have a longer caching policy than HTML. Just keep in mind that every time you invoke one of your icon functions in a Sass file you’re injecting the full SVG into the compiled CSS file. You should be able to structure your code so that an icon function is only called once on any given page. For example:
In the first example “DON’T do this” the SVG is needlessly injected into your CSS files twice. This is avoidable since we’re just changing CSS properties between the regular and large versions of the icon. As noted in the section above, however, if you’re changing SVG attributes you will have to call your get-icon-x function multiple times.
There are a lot of approaches to using SVG icons and the best one is going to depend on your use case. But the above seems to have a lot going for it:
Each icon is defined once and all icons are located in a single location.
Unlike with font icons or sprites you can selectively include the icons needed on a page.
Passing in attribute values lets you reuse the same SVG in different contexts and adds clarity to the CSS.
Your CSS files are much easier to read. When you see background-image: get-icon-anchor(#b44); it’s immediately clear what’s going on.