Grow your CSS skills. Land your dream job.

SVG `symbol` a Good Choice for Icons

Published by Chris Coyier

You could design an icon set where the icons all had the exact same aspect ratio. But that's probably not typically going to be the case. The container around a little beaker icon might be tall and narrow. The container around a little fish perhaps short and long. You probably shouldn't have to think too much about that, but unfortunately you kinda have to when you use an SVG icon system as I've described in the past, because you need to use the viewBox attribute to describe that container/aspect ratio.

An improvement is to use the <symbol> element in SVG instead of directly referencing shapes (or a <g>), because you can define the viewBox directly on the <symbol> and then not need one when you <use> it later in an <svg>.

An example is in order.

Here's two icons with very different aspect ratios, as you can tell from the artboard in Illustrator.

We could adjust them to all be placed within a consistent aspect ratio, but I find it's more flexible and workable to know that your icons edges are right where the shapes stop, not with an arbitrary amount of white space around them.

The "Old" Way

If we go the <defs>-block route, we might combine them into:

<svg>
  <defs>
    <g id="shape-icon-1">
      <!-- all the paths and shapes and whatnot for this icon -->
    <g>
    <g id="shape-icon-2">
      <!-- all the paths and shapes and whatnot for this icon -->
    <g>
  </defs>
</svg>

Then use them like:

<!-- These viewBox's better be right or the icons won't look right! -->

<svg class="icon" viewBox="214.7 0 182.6 792">
  <use xlink:href="#shape-icon-1" />
</svg>

<svg class="icon" viewBox="0 26 100 48">
  <use xlink:href="#shape-icon-2" />
</svg>

That puts a good amount of onus on the implementer to get those viewBox attributes correct in the markup. That's one reason why might want to try and get all those icons at a consistent viewBox="0 0 100 100" (or something), but then we have that kinda arbitrary whitespace thing going on.

The "New" Way

Enter Fabrice Weinberg and TxHawks. Fabrice works on grunt-svgstore, a Grunt plugin for creating the SVG sprites from a folder of SVG files. This kind of thing makes the SVG icon workflow quick and easy. That is, except for the fact that you need to know that dang viewBox for each icon before you use it.

TxHawks suggested having grunt-svgstore at least put data-* attributes on the <g> elements that wrap each icon, so there could be programmatic access to what it is supposed to be. But unfortunately SVG doesn't allow those (it would have probably worked, but might as well make a build tool spec-compliant). It doesn't matter though, because soon after, they suggested using <symbol> instead, which turns out to be quite a good idea.

Instead of using <g> to wrap all the icon shapes, use <symbol>, like this:

<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
  
  <symbol id="beaker" viewBox="214.7 0 182.6 792">
    <!-- <path>s and whatever other shapes in here -->  
  </symbol>
  
  <symbol id="shape-icon-2" viewBox="0 26 100 48">
    <!-- <path>s and whatever other shapes in here -->  
  </symbol>
  
</svg>

Note that the viewBox is defined for each icon and as you're defining it as opposed to when you're using it. That means using it becomes easier:

<!-- We ain't even need no viewBox round here. --> 

<svg class="icon">
  <use xlink:href="#shape-icon-1" />
</svg>

<svg class="icon">
  <use xlink:href="#shape-icon-2" />
</svg>

Easier, and less error prone.

And it gets better: you can add <title> and <desc> tags in the symbol as well, meaning the accessibility stuff is baked in as you use it.

<symbol id="icon1" viewBox="original-file's-viewBox">
  <title>original-file's-title</title> 
  <desc>original-file's-desc</desc>

  <!-- <path>s and other shapes -->

</symbol>

grunt-svgstore does this now, thanks to TxHawks and Fabrice!

Why <symbol> is better for icons

Just to put a point on it:

  1. The viewBox can be defined on the symbol, so you don't need to use it in the markup (easier and less error prone).
  2. title and desc tags can be added there as well, making accessibility easier to do right.
  3. Symbols don't display as you define them, so no need for a <defs> block.
  4. This is probably what <symbol> was invented for anyway.

Demo

It works:

See the Pen Hwcxp by Chris Coyier (@chriscoyier) on CodePen.

Comments

  1. Travis

    Beautiful. Definitely going to try this out on one of my current projects. Thanks Chris!

  2. This is a great idea!

    Is symbol supported everywhere inline SVG is?

  3. Wow. This technique is exactly what I was looking for as a follow up to the other with SVG article.

    I had a set of images that were all SVG but different viewports, dynamically included, and benefited greatly from being styled via CSS vs the old way I had to do it (gross image swapping). Think I’ll be coming back to this technique a LOT. Thanks Chris and Co!

  4. Not to rain on a wonderful parade…but on my system…which is Windows 8 pro running Chrome 35 …The shark symbol in the codepen is cut off…

    On my same system (Windows 8 pro) but using Firefox 29 the shark looks fine…

    Also IE 11 looks fine to…

    Even still…great post…

    Michael Whyte

    • MaxArt

      If that may relieve you, it looks just fine on my Chrome 37 on Windows 7.
      There’s a change it’s a temporary bug and the fix will land soon.

    • Charles

      The shark looks fine for me on Chrome 35, however as soon as I zoom in it starts getting cut off. Zooming out does something similar, it just shrinks. I know things like to mess up when the zoom level is changed, but I haven’t seen anything change so dramatically before.

    • I had the same issue when using this technique on a project recently and the fix was to add the viewBox to the :(

      If anyone figures out another solution, please share!

  5. Terence

    This was my ONE issue that I had with using svg icon sprites (I still used them and loved them) and now it’s solved! Thanks for being awesome, can’t wait to use it in my next project and not have to worry about those dreaded viewBoxes.

  6. Awesome! I was hoping someone would get around the view box issue. I will ‘defs’ be using this in favour of font icons.

  7. Cherryn

    I just tried this method and found that if I zoom the page the symbol can’t show properly in its viewBox, ’cause viewBox is defined in defs. But this will not occur if I use .

  8. Olde Worlde

    This doesn’t work on my new Windows XP machine running IE6

  9. This is pretty awesome. However for this to work, these icons still need to be a set width and height, with the same aspect ratio, right? Right now, to achieve fluidity you still have to use that padding-bottom technique!

  10. Dope tutorial sir… #Futuristic

  11. I think I could weep from happiness. I was just wrangling with viewbox parameters last week, and didn’t really understand why it was giving me such a hard time.

  12. Afram

    has anyone else found that this technique doesn’t work on IE when including external SVG sprites with the tag? I am aware that IE does not allow for external svg files OOB (though Microsoft say it is under consideration atm).

    I am using svg4everybody which used to work before, but now when moving over to symbols, it refuses to work. Any ideas?

    • Afram

      sorry, typo. Meant to say ‘including external SVG sprites with the ‘use’ tag”

  13. This technique will not work as expected with external SVGs in any browser. The viewBox attribute will be ignored, whether it is on the external <symbol> or <svg> element. As with the <use> technique, IE will ignore the external SVG all together.

    Your JavaScript-free options with <symbol> are:
    1. Only use internal SVGs.
    2. Always add the viewBox attribute to every SVG.

    Your JavaScript options are:
    1. Guess and follow a syntax that you hope becomes native.
    2. Use a syntax that is entirely unique.

    IMHO, all of those options are valid. Requiring a JavaScript that never breaks is a worthwhile downside to never having to write the viewBox attribute for every single SVG on every single page.

    Meanwhile, I will pursue option 1: http://discourse.specifiction.org/t/simple-svg-markup/92

    • Afram

      Thanks for your reply Johathan, just a couple of things.

      This technique will not work as expected with external SVGs in any browser.

      I don’t think this is true. I’ve managed to successfully use an external SVG of symbols. It works on all browsers except for IE.

      I think the JavaScript-free options wont work for big projects, so that leaves the JS options.

      I’m still thinking about what would be best here, but will update this thread if I find something satisfying.

  14. Josh

    Nice, will have to give this a try at some point. How does this work vertically aligning along side other elements?

    Currently using display table & table cell.

  15. It’s exciting opportunities that we provide the new methods be current though everything is a little stressful.
    Best regards.

  16. Could you expand on the benefits of using symbol instead of (nested) svg? Are there any?

    Example:

        &lt;!-- s and whatever other shapes in here --&gt;  
    
    
    
        &lt;!-- s and whatever other shapes in here --&gt;  
    
  17. Philip
    Permalink to comment#

    Hi together

    Is it possible to animate a path in the symbol tag with css3 keyframe animation? It doesn’t work in my cases.

    Example: http://codepen.io/anon/pen/Hfbmr

    Cheers Philip

  18. fvsch
    Permalink to comment#

    By the way, it’s not mandatory but the SVG 1.1 spec recommends putting symbol elements inside defs for clarity and accessibility. It’s probably more theoretical than having a real impact on accessibility, though.

  19. this is so awesome

    vector (check)
    multi color (check)
    single trip to the server for all your vector resources (check)

    it’s the holy grail

  20. Hey this seems to be reeally cool! I thought it’s cool enough to use this technique with grunt-svgstore on a project. But I noticed a big caveat which I hope you can help me solve. How do you go about doing hoverstates? When you select the svg you want to give a hoverstate it won’t work because you can’t select the actual svg shape when you just use “. On the other hand if you directly select the shape like so #icon-arrow it won’t work because you never actually hover this shape since the shape you see is only a reference to it…or is it? Any ideas/experiences you can share?

    Thanks a lot!

  21. You could add rules that are not already used in original SVG, as stroke or stroke-width.

    Others, like fill, wich are default, can be written with fancy values, so they cannot be applied and fixed later. DEMO

    svg {
      fill:errorColorName;/* this is an error on purpose, so no color are applied */
      stroke:red;
    }
    .icon:hover use {
      fill:turquoise;
    }
    .icon +.icon:hover use {
      fill:yellow
    }
    
  22. Hi,
    Good News. All zooming problems are solved with Chrome Canary v.38

  23. Turns out, must go above in the document, otherwise this technique won’t work. So, you cannot have all of your icons at the bottom of the document.

  24. Found an issue with symbol when page is scaled in Webkit:

    http://monosnap.com/image/SRqgBNCb6qSjT12yAYPtxb5poXgdMI

    Code here – http://codepen.io/vikmind/full/sAmjK/

This comment thread is closed. If you have important information to share, you can always contact me.

*May or may not contain any actual "CSS" or "Tricks".