SVG Icons FTW

SVGIcons

A lot of great methodologies and principles arose making our CSS more and more modular, structured and flexible. But think about your icons. Using raster graphics for icons means that they are not manageable in CSS. What if we need to change a color of an icon for its hover/active/focus state? For each icon state we need to add one more icon image. What about size? For each icon size we need to add yet another icon image. What about a shape? A shadow? Yup, the same thing. Not very convenient, right? So we are ending up with bloated sprites and style sheets that are hard to maintain and scale.

But fortunately we’ve had icon fonts coming to the rescue. They allow us to manage all these things more easily and we have the following benefits:

  • It’s plain text, which means they can be gzipped to up to 95%
  • It’s a vector graphic, which means it can be scaled to any size making it Retina ready
  • It’s one source file, which means a minimum of HTTP requests
  • It’s a font – you can easily change size, color, shape and add a shadow
  • Browser support for older browsers possible (e.g. IE6)

Unfortunately, icon fonts have some limitations like being monochrome (still) and we can only use styles for texts like e.g. a text shadow. We can’t, for example, use an inset text shadow or apply different colors to the details.

So today we will explore the possibilities of using SVG icons instead.

Using SVG icons

The icon solution I want to share here with you is based on SVG (Scalable Vector Graphics). Extending the benefits of icon fonts, it will give us some additional super powers:

  • The power of consistently crisp rendering across browsers and operating systems.
  • The power of CSS. We will obtain the ability to style our icons with CSS.
  • The power of SVG filter effects.
  • The power of interactivity. We will be able to use animations with SMIL, CSS or JavaScript.
  • The power of the Markup language.

Currently, SVG looks more like a retired super hero. For humanity’s sake, let’s kick him off the sofa and send him to the gym.

Getting started

First, we’ll need to get some SVGs to work with. I’m assuming you are familiar with vector graphics and know how to create or where to get a SVG file. Check out this article over at CSS-Tricks for a good introduction on how to create and work with SVGs. For this tutorial we’ll be using this SVG file. All demos you will see here will actually be working pens, so you will have the opportunity to play with them live.

At this point we have a vector graphic to play with, so let’s add an empty SVG document just after the opening body tag.

I will refer to this SVG as “SVG source document” throughout this tutorial.

Let’s add the “inner” SVG source into the empty SVG declaration and give this shape a unique ID for future reference.

<!doctype html>
<html>
<head>
    <meta charset=utf-8 />
</head>
<body>
  <!-- SVG SOURCE -->
  <svg height="0" width="0" style="position:absolute;margin-left: -100%;">
    <path id="heart-icon" d="M256,465.559c0,0- 239.054-135.345-239.054-291.062c0-159
       .761,224.692-177.574,239.054-7.756 c17.244-169.692,239.054-152.008,239.054,
       7.756C495.054,330.214,256,465.559,256,465.559z"/>
  </svg>
  <!-- SVG SOURCE ends-->
<body>
<html>

Find this code on Codepen

If your shape consist of many small chunks, you’ll need to wrap them all with a g tag and add an ID to that group.

Now we can “use” this graphic wherever we want in our HTML document with the help of use. The xlink:href attribute is a reference to our shape by its ID:

<!doctype html>
<html>
<head>
  <meta charset=utf-8 />
</head>
<body>

<!-- SVG SOURCE —>
...
<!-- SVG SOURCE ends —>

<svg class="icon" viewBox="0 0 32 32"
    <use xlink:href="#heart-icon">
</svg>

</body>
</html>

See the Pen 7bec14841770e985c98af75331e339a9 by Lego Mushroom (@sol0mka) on CodePen

Isn’t that nice?

The use element takes nodes from within the SVG document, and duplicates them somewhere else. The effect is the same as if the nodes were deeply cloned into a non-exposed DOM, and then pasted where the use element is, much like anonymous content and XBL.— Mozilla DN

Thoughtful readers probably noticed the most interesting part of the above pen: we can simply use CSS to style our icons. Take a look:

See the Pen 80187d6000795e8cfd104486861a801e by Lego Mushroom (@sol0mka) on CodePen

The set of properties we can work with is pretty large. The most useful and common ones are:

  • width and height
  • icon color by using the fill property
  • stroke by setting stoke or stroke-width

Styling these properties will give us many possibilities. But let’s make it even better – let’s add our wanted inset shadow.

Adding filter effects

Filter effects are the real super powers of SVG and if you are interested in a detailed overview, check out Filter Effects – SVG 1.1 (Second Edition) and SVG Filters by Mike Sierra.

Let’s work with some pre-made filters. Fortunately, there are a lot of ready-to-use SVG filters around.

To use a filter effect with our icon we need to declare it in our “SVG source document” with a unique ID for referencing, just like the we did with the icon but now we’ll have a filter tag.

<filter id='inset-shadow'>
<!-- Shadow offset -->
<feOffset
dx='0'
dy='0'
/>
<!-- Shadow blur -->
<feGaussianBlur
stdDeviation='1'
result='offset-blur'
/>
<!-- Invert drop shadow to make an inset shadow -->
<feComposite
operator='out'
in='SourceGraphic'
in2='offset-blur'
result='inverse'
/>
<!-- Cut color inside shadow -->
<feFlood
flood-color='black'
flood-opacity='.95'
result='color'
/>
<feComposite
operator='in'
in='color'
in2='inverse'
result='shadow'
/>
<!-- Placing shadow over element -->
<feComposite
operator='over'
in='shadow'
in2='SourceGraphic'
/>

Now, let’s add the reference to the filter to a new group wrapper:

<svg viewBox="0 0 32 32">
  <g filter="url(#inset-shadow)">
    <use xlink:href="#heart-icon"></use>
  </g>
</svg>

Check out the following examples of SVG filters:

Inset shadow:

See the Pen c6721c06e93b4ee6cc95a21f6a4caedc by Lego Mushroom (@sol0mka) on CodePen

Drop shadow:

See the Pen 3bb230339c8f72008ef410246b44c127 by Lego Mushroom (@sol0mka) on CodePen

More examples:

See the Pen 6eca814eda8ec7e758d0feab628bd390 by Lego Mushroom (@sol0mka) on CodePen

Isn’t this awesome?

Adding interactivity

As I’ve mentioned at the beginning of this tutorial, we can use SMIL, CSS or JavaScript for animating icons.

So let’s bring some life to our icons! We’ll create a working animated clock which will be great for showing the possibilities of what we can do.

We will start with an alarm clock icon as the “SVG source document”. It consists of different shapes wrapped in a group with the ID #clock-icon for reference.

See the Pen 841a1d6e68f73f7c10ac9c3385ec7d17 by Lego Mushroom (@sol0mka) on CodePen

Icon credits go to visualpharm. License: CC by-nd 3.0.

I’ve also added some other IDs like #hour-hand, #minute-hand and #second-hand where appropriate for referring to the paths in the animation we will create.

Now, let’s declare a rotation 2D transform for each of the clock hands.

For that we’ll add a transform value of rotate(0 16 17) where

  • 0 is the amount of degrees the clock hand is rotated
  • 16 17 simply the center of our rotation on the X and Y axis.

So, using this rotation value

<rect id="hour-hand" transform="rotate(320 16 17)" x="15.386" y="10.291" width="1.227" height="7.626"/>

will rotate hour clock hand 320 degrees.

Take a look at the demo:

See the Pen 9da5c5c7ba2a9fc636ad041cd38a8f2e by Lego Mushroom (@sol0mka) on CodePen

The next step is to add an animation of the rotation we made above. For this purpose we will use some JavaScript and set a new rotate value every full second:

  <script>
    var setTime = function(){
      var date = new Date(),
      MINUTE = 60, HOUR   = 60*MINUTE,
      seconds = date.getSeconds(),
      minutes = (date.getMinutes()*MINUTE) + seconds,
      hours = (date.getHours()*HOUR)+minutes;

      document.getElementById('second-hand').setAttribute('transform', 'rotate('+360*(seconds/MINUTE)+',16,17)');
      document.getElementById('minute-hand').setAttribute('transform', 'rotate('+360*(minutes/HOUR)+',16,17)');
      document.getElementById('hour-hand').setAttribute('transform', 'rotate('+360*(hours/(12*HOUR))+',16,17)');
    }
    setTime();
    var interval = setInterval(setTime,1000);
</script>

See the Pen 66f2f34eb1a34dee4486060360a7c1a7 by Lego Mushroom (@sol0mka) on CodePen

Isn’t this awesome?

If you are interested in seeing a version using SMIL animation, you can take a look at this demo. Also, check out the CSS version (both demos best viewed in Chrome). Note that SMIL animation support is limited.

Using Media Queries

We can use isolated CSS and especially media queries inside of our SVG icon. That means that we have the ability to change, for instance, a shape of an icon depending on screen size and more. We’ll use this kind of inline style for this purpose:

<style>
   <![CDATA[ 
    @media screen and (max-width:810px){
      .hide{
        display: none;
      }
    }
   ]] >
</style>

Try to resize the browser when viewing this pen in a new window to less or more than 810 pixel.

See the Pen d4676a34387d630bced2bc2979c02cbb by Lego Mushroom (@sol0mka) on CodePen

Isn’t this… SVG?

More fun with SVG icons

fun

Let’s do something fun with all these things we’ve just gone over. The demo below shows differently sized and filled icons, with some JS animations:

See the Pen 430f1716da3ab67e3f86d45f59da3283 by Lego Mushroom (@sol0mka) on CodePen

View this demo in full page

In the next demo we’ll add some filter effects:

See the Pen 92ca494d1a1f8fc1abaa3b4a83faf11c by Lego Mushroom (@sol0mka) on CodePen

View this demo in full page

Having all these superpowers, the only limit is your imagination.

Browser support

What we have done so far should work well in all popular modern browsers:

  • IE 9+ (filter effects since IE10)
  • Mozilla 4+
  • Opera 11.6+ (filter effects since Opera 12)
  • Safari 5.1+ (filters since Safari 6)
  • Chrome 14+ (at least)

Perfomance

After running a couple of tests, it shows that SVG icons are about two times slower compared to using a .wof icon font in Chrome. The time cost of the first paint operation is about 0.05 millisecond per icon or about 1.453ms for an average of 30 icons per user’s view.

Number of iconsPaint operation time, msPaint operation time per 1 icon, ms
10.0590.059
301.4530.04843
904.3730.04858
1809.3150.05175
300032.1310.01071

These tests were made without any filter effects or interactivity which clearly will have an impact on these numbers.

Conclusion

professor farnsworth

Let’s look back and analyze what we’ve done.

Using SVG as icons in our web projects gives us the following advantages:

  • Only one HTTP request
  • Scalable vector and hence intrinsically Retina ready
  • Easy styling of color/size/effects in CSS
  • SVG icons are “light-weight” (the heart icon from my examples has only 189 bytes not gzipped)
  • It’s text so it can be gzipped up to 95%
  • Power of SVG filter effects
  • XML like structure
  • We can have multi-colored icons
  • We can make use of isolated styles and media queries
  • We can make use of isolated animations with SMIL, CSS or JS
  • Supported by all major modern browsers

The Iconmelon project

iconmelon

As you have seen, using SVGs in your web project gives you a lot of possibilities but it would be great to have some kind of boilerplate for the icons itself, the filters and the CSS. So, I’ve been working on a free and open project called Iconmelon which aims to collect free SVG icon sets and provide a filter and CSS boilerplate. You can create your icon sets and download all the needed files and also submit your own graphics.

IconmelonScreenshot

It’s a brand new project so if you see any issues or have any feedback please let me know. Please also check out the support us page to see how you can help it grow.

Thanks!

I’ve spent quite some time playing with SVG, so if you have any questions don’t hesitate to ask me. I’m waiting for your feedback in the comments. Cheers!