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):
<g>
wrappers that surround the gradient and the path, so the whole of the symbol is just <path>-<path>-<linearGradient>-<path>
<defs>
container<defs>
and also moving it to the top of the SVG file, i.e. outside the bounds of the YouTube-specific <symbol>
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.
To use a gradient, we have to reference it from an object's fill or stroke attributes. This is done the same way you reference elements in CSS, using a url . In this case, the url is just a reference to our gradient, which I've given the creative ID, "Gradient". To attach it, set the fill to url(#Gradient) , and voila!
SVG provides for two types of gradients: linear gradients and radial gradients. Once defined, gradients are then referenced using 'fill' or 'stroke' properties on a given graphics element to indicate that the given element shall be filled or stroked with the referenced gradient.
The x1, x2, y1,y2 attributes of the <linearGradient> tag define the start and end position of the gradient. The color range for a gradient can be composed of two or more colors. Each color is specified with a <stop> tag. The offset attribute is used to define where the gradient color begin and end.
To create a sprite in SVG we use the <symbol> tag and apply an ID for referencing later and the viewBox attribute for defining the canvas size. Inside of the symbol icon we create our shapes, text and any other elements that make up our icon.
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>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With