Looking for a better way to do SVG icons? Inline SVG might be your best bet, which you can learn about in this more recent post.
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 element in SVG instead of directly referencing shapes (or a
), because you can define the
viewBox
directly on the and then not need one when you
it later in an
.
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 -block route, we might combine them into:
Then use them like:
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 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
instead, which turns out to be quite a good idea.
Instead of using to wrap all the icon shapes, use
, like this:
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:
Easier, and less error prone.
And it gets better: you can add
original-file's-desc
grunt-svgstore does this now, thanks to TxHawks and Fabrice!
Why <symbol> is better for icons
Just to put a point on it:
- The
viewBox
can be defined on the symbol, so you don’t need to use it in the markup (easier and less error prone). title
anddesc
tags can be added within theand they kinda “come along for the ride” when the symbol gets used, making accessibility easier to do right.
- Symbols don’t display as you define them, so no need for a
block.
- This is probably what
was invented for anyway.
Demo
It works:
See the Pen Hwcxp by Chris Coyier (@chriscoyier) on CodePen.
Beautiful. Definitely going to try this out on one of my current projects. Thanks Chris!
This is a great idea!
Is symbol supported everywhere inline SVG is?
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!
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
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.
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.
Ugh. Another SVG implementation bug.
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.
Chromium Issue 334526 seems relevant, no mention of a fix coming out.
http://codepen.io/AmeliaBR/pen/stAki?editors=110
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!
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.
Awesome! I was hoping someone would get around the view box issue. I will ‘defs’ be using this in favour of font icons.
Huzzah! Thanks for your diligent exploration, Chris.
This is finally the missing piece I’ve been waiting for to translate http://stackicons.com from icon font-based to SVG-based!
Gulp plugin: https://www.npmjs.org/package/gulp-svg-icons/
Have you figured out a clean solution for png fallback with gulp?
An important tip. It really does simplify the mark-up.
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).
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 .
I just discovered this as well. Any solution?
This doesn’t work on my new Windows XP machine running IE6
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!
http://codepen.io/AmeliaBR/pen/lykCm/?editors=110
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…
Dope tutorial sir… #Futuristic
Another benefit of switching to
symbol
is that it allows for incorporatingdefs
blocks from the original SVGs in the generated one, and thus enabling us to use gradients, clipping paths, etc. in icons.This means that:
Will become:
Hence allowing us to
use
the gradients defined inside the inside the original SVGs consistently across allsymbol
s .It does mean, however, that for the gradients to render, we need to hide the svg with
visibility:hidden;
rather thandisplay:none;
.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.
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?
sorry, typo. Meant to say ‘including external SVG sprites with the ‘use’ tag”
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
Thanks for your reply Johathan, just a couple of things.
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.
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>.
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.
It’s exciting opportunities that we provide the new methods be current though everything is a little stressful.
Best regards.
Could you expand on the benefits of using
symbol
instead of (nested)svg
? Are there any?Example:
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
By the way, it’s not mandatory but the SVG 1.1 spec recommends putting
symbol
elements insidedefs
for clarity and accessibility. It’s probably more theoretical than having a real impact on accessibility, though.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
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!
My solution to this: http://codepen.io/jjenzz/pen/AxLKz
So it’s the fill=”currentColor” in the symbol path declared at the head it seems to make this possible?
You could add rules that are not already used in original SVG, as
stroke
orstroke-width
.Others, like
fill
, wich are default, can be written with fancy values, so they cannot be applied and fixed later. DEMOThis was actually a reply to ↑ Flavio Gortana https://css-tricks.com/svg-symbol-good-choice-icons/#comment-1583277
Hi,
Good News. All zooming problems are solved with Chrome Canary v.38
Turns out,
must go abovein the document, otherwise this technique won’t work. So, you cannot have all of your icons at the bottom of the document.
Found an issue with symbol when page is scaled in Webkit:
http://monosnap.com/image/SRqgBNCb6qSjT12yAYPtxb5poXgdMI
Code here – http://codepen.io/vikmind/full/sAmjK/