SVG `symbol` a Good Choice for Icons

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>

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:

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!

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.

The “good” news is that it isn’t related to <symbol> or <use> elements (there are lots of <use> element bugs).

The bad news is that it applies to any graphical content that is:

a. Sized using percentage measures, or that has percentage-based default sizing (like a <use> element referencing a <symbol>element—default size is 100% of height and width).
b. Inside an SVG that uses a default coordinate system (i.e., it doesn’t have its own viewBox attribute).

As @Charles identified, the problem isn’t visible at default browser settings, but it shows up if your browser window is zoomed in or out. I don’t have beta versions of newer Chrome, but I did test it on Opera and got the same problem, so this looks like it’s deep in the webkit source code.

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.

A note on the title and desc elements: Not all browser/screen reader combinations will fetch the descriptive text from an element that is being duplicated with a <use> element. So defining the title inside the symbol might not actually have an effect when the symbol is used.

Screen reader support for these elements is in general not currently as good as desired, but I know I’ve seen other tests that point out the specific problems with <use> (sorry, couldn’t find the link).

Hopefully, support for the built-in accessibility features will get better soon, but in the meantime web developers might want to consider other ways of inserting text descriptions of their icons if they are essential for meaning (see the linked article for examples).

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!

There’s no way to get the wrapper SVG to shrink to fit its contents, like a span containing a proportional-width icon font could do. The symbol shrinks or expands to fit the SVG, adjusted according to the preserveAspectRatio attribute, but if you wanted to change the actual SVG dimensions to match the proportions you’d need extra CSS rules for each aspect ratio.

The “padding-bottom technique” sort-of works with a “slice” preserveAspectRatio attribute on the symbol itself, although only if you’re generous with your overflow:visible CSS rules. You have to allow it on the symbol for Chrome and IE, and on nested <svg> for Firefox (because of the way they implement <use> elements).

However, just using bottom padding you end up with a top-aligned image within the icon square. If you try to use a centering value for preserveAspectRatio, it gets centered within the “official” height of the SVG, not including padding, so you need to center the SVG within top and bottom padding to make things work. Things also get wonky if you scale down so that the official SVG dimensions cause the symbol to overflow the SVG in the horizontal dimension.

The alternative is to apply viewBox and preserveAspectRatio attributes on the SVG itself, but that cancels out the benefit of having clean mark-up from using symbols with their own viewBox attributes!

I really hope SVG2 ends up adopting better rules for auto-scaling inline SVG so we don’t have to hack it like this any more…

Another benefit of switching to symbol is that it allows for incorporating defs blocks from the original SVGs in the generated one, and thus enabling us to use gradients, clipping paths, etc. in icons.

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?

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.

That SVG thing I just said. Totally wrong. Not 100% wrong. But mostly wrong. I was still testing against <g>. IE still does not support external SVGs. I have updated https://github.com/jonathantneal/svg4everybody to work with <symbols>.

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.

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?

We have a pretty good* newsletter.

Email Address

CSS-Tricks* is created, written by, and maintained by Chris Coyier and a team of swell people. It is built on WordPress, hosted by Media Temple, and the assets are served by MaxCDN. The fonts are Source Sans Pro and Source Code Pro. It is made possible through sponsorships from products and services we like.