The following is a guest post by Rob Levin and Chris Rumble. Rob and Chris both work on the product design team at Mavenlink. Rob is also creator and host of the SVG Immersion Podcast and wrote the original 5 Gotchas article back in ’14. Chris, is a UI and Motion Designer/Developer based out of San Francisco. In this article, they go over some additional issues they encountered after incorporating inline SVGs in to Mavenlink’s flagship application more then 2 years ago. The article illustrations were done by Rob and—in the spirit of our topic—are 100% vector SVGs!
Wow, it’s been over 2 years since we posted the 5 Gotchas Getting SVG Into Production article. Well, we’ve encountered some new gotchas making it time for another follow up post! We’ll label these 6-10 paying homage to the first 5 gotchas in the original post :)
Gotcha Six: IE Drag & Drop SVG Disappears

If you take a look at the animated GIF above, you’ll notice that I have a dropdown of task icons on the left, I attempt to drag the row outside of the sortable’s container element, and then, when I drop the row back, the SVG icons have completely disappeared. This insidious bug didn’t seem to happen on Windows 7 IE11 in my tests, but, did happen in Windows 10’s IE11! Although, in our example, the issue is happening due to use of a combination of jQuery UI Sortable and the nestedSortable plugin (which needs to be able to drag items off the container to achieve the nesting, any sort of detaching of DOM elements and/or moving them in the DOM, etc., could result in this disappearing behavior. Oddly, I wasn’t able to find a Microsoft ticket at time of writing, but, if you have access to a Windows 10 / IE11 setup, you can see for yourself how this will happen in this simple pen which was forked from fergaldoyle. The Pen shows the same essential disappearing behavior happening, but, this time it’s caused by simply moving an element containing an SVG icon via JavaScript’s appendChild
.
A solution to this is to reset the href.baseVal attribute on all <use>
elements that descend from event.target
container element when a callback is called. For example, in the case of using Sortable, we were able to call the following method from inside Sortable’s stop
callback:
function ie11SortableShim(uiItem) {
function shimUse(i, useElement) {
if (useElement.href && useElement.href.baseVal) {
// this triggers fixing of href for IE
useElement.href.baseVal = useElement.href.baseVal;
}
}
if (isIE11()) {
$(uiItem).find('use').each(shimUse);
}
};
I’ve left out the isIE11
implementation, as it can be done a number of ways (sadly, most reliably through sniffing the window.navigator.userAgent
string and matching a regex). But, the general idea is, find all the <use>
elements in your container element, and then reassign their href.baseVal
to trigger to IE to re-fetch those external xlink:href
‘s. Now, you may have an entire row of complex nested sub-views and may need to go with a more brute force approach. In my case, I also needed to do:
$(uiItem).hide().show(0);
to rerender the row. Your mileage may vary ;)
If you’re experiencing this outside of Sortable, you likely just need to hook into some “after” event on whatever the parent/container element is, and then do the same sort of thing.
As I’m boggled by this IE11 specific issue, I’d love to hear if you’ve encountered this issue yourself, have any alternate solutions and/or greater understanding of the root IE issues, so do leave a comment if so.
Gotcha Seven: IE Performance Boosts Replacing SVG4Everybody with Ajax Strategy
In the original article, we recommended using SVG4Everybody as a means of shimming IE versions that don’t support using an external SVG definitions file and then referencing via the xlink:href
attribute. But, it turns out to be problematic for performance to do so, and probably more kludgy as well, since it’s based off user agent sniffing regex. A more “straight forward” approach, is to use Ajax to pull in the SVG sprite. Here’s a slice of our code that does this which is, essentially, the same as what you’ll find in the linked article:
loadSprite = null;
(function() {
var loading = false;
return loadSprite = function(path) {
if (loading) {
return;
}
return document.addEventListener('DOMContentLoaded', function(event) {
var xhr;
loading = true;
xhr = new XMLHttpRequest();
xhr.open('GET', path, true);
xhr.responseType = 'document';
xhr.onload = function(event) {
var el, style;
el = xhr.responseXML.documentElement;
style = el.style;
style.display = 'none';
return document.body.insertBefore(el, document.body.childNodes[0]);
};
return xhr.send();
});
};
})();
module.exports = {
loadSprite: loadSprite,
};
The interesting part about all this for us, was that—on our icon-heavy pages—we went from ~15 seconds down to ~1-2 seconds (for first uncached page hit) in IE11.
Something to consider about using the Ajax approach, you’ll need to potentially deal with a “flash of no SVG” until the HTTP request is resolved. But, in cases where you already have a heavy initial loading SPA style application that throws up a spinner or progress indicator, that might be a sunk cost. Alternatively, you may wish to just go ahead and inline your SVG definition/sprite and take the cache hit for better-percieved performance. If so, measure just how much you’re increasing the payload.
Gotcha Eight: Designing Non-Scaling Stroke Icons
In cases where you want to have various sizes of the same icon, you may want to lock down the stroke sizes of those icons…
Why, what’s the issue?

Imagine you have a height: 10px; width: 10px;
icon with some 1px
shapes and scale it to 15px
. Those 1px
shapes will now be 1.5px
which ends up creating a soft or fuzzy icon due to borders being displayed on sub-pixel boundaries. This softness also depends on what you scale to, as that will have a bearing on whether your icons are on sub-pixel boundaries. Generally, it’s best to control the sharpness of your icons rather than leaving them up to the will of the viewer’s browser.
The other problem is more of a visual weight issue. As you scale a standard icon using fills, it scales proportionately…I can hear you saying “SVG’s are supposed to that”. Yes, but being able to control the stroke of your icons can help them feel more related and seen as more of a family. I like to think of it like using a text typeface for titling, rather than a display or titling typeface, you can do it but why when you could have a tight and sharp UI.
Prepping the Icon
I primarily use Illustrator to create icons, but plenty of tools out there will work fine. This is just my workflow with one of those tools. I start creating an icon by focusing on what it needs to communicate not really anything technical. After I’m satisfied that it solves my visual needs I then start scaling and tweaking it to fit our technical needs. First, size and align your icon to the pixel grid (⌘⌥Y in Illustrator for pixel preview, on a Mac) at the size you are going to be using it. I try to keep diagonals on 45° and adjust any curves or odd shapes to keep them from getting weird. No formula exists for this, just get it as close as you can to something you like. Sometimes I scrap the whole idea if it’s not gonna work at the size I need and start from scratch. If it’s the best visual solution but no one can identify it… it’s not worth anything.
Exporting AI
I usually just use the Export As “SVG” option in Illustrator, I find it gives me a standard and minimal place to start. I use the Presentation Attributes setting and save it off. It will come out looking something like this:
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18">
<title>icon-task-stroke</title>
<polyline points="5.5 1.5 0.5 1.5 0.5 4.5 0.5 17.5 17.5 17.5 17.5 1.5 12.5 1.5" fill="none" stroke="#b6b6b6" stroke-miterlimit="10"/>
<rect x="5.5" y="0.5" width="7" height="4" fill="none" stroke="#b6b6b6" stroke-miterlimit="10"/>
<line x1="3" y1="4.5" x2="0.5" y2="4.5" fill="none" stroke="#b6b6b6" stroke-miterlimit="10"/>
<line x1="17.5" y1="4.5" x2="15" y2="4.5" fill="none" stroke="#b6b6b6" stroke-miterlimit="10"/>
<polyline points="6 10 8 12 12 8" fill="none" stroke="#ffa800" stroke-miterlimit="10" stroke-width="1"/>
</svg>
I know you see a couple of 1/2 pixels in there! Seems like there are a few schools of thought on this. I prefer to have the stroke line up to the pixel grid as that is what will display in the end. The coordinates are placed on the 1/2 pixel so that your 1px stroke is 1/2 on each side of the path. It looks something like this (in Illustrator):

Gotcha Nine: Implementing Non-Scaling Stroke
Clean Up
Our Grunt task, which Rob talks about in the previous article, cleans up almost everything. Unfortunately for the non-scaling-stroke
you have some hand-cleaning to do on the SVG, but I promise it is easy! Just add a class to the paths on which you want to restrict stroke scaling. Then, in your CSS add a class and apply the attribute vector-effect: non-scaling-stroke;
which should look something like this:
.non-scaling-stroke {
vector-effect: non-scaling-stroke;
}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 18">
<title>icon-task-stroke</title>
<polyline class="non-scaling-stroke" points="5.5 1.5 0.5 1.5 0.5 4.5 0.5 17.5 17.5 17.5 17.5 1.5 12.5 1.5" stroke="#b6b6b6" stroke-miterlimit="10"/>
<rect class="non-scaling-stroke" x="5.5" y="0.5" width="7" height="4" stroke="#b6b6b6" stroke-miterlimit="10"/>
<line class="non-scaling-stroke" x1="3" y1="4.5" x2="0.5" y2="4.5" stroke="#b6b6b6" stroke-miterlimit="10"/>
<line class="non-scaling-stroke" x1="17.5" y1="4.5" x2="15" y2="4.5" stroke="#b6b6b6" stroke-miterlimit="10"/>
<polyline class="non-scaling-stroke" stroke="currentcolor" points="6 10 8 12 12 8" stroke="#ffa800" stroke-miterlimit="10" stroke-width="1"/>
</svg>
This keeps the strokes, if specified, from changing (in other words, the strokes will remain at 1px even if the overall SVG is scaled) when the SVG is scaled. We also add fill: none;
to a class in our CSS script where we also control the stroke color as they will fill with #000000
by default.That’s it! Now, you have beautiful pixel adherent strokes that will maintain stroke width!
And after all is said and done (and you have preprocessed via grunt-svgstore per the first article), your SVG will look like this in the defs file:
<svg>
<symbol viewBox="0 0 18 18" id="icon-task-stroke">
<title>icon-task-stroke</title>
<path class="non-scaling-stroke" stroke-miterlimit="10" d="M5.5 1.5h-5v16h17v-16h-5"/>
<path class="non-scaling-stroke" stroke-miterlimit="10" d="M5.5.5h7v4h-7zM3 4.5H.5M17.5 4.5H15"/>
<path class="non-scaling-stroke" stroke="currentColor" stroke-miterlimit="10" d="M6 10l2 2 4-4"/>
</symbol>
</svg>
CodePen Example
The icon set on the left is scaling proportionately, and on the right, we are using the vector-effect: non-scaling-stroke;
. If your noticing that your resized SVG icon’s strokes are starting to look out of control, the above technique will give you the ability to lock those babies down.
See the Pen SVG Icons: Non-Scaling Stroke by Chris Rumble (@Rumbleish) on CodePen.
Gotcha Ten: Accessibility
With everything involved in getting your SVG icon system up-and-running, it’s easy to overlook accessibility. That’s a shame, because SVGs are inherently accessible, especially if compared to icon fonts which are known to not always play well with screen readers. At bare minimum, we need to sprinkle a bit of code to prevent any text embedded within our SVG icons from being announced by screen readers. Although we’d love to just add a <title>
tag with alternative text and “call it a day”, the folks at Simply Accessible have found that Firefox and NVDA will not, in fact, announce the <title>
text.
Their recommendation is to apply the aria-hidden="true"
attribute to the <svg>
itself, and then add an adjacent span
element with a .visuallyhidden
class. The CSS for that visually hidden element will be hidden visually, but its text will available to the screen reader to announce. I’m bummed that it doesn’t feel very semantic, but it may be a reasonable comprimise while support for the more intuitively reasonable <title>
tag (and combinations of friends like role
, aria-labelledby
, etc.) work across both browser and screen reader implementations. To my mind, the aria-hidden
on the SVG may be the biggest win, as we wouldn’t want to inadvertantly set off the screen reader for, say, 50 icons on a page!
Here’s the general patterns borrowed but alterned a bit from Simply Accessible’s pen:
<a href="/somewhere/foo.html">
<svg class="icon icon-close" viewBox="0 0 32 32" aria-hidden="true">
<use xlink:href="#icon-close"></use>
</svg>
<span class="visuallyhidden">Close</span>
</a>
As stated before, the two things interesting here are:
aria-hidden
attribute applied to prevent screen readers from announcing any text embedded within the SVG.- The nasty but useful
visuallyhidden
span which WILL be announced by screen reader.
Honestly, if you would rather just code this with the <title>
tag et al approach, I wouldn’t necessarily argue with you as it this does feel kludgy. But, as I show you the code we’ve used that follows, you could see going with this solution as a version 1 implementation, and then making that switch quite easily when support is better…
Assuming you have some sort of centralized template helper or utils system for generating your use xlink:href
fragments, it’s quite easy to implement the above. We do this in Coffeescript, but since JavaScript is more universal, here’s the code that gets resolved to:
templateHelpers = {
svgIcon: function(iconName, iconClasses, iconAltText) {
var altTextElement = iconAltText ? "" + iconAltText + "" : '';
var titleElement = iconTitle ? "<title>" + iconTitle + "</title>" : '';
iconClasses = iconClasses ? " " + iconClasses : '';
return this.safe.call(this, "<svg aria-hidden='true' class='icon-new " + iconClasses + "'><use xlink:href='#" + iconName + "'>" + titleElement + "</use></svg>" + altTextElement);
},
...
Why are we putting the <title>
tag as a child of <use>
instead of the <svg>
? According to Amelia Bellamy-Royds(Invited Expert developing SVG & ARIA specs @w3c. Author of SVG books from @oreillymedia), you will get tooltips in more browsers.
Here’s the CSS for .visuallyhidden
. If you’re wondering why we’re doing it this particular why and not, say, display: none;
, or other familiar means, see Chris Coyier’s article which explains this in depth:
.visuallyhidden {
border: 0;
clip: rect(0 0 0 0);
height: 1px;
width: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
position: absolute;
}
This code is not meant to be used “copy pasta” style, as your system will likely have nuanced differences. But, it shows the general approach, and, the important bits are:
- the
iconAltText
, which allows the caller to provide alternative text if it seems appropriate (e.g. the icon is not purely decorative) - the
aria-hidden="true"
which now, is always placed on the SVG element. - the
.visuallyhidden
class will hide the element visually, while still making the text in that element available for screen readers
As you can see, it’d be quite easy to later refactor this code to use the <title>
approach usually recommended down the road, and at least the maintainence hit won’t be bad should we choose to do so. The relevant refactor changes would likely be similar to:
var aria = iconAltText ? 'role="img" aria-label="' + iconAltText + '"' : 'aria-hidden="true"';
return this.safe.call(this, "<svg " + aria + " class='icon-new " + iconClasses + "'><use xlink:href='#" + iconName + "'>" + titleElement + "</use></svg>");
So, in this version (credit to Amelia for the aria part!), if the caller passes alternative text in, we do NOT hide the SVG, and, we also do not use the visually hidden span technique, while adding the role
and aria-label
attributes to the SVG. This feels much cleaner, but the jury is out on whether screen readers are going to support this approach as well as using the visually hidden span technique. Maybe the experts (Amelia and Simply Accessible folks), will chime in on the comments :)
Bonus Gotcha: make viewBox width and height integers or scaling gets funky
If you have an SVG icon that you export with a resulting viewBox
like: viewBox="0 0 100 86.81"
, you may have issues if you use transform: scale
. For example, if your generally setting the width and height equal as is typical (e.g. 16px x 16px), you might expect that the SVG should just center itself in it’s containing box, especially if you’re using the defaults for preserveAspectRatio
. But, if you attempt to scale it at all, you’ll start to notice clipping.
In the following Adobe Illustrator screen capture, you see that “Snap to Grid” and “Snap to Pixel” are both selected:

The following pen shows the first three icons getting clipped. This particular icon (it’s defined as a <symbol>
and then referenced using the xlink:href
strategy we’ve already went over), has a viewBox with non-integer height of 86.81, and thus we see the clipping on the sides. The next 3 examples (icons 4-6), have integer width and heights (the third argument to viewBox is width and the fourth is height), and does not clip.
See the Pen SVG Icons: Scale Clip Test 2 by Rob Levin (@roblevin) on CodePen.
Conclusions
The above challenges are just some of the ones we’ve encountered at Mavenlink having had a comprehensive SVG icon system in our application for well over 2 years now. The mysterious nature of some of these is par for the course given our splintered world of various browsers, screen readers, and operating systems. But, perhaps these additional gotchas will help you and your team to better harden your SVG icon implementations!
Thanks for this update. I’ve been following the articles on inline SVGs on this site for a while. This is useful information for the future (especially the suggestion on the stroke vs fill scaling issue)
IE11: I have come to the conclusion that if there are minor interface bugs with very, very old browsers (compare the evergreen versions with 4+ year old IE11) that if there are minor bugs like this, let them be. It’s not worth the time, considering IE11 is dying away.
My solution over the past 20 years now is to plan for the future, and only fix critical bugs for dying browsers. This saved tons of time not learning and implementing hacks (star hack anyone?) and then later spending unfunded time removing hacks when they weren’t needed.
If a client complained, it was simple to say “you are running an non-web standards compliant browser (or a very old one even MS doesn’t support anymore)”. That fixed the issue every time, unless they were willing to pay the cost make a little glitch not show up on their screen. When it comes to money, it’s a far easier decision for them to live with IE limitations.
Thanks Vanderson, I’m glad you found it helpful.
I have empathy for both your position as a developer, but also those customers that are stuck on some version of IE that their sysadmin or company policy due to previously purchased software requirements, etc. It sounds like you either freelance or have your own bootstrapped product, in which case your approach is reasonable if you’ve given them the “browsers supported” info up front. The approach to dealing with IE to my mind is one of those, “well, it depends” kind of things :)
I’ve had plenty of client required using older browsers. (Mainly corporate environments.) And these same clients and many more now, are stuck on IE 11, just like you are dealing with. Before it was years of IE 6 not dying. Because of this, I simply did not use any CSS or fancy features that broke IE 6.
And any minor visual bugs, I asked them if they wanted to pay extra to fix the minor visual issue in IE, and they always declined and said it didn’t matter. Every time. I let the client chose.
This includes the examples above, minor visual issues in IE 11 today, will be a non-issue next year or sooner depending on if the client gets hacked and has to change browsers or a forced update to win 10, etc…
Also, these kinds of interfaces are almost never public facing. Public facing stuff should work with IE 11. But that stuff is usually just a website, not an app. And then the fancy interface stuff doesn’t arise or cause issues like this.
Vanderson, I definitely don’t hold IE11 et al to the same visual aesthetic standards as “A grade” browsers, and I definitely hear you…we’ve all felt this same dev “pain” :)
Gotcha!
But what about going through Jake Archibald’s SVGOMG here is this a good idea too? He has features that should quickly make the changes necessary?
Kind regards,
Mic
Hi Mic, yeah, I’ve heard nothing but great things about SVGOMG and have seen similar approaches to ours that use that. I’m sure it could be used to do what we’re doing, but, I can only say that with the disclaimer that I haven’t done so myself. Perhaps, if you go that route, you could refer to some of the gotchas listed and just figure out if/how to get them supported via that tool.
As I wrote this article with Chris Rumble, we were cognizant of the fact that there are probably several tools/approaches available to tackle SVG icons at this point, and, also there’s the whole move of many to these various Webpack and friends bundlers, which will require a completely different approach. Our intent for this particular article was to provide new findings for this particular toolchain given the first article put out in ’14, and the fact that likely many teams—like ours—might still be using this setup and not yet see ROI in moving in a totally different direction.
That said, I almost wrote a gotcha, “maybe it’s time for a different approach if your team is moving towards Webpack & friends”. But, as of now, it’s another area I cannot yet speak to until I’ve had some hands on experience. I’d personally be fine if someone has a link they want to post to a battle tested solution similar to ours, that they’ve implemented successfully, but, with SVGOmg and/or Webpack :)
Do you know if svgxuse suffers from the same performance problems as SVG4Everybody?
I haven’t used this. I do like that it uses feature detection over regex, although, for IE, I wonder just how reliable that turns out to be. I still prefer the “just always ajax it in” approach, but I can see that there are even issues with that approach (as mentioned in article). My gut tells me that the whole looping every
<use>
element and placing functionality is what caused our issues (I didn’t bother to find root cause as I was already keen to refactor towards using the ajax approach, but that’s got to be the perf killer I’d think); and with that said, this library does appear to do that as well.I don’t like making recommendations without better proof and so please take what I say above as an educated guess or hunch to be fair ;)