I'm making a website for a client which sells a board game. The boards come with different coloring, so to display them it felt natural to use SVG, because they designed them with illustrator. Everything was fine on chrome and edge, the 2 browsers I'm using daily. However, on Safari and firefox some of the SVGs appears glitchy, with missing parts or miss connected ones.
Here is how one looks like on edge:
And here it is on firefox (the difference is circled in red):
Here is a minimal reproducible example:
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 134.92 134.92">
<defs>
<style>
.cls-2{clip-path:url(#clip-path);}
.cls-3{fill:url(#linear-gradient);}
.cls-4{fill:#dcccb9;}
.cls-5{fill:#fff;}
</style>
<linearGradient xmlns="http://www.w3.org/2000/svg" id="linear-gradient" x1="0.06" y1="-0.06" x2="134.85" y2="134.98" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#eb600a"/>
<stop offset="1" stop-color="#ec7724"/>
</linearGradient>
</defs>
<g id="Calque_2" data-name="Calque 2">
<g id="PLATEAUX">
<g class="cls-2">
<rect xmlns="http://www.w3.org/2000/svg" class="cls-3" width="134.92" height="134.92"/>
<rect xmlns="http://www.w3.org/2000/svg" class="cls-4" x="10.22" y="10.22" width="114.48" height="114.48" rx="3.01"/>
<path xmlns="http://www.w3.org/2000/svg" class="cls-5" d="M33.45,120.25h0l0,0a.42.42,0,0,0,0-.06s0,0,0-.06l0,0,0-.07a373164063697.4,373164063697.4,0,0,1,0-.1.13.13,0,0,1,0-.06.08.08,0,0,1,0,0,.05.05,0,0,1,0,0l.06,0,.06,0,0,0a.14.14,0,0,1,0,.06.2.2,0,0,1,0,.07.25.25,0,0,1,0,.08s0,.05,0,.08l.06,0a.09.09,0,0,0,0-.06l0,0h0v0h0a.16.16,0,0,1-.06.08.17.17,0,0,1-.1,0l0,.09-.06.07-.07,0a.11.11,0,0,1-.08,0h-.06l-.06,0a.15.15,0,0,1,0-.06.36.36,0,0,1,0-.11.69.69,0,0,1,0-.13.75.75,0,0,1,.07-.12l0,0h0s0,0,0,0v0l0,0a.35.35,0,0,0,0,.06.19.19,0,0,0,0,.08.46.46,0,0,0,0,.14A.08.08,0,0,0,33.45,120.25Zm.11-.27a.39.39,0,0,0,0-.13s0,0,0,0a0,0,0,0,0,0,0v.07s0,0,0,0,0,0,0,0l0,0v0Z" transform="translate(3.68 3.68)"/>
</g>
</g>
</g>
</svg>
It looks like paths are not rendered in the same order on chrome and firefox.
Additional information:
My client is designing these boards with an old version of illustrator (2010 or something similar)
The website is made with wordpress, meaning that I import the svg with the editor and the client will in the future use wordpress to add more SVGs
There are smaller glitches with the texts on the image but they are due to the decimal precision (2 diigits) and they are ok
My question is: What can I do to make the SVGs render properly on Safari and Firefox?
Thanks for your help.
The problem is an overflow. Look at this path d attribute string. The bold part is supposed to be two fixed point numbers:
M33.45,120.25h0l0,0a.42.42,0,0,0,0-.06s0,0,0-.06l0,0,0-.07a373164063697.4,373164063697.4,0,0,1,0-.1.13.13,0,0,1,0-.06.08.08,0,0,1,0,0,.05.05,0,0,1,0,0l.06,0,.06,0,0,0a.14.14,0,0,1,0,.06.2.2,0,0,1,0,.07.25.25,0,0,1,0,.08s0,.05,0,.08l.06,0a.09.09,0,0,0,0-.06l0,0h0v0h0a.16.16,0,0,1-.06.08.17.17,0,0,1-.1,0l0,.09-.06.07-.07,0a.11.11,0,0,1-.08,0h-.06l-.06,0a.15.15,0,0,1,0-.06.36.36,0,0,1,0-.11.69.69,0,0,1,0-.13.75.75,0,0,1,.07-.12l0,0h0s0,0,0,0v0l0,0a.35.35,0,0,0,0,.06.19.19,0,0,0,0,.08.46.46,0,0,0,0,.14A.08.08,0,0,0,33.45,120.25Zm.11-.27a.39.39,0,0,0,0-.13s0,0,0,0a0,0,0,0,0,0,0v.07s0,0,0,0,0,0,0,0l0,0v0Z
Firefox seems to have a problem interpreting this as a number, while Chrome at least seems not to produce a suprising rendering.
Adobe Illustrator has a way of minimizing these path commands that makes them hard to read, but you can make out what is supposed to happen:
...0-.07a373164063697.4,373164063697.4,0,0,1,0-.1.13.13,0,0,1,0-.06...
is two relative arc commands, the first with a very large radius. You can safely replace the first arc segment with a line segment. But since the following command is also an arc, written without the command letter a, it needs now to be amended for the following segment:
...0-.07l0-.1a.13.13,0,0,1,0-.06...
Here is the completed result:
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 134.92 134.92">
<defs>
<style>
.cls-2{clip-path:url(#clip-path);}
.cls-3{fill:url(#linear-gradient);}
.cls-4{fill:#dcccb9;}
.cls-5{fill:#fff;}
</style>
<linearGradient xmlns="http://www.w3.org/2000/svg" id="linear-gradient" x1="0.06" y1="-0.06" x2="134.85" y2="134.98" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#eb600a"/>
<stop offset="1" stop-color="#ec7724"/>
</linearGradient>
</defs>
<g id="Calque_2" data-name="Calque 2">
<g id="PLATEAUX">
<g class="cls-2">
<rect xmlns="http://www.w3.org/2000/svg" class="cls-3" width="134.92" height="134.92"/>
<rect xmlns="http://www.w3.org/2000/svg" class="cls-4" x="10.22" y="10.22" width="114.48" height="114.48" rx="3.01"/>
<path xmlns="http://www.w3.org/2000/svg" class="cls-5" d="M33.45,120.25h0l0,0a.42.42,0,0,0,0-.06s0,0,0-.06l0,0,0-.07l0-.1a.13.13,0,0,1,0-.06.08.08,0,0,1,0,0,.05.05,0,0,1,0,0l.06,0,.06,0,0,0a.14.14,0,0,1,0,.06.2.2,0,0,1,0,.07.25.25,0,0,1,0,.08s0,.05,0,.08l.06,0a.09.09,0,0,0,0-.06l0,0h0v0h0a.16.16,0,0,1-.06.08.17.17,0,0,1-.1,0l0,.09-.06.07-.07,0a.11.11,0,0,1-.08,0h-.06l-.06,0a.15.15,0,0,1,0-.06.36.36,0,0,1,0-.11.69.69,0,0,1,0-.13.75.75,0,0,1,.07-.12l0,0h0s0,0,0,0v0l0,0a.35.35,0,0,0,0,.06.19.19,0,0,0,0,.08.46.46,0,0,0,0,.14A.08.08,0,0,0,33.45,120.25Zm.11-.27a.39.39,0,0,0,0-.13s0,0,0,0a0,0,0,0,0,0,0v.07s0,0,0,0,0,0,0,0l0,0v0Z" transform="translate(3.68 3.68)"/>
</g>
</g>
</g>
</svg>
I have no idea if the path in this new variant is what was originally intended. The outline looks a bit strange:
If it is not, Illustrator is the problem.
SUMMARY: An SVG sprite contains five icon <symbol> blocks, one of which references its own gradient definition by ID. It is no longer able to find this gradient and render properly.
JSFIDDLE: http://jsfiddle.net/Qtq24/1/
I am switching some graphics to SVG, and being that they are icons (in this case for social networking profiles) I'd like to keep them in a sprite (as I had with PNG before).
I've followed this guide to SVG sprites on CSS-tricks.com (along with this follow-up which advises using <symbol> instead of <g>).
I now have an SVG sprite file, social-sprite.svg, which you can view in full here.
This is one complete <svg> block containing five different <symbol> blocks, each with an id and with a viewBox attribute. In each case I got the SVG code for each symbol by preparing official icons in Adobe Illustrator and retaining the relevant parts of the processed code.
The .svg file is included via PHP as soon as the <body> tag opens (and this is why the main <svg> container inside it is marked with style="display: none;") so that the references to each symbol work from the HTML.
Four icons work perfectly, and the only one I am having trouble with is the YouTube icon, because it uses an internally-defined gradient. Here is the YouTube part of the SVG code:
<symbol id="youtube" viewBox="0 0 400 281.641">
<path id="Triangle" fill="#FFFFFF" d="M159.845,191.73l106.152-54.999L159.845,81.348V191.73z"/>
<path id="The_Sharpness" opacity="0.12" fill-rule="evenodd" clip-rule="evenodd" fill="#420000" d="M159.845,81.348l93.091,62.162
l13.061-6.778L159.845,81.348z"/>
<g id="Lozenge">
<g>
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="200.4204" y1="2.6162" x2="200.4204" y2="278.9292">
<stop offset="0" style="stop-color:#E52D27"/>
<stop offset="1" style="stop-color:#BF171D"/>
</linearGradient>
<path fill="url(#SVGID_1_)" d="M392.928,62.226c0,0-3.839-27.073-15.617-38.995C362.371,7.583,345.626,7.506,337.947,6.59
c-54.975-3.974-137.441-3.974-137.441-3.974h-0.171c0,0-82.464,0-137.44,3.974c-7.68,0.916-24.419,0.993-39.364,16.641
C11.753,35.153,7.92,62.226,7.92,62.226s-3.929,31.792-3.929,63.583v29.805c0,31.791,3.929,63.582,3.929,63.582
s3.833,27.073,15.611,38.995c14.945,15.646,34.575,15.152,43.318,16.792c31.43,3.015,133.571,3.946,133.571,3.946
s82.552-0.124,137.526-4.099c7.679-0.915,24.424-0.993,39.363-16.64c11.778-11.922,15.617-38.995,15.617-38.995
s3.923-31.791,3.923-63.582v-29.805C396.851,94.017,392.928,62.226,392.928,62.226z M159.863,191.73l-0.018-110.383
l106.152,55.384L159.863,191.73z"/>
</g>
</g>
</symbol>
And this is called in the HTML with:
<svg width="30" height="21">
<use xlink:href="#youtube" src="fallback.png" width="30" height="21" />
</svg>
The opening two paths work fine, the problem is that in this new combined sprite SVG file, with each icon separated as a <symbol>, the "Lozenge" <path> is unable to find the #SVGID_1_ reference to the <linearGradient>.
In Firefox this causes the lozenge to display as white (I assume, perhaps it is not displaying at all - not really looked into it):
whilst Chrome renders it in black:
Obviously neither is acceptable. The only thing I can do at the moment is remove fill="url(#SVGID_1_)" on the path and just fill with a flat colour red appropriate to the YouTube logo. This is not a proper solution though, even discounting the fact that bastardising the YouTube logo in this way would not be accepted under their brand guidelines.
Things I've tried (and had no luck with):
removing the two <g> wrappers that surround the gradient and the path, so the whole of the symbol is just <path>-<path>-<linearGradient>-<path>
wrapping the gradient definition inside a <defs> container
wrapping it in a <defs> and also moving it to the top of the SVG file, i.e. outside the bounds of the YouTube-specific <symbol>
changing ID name (you never know!)
redefining the gradient with percentages rather than pixel values
So how do I get an already-internal <symbol> to reference an also-internal <linearGradient> definition?
EDIT: It turns out the gradient fails when the whole <svg> block is marked with style="display: none;". If this style is removed, the gradient renders properly. But as a reminder, this styling is added so that when you import the SVG sprite it is not rendered instantly on the page, and just allows you to make references to the id-defined symbols as required.
visibility: hidden or opacity: 0 both allow the gradient to render properly, obviously they don't offer proper solutions though as they still demarcate the space that the SVG would have taken up if visible.
After discovering all this, I was pretty sure it would be no problem to have the fully visible <svg> with no stylings added INSIDE a container <div> which is hidden. However, even this causes the gradient not to render. I'm no closer to solving the issue.
Firstly please note the edit to my question - whereupon I discover that the use of display: none to hide the SVG symbols until we need them was the problem.
I kept fiddling and settled upon this "answer", which is far from perfect, but should still be reliable for any such situation.
All you need to do is wrap the entire <svg> code in a <div> container which must be displayed but will never affect layout, so I've just done this via mega overkill CSS such as:
height: 0; width: 0; position: absolute; visibility: hidden;
And this works great. See the final fiddle: http://jsfiddle.net/Qtq24/5/
If anyone has a better solution, I'd love to hear it as this feels like a bit of a hacky way of doing it but I guess no more hacky than having to use display: none; anyway.
Don't use style="display: none;" in SVG. You have it on the root <svg> element. Either visibility:hidden, height/width="0" or <defs> are better alternatives.
There used to be a bug in Firefox with gradient elements in symbols. That bug was fixed many versions ago now. The original code works as expected.
<svg width="30" height="21">
<symbol id="youtube" viewBox="0 0 400 281.641">
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="200.4204" y1="2.6162" x2="200.4204" y2="278.9292">
<stop offset="0" style="stop-color:#E52D27"/>
<stop offset="1" style="stop-color:#BF171D"/>
</linearGradient>
<path id="Triangle" fill="#FFFFFF" d="M159.845,191.73l106.152-54.999L159.845,81.348V191.73z"/>
<path id="The_Sharpness" opacity="0.12" fill-rule="evenodd" clip-rule="evenodd" fill="#420000" d="M159.845,81.348l93.091,62.162
l13.061-6.778L159.845,81.348z"/>
<g id="Lozenge">
<g>
<path fill="url(#SVGID_1_)" d="M392.928,62.226c0,0-3.839-27.073-15.617-38.995C362.371,7.583,345.626,7.506,337.947,6.59
c-54.975-3.974-137.441-3.974-137.441-3.974h-0.171c0,0-82.464,0-137.44,3.974c-7.68,0.916-24.419,0.993-39.364,16.641
C11.753,35.153,7.92,62.226,7.92,62.226s-3.929,31.792-3.929,63.583v29.805c0,31.791,3.929,63.582,3.929,63.582
s3.833,27.073,15.611,38.995c14.945,15.646,34.575,15.152,43.318,16.792c31.43,3.015,133.571,3.946,133.571,3.946
s82.552-0.124,137.526-4.099c7.679-0.915,24.424-0.993,39.363-16.64c11.778-11.922,15.617-38.995,15.617-38.995
s3.923-31.791,3.923-63.582v-29.805C396.851,94.017,392.928,62.226,392.928,62.226z M159.863,191.73l-0.018-110.383
l106.152,55.384L159.863,191.73z"/>
</g>
</g>
</symbol>
<use xlink:href="#youtube" width="30" height="21" />
</svg>