SVG Icons FTW

Despite the growing popularity of SVG, its time around and excellent browser support, we have a lack of good vector icon approaches. It’s time to fill this gap and use SVG as icons in our web projects.

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.

Tiny break: 📬 Want to stay up to date with frontend and trends in web design? Subscribe and get our Collective newsletter twice a tweek.

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)

Performance

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 icons Paint operation time, ms Paint operation time per 1 icon, ms
1 0.059 0.059
30 1.453 0.04843
90 4.373 0.04858
180 9.315 0.05175
3000 32.131 0.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!

Stay in the loop: Get your dose of frontend twice a week

Fresh news, inspo, code demos, and UI animations—zero fluff, all quality. Make your Mondays and Thursdays creative!

Feedback 53

Comments are closed.
  1. Love the clock demo. I’ll also be spending some time studying your section on filters. I have a little social icon project in progress here: maxw3st.us/svgshare/share_reveal.html

  2. One of the downsides of filters, at least as I’m seeing them in Safari on iOS 7, is that it turns the SVG into a bitmap. The icons are pixelated on my retina screen when a filter is used. Is there any way around this?

  3. This might be kind of a newbie question but I’m having a hard time finding an answer.

    I want to use SVG on several pages, but not actually put the code on each page. What is the best way to include it? Could I include it through a stylesheet?

    Or would a php or link href= be the way to go?

    Essentially I want to create a set of icons usable on any page of a website, but keep the code separate.

    • Good question! You can make xhr request to load svg file with your icons and then prepend it’s contents to the tag. Iconmelon uses this snippet to do so. You can also use a Jquery version if you like.
      The vital detail here is always PREpend svg source file to the , so everything else goes after it. Cheers!

  4. There is also a nice technique to stack all images (e.g. different icons) at the same position within one SVG file (but within their own layer) to include them via hash + layername (e.g. #icon). This will make it even easier to use and maintain the image(s) and can be combined with the filters/effects/animations you mentioned above.
    Basically this is how to create the SVG stack:
    » http://hofmannsven.com/2013/laboratory/svg-stacking/

    • Hey! This is a nice technique, but unfortunately it doesn’t allow us to use CSS to style our icons. This is significant difference between this to methods, and I spend a lot of my time trying to figure out how to achieve this feature. It also wouldn’t allow as to use an animation, I believe.
      The second trouble with SVG stacks – they are currently seem to be dead and don’t work in chrome.

      I have another solution for CSS manageable SVG icons and it uses CSS only, even to apply a shape of an icon. It needs some tricks and code preprocessing to work across browsers(with bit of chrome issues) ) so it is a bit harder to explain. If you are interested in it you can see it here.
      The cross browsers’ source codes for it is here(it has branches for Stylus, Less and Sass preprocessors).

  5. Hi, Thank you for nice article, can provide a description how prepare the graphics for the clock, since you mentioned they need to separate ID’s are they positioned in absolutely.

    • Hi, thank you! Sure, let me try.
      Since vector graphics has no flow as CSS has, we can say that everything in SVG document is positioned absolutely.
      But the position is determined by SVG and not by CSS. The start of coordinates system(x:0,y:0) is upper left corner. In my example positions of the clock hands is set by “x” and “y” attributes on each one: imagedemo see html tab.
      The ids on this clock hand tags were set only for the ability to “hook” them from javascript but not to posit them, so this demo contains a plain SVG file produced in Illustrator, with an addition of id and transform attributes on clock hand tags by me.

  6. great work. specially the interactive clock! (i didn’t notice the time was working till i saw my clock!)

  7. This is great, but I’m a bit mystified: how would you specify an absolute stroke width? It seems to be proportional to the size of the original graphic somehow—which I can’t quite figure out; in any case, adding the following code to any of those codepens results in a stroke much bigger than 1px:

    .icon{ width: 32px; height: 32px; fill: #ccc; stroke: #000; stroke-width: 1px }

    • Hey, nice question! Sort answer is yes.

      Long answer:
      Due to svg viewport(viewBox), stroke scales pretty confusing, no matter if you specify it in absolute(px) or relative(%) units. So stroke-width of 1px(or %) is relative to static viewBox size, while icon size is dynamic.
      For instance, if you have a viewBox of 32px(0 0 32 32) with 1px stroke, for your 64×64 pixels the icon stoke will be 2px , then 3px for your 96×96 icon and so on. As you probably have noticed. Fortunately can specify a non-scaling stroke to achieve exactly what you want. demo

  8. How does this affect accessibility?

    Seems like we are cluttering the markup quite a lot with our svg definitions, and we are inserting things that a blind person parsing the html has no use for. For a lot of pages I think I would still prefer to load in the svg as backgrounds via css :after or :before. I get that it limits the possibilities for manipulating it further on with css though.

    • yes good point made by jorgen. I would like to know about the accessibility issues it might cause as well.

    • Hey! Thanks for your care about accessibility.
      I guess you mean this high contrast mode fix for disappearing background images. It is not necessary here at all, svg behaves pretty nice in high contrast mode: demo 1 demo 2.
      We have to realize that we are injecting chunks of markup language, that was intended to be parsed by screen readers and crawlers. I’ve made some accessibility tests and didn’t face any issues caused by the described approach(and don’t see them even theoretically), if you do, please let me know here or at iconmelonissues tracker. Cheers!

  9. Thanks for this nice summary / tutorial, Oleg!

    However, I consider the biggest disadvantage of this approach that all SVG has to reside inline inside the HTML source. Not only that this might affect accessibility as Jørgen mentioned, it also prevents the browser cache from being leveraged the way it could. As we are talking about icons, and icons in general are likely to be used multiple times across multiple documents / URLs, it seem’s desirable to not load them more often than necessary but deliver them from the cache instead as often as possible. Yes, you are right, HTML along with inline SVG may be compressed pretty well for transmission, but still there’s an overhead that could be avoided. Unfortunately, this seems to be the tradeoff we have to pay at the moment for having all those shiny client side manipulation possibilities.

    Another disadvantage might be that there doesn’t seem to be a proper fallback technique for browsers not supporting SVG. With external SVG icon files one can easily implement a fallback using PNG images (e.g. by using iconizr or grunticon). I’d love to see something similar for this inline approach! Any ideas on that?

    • Hi, thanks! SVG source file not necessary has to reside inside HTML source.
      You can make a xhr request to load it(please see this comment) so it could be cached. There is no need for it, if you are developing one page app though.

      Sure, we can implement a png fallback for old browsers or SVG background image fallback(if we don’t care about IE<8 and Andriod<2.3 browsers) see demo. We will probably implement a png fallback option for Iconmelon shortly. I should notice, we are facing a problem here – icon with this fallback will have a static color in old browsers, while SVG icon will have a dynamic color for modern ones(set in CSS).

    • Yeah I think the biggest issue with SVGs is their inline use. They bloat the file terribly and if they’re anything more complicated than the example here it can get really unwieldy. Still there are a lot of benefits.

  10. Been researching SVGs for hours and reached the following conclusion:
    SVGs are even slower in other browsers. Actually chrome is the fastest according to the below article – so if SVG is 2x slower in chrome, it will be much, much slower in other browsers (actually SVG performance takes a hit exponentially so the more icons u have on the page, the worse it is and hence, using SVG for icons is probably not a solution as of yet). Also, like you said, if you try to add interactivity to SVGs or animate them in some way, the performance will be EVEN worse!
    http://frozeman.de/blog/2013/08/why-is-svg-so-slow/

    • We just launched a site that has dozens of inlined SVG icons on many pages, and they render very quickly even on mobile.

      Maybe browsers caught up?

  11. The second example is wrong. Error in 13 string
    <use xlink:href="#heart-icon“>

    tag strong…

  12. Great article, much appreciated.
    there is a typo in: “PERFOMANCE” missing “R”
    We were about to switch from icon-font to SVG, but we have concerns about the performance. Some sources state that the performance impact is huge (especially apart from Chrome). Could you briefly describe your test setup? I try to investigate the performance in detail. (It would be nice to see the impact of inline svg compared to, svg background-image, png background-image, canvas and icon-font)

    • Hey Stophe! I’ve used the plain chrome dev tools(esp timeline) to mesure page load time in my tests.
      You are doing the fundamental work with your tests, thank you for that, and keep going! The web definitely needs more tests on rendering performance and especially the svg one because it makes the first steps to settle down in browsers. It’s pretty neat to see a comparison of png to svg or webp rendering time. I think you can add the jpg format since it has another decoding algorithm. Cheers!

  13. Great and usefull tutoriel but I have a question..
    I try to use 2 SVG with different size on the setting, and for have the same result on html i need write different size on the viewBox (on my menu)…
    In this case, i can’t loop on my HTML and i must set size on HTML each time I want use an image.. (And if I want change the SVG, i need update all the html source for the new size :/ )

    Exemple on jsfiddle : http://jsfiddle.net/Nh57e/

    How can i do for use image without set the icon size ??

    • For auto-reply, this is a good example for use 2 SVG with different size without repeat on the HTML code :

      http://jsfiddle.net/Nh57e/4/

      I already have a probleme with stoke-width look like 2 time smaller on the 64×64 transformed on 32×32, but I can use both 🙂

  14. Instead of writing weird <svg height="0" width="0" style="position:absolute;margin-left: -100%;"><!--formatted--> you can simply put your definitions in <defs><!--formatted--> tag.

  15. Great and usefull tutoriel but I have a question (in french) sorry
    Comment peut-on centrer le svg time du 1er exemple avec les differents reveils sur 960px à la place de la balise body{ text-align: center;} pour éviter que mes autres textes de la page soient aussi centrés. Merci

    (How can we focus the svg time 1 example with different alarm clocks to 960px instead of the body tag {text-align: center;} to prevent my other texts on the page are also centered. thank you)

  16. Excellent post but for WordPress users, the native Dashicons built into the core are very efficient and easy to use.

  17. IE10 isn’t displaying/rendering any of the heart-icons that have the drop shadow applied.

  18. Just discovered SVG icons, and they’re awesome. Can I suggest iconmoon.io for uploading any of your images and creating a svg resources.

  19. Thanks for the terrific article. The examples are great for a SVG newbie like me. A lot covered and clearly.

  20. Hi, this is fantastic! Thank you! I just have a problem seeing any of your demos on any of the browsers – do you know what might be an issue ? :/

    • Hey 🙂 The demos are Codepen embeds and I can see them from here… Are you having trouble viewing Codepen embeds elsewhere? Is there any error shown in the console? Thanks and cheers 🙂 ML