The SVG Re-<use>
Case
Case is that you want to re-use an svg
element independent from it's declaration. To display it more than once in your html
page flow. Or to apply a svg
element from a declaration external in your html
page.
In short course
- Put an
svg
element inside yourhtml
or an externalsvg
file. - Make that
svg
element invisible by giving it an attribute ofdisplay="none"
- Add a
symbol
element inside thesvg
element, giving it a uniqueid
attribute - Put whatever SVG element renders your SVG inside that
symbol
element - From your html element flow, use that
symbol
element by referencing it via<use xlink:href="relativePathToAnExternalSvgFileOrEmpty#theIdFromTheSymbol"
, sorrounding it with a anothersvg
element
The no-scaling case
The following contained svg elements (text
and path
) are defined in a <symbol>
element, which means that they won’t be directly rendered on the SVG canvas. The SVG document itself, where you defined that symbol, will still be rendered on the page, even if it contains no rendered elements. In order to avoid that, make sure you set display: none
on the first SVG. Else it get's displayed and therefore rendered as a HTML Replaced Element.
<svg display="none">
<symbol id="v-line">
<text fill="white" x="50" y="-6" transform="rotate(90)" text-anchor="middle">100 px</text>
<path stroke="white" stroke-width="2" d="M20,0V+100Z"/>
</symbol>
</svg>
When you <use>
a symbol
element, the contents of that element are copied, as is, into wherever you place the <use>
in the page. This copy is a called a Shadow DOM. The Shadow DOM references a document fragment which is basically just another subtree of nodes elsewhere having it's own scope encapsulated.
Now we can render multiple instances of the icon by a <use>
element with a xlink:href="#theIdFromTheSymbol"
attribute, whose value references the id
of the symbol
element above.
<svg class="line" style="vertical-align: top;">
<use href="#v-line"/>
</svg>
<!--
--><svg class="line" style="vertical-align: baseline;">
<use href="#v-line"/>
</svg>
.line {
display: inline;
width: 24px;
height: 100px;
font-size: 0.125em;
font-weight: 1000;
color: $color-block-attent;
}
Note that neither the viewbox
nor the width
and height
attributes on the <svg>
element itself is set. Hence the SVG has no scaling applied and all measures are interpreted as absolute in pixels. When the width and height were specified into a css class referenced via the class
attribute, the SVG was rendered in a rectangle that is 24px wide and 100px tall.
The scaling case
Do not drop the width and height in the svg element
So why would you want to set explicit dimensions on your <svg>
when what you’re really trying to do is make the SVG fluid, right?
Ideally, we should make our SVGs responsive while also catering for any worst case scenarios where our styles are ignored or simply not applied for any reason. By taking advantage of the SVG style inheritance tree, we can make our SVGs responsive while simultaneously planning for worst case scenarios, and providing a decent, less broken, fallback.
- Browsers that don’t support the applied technique
- Slow on loading CSS
- SVG displayed as replaced element
- SVG displayed at 100% x 100% of browser viewport
- Default fallback on SVG styles; fill and stroke will be black
In order to avoid the no-CSS scaling issue, all you need to do is NOT remove the width and height attributes from the SVG. And, specify your desired width and height values in the CSS.
What width and height to apply as default
Use the same dimensions as the viewBox dimensions, but they don’t have to be identical. But they have to be in the same ratio as the viewBox
dimension.
.icon {
width: 100px;
height: 125px;
}
What width and height to apply as overriding dimensions
Let it be fluid and fill out its container’s width?
svg {
width: 100%;
height: 100%;
}
Scale it in proportion to the text it is inline with?
svg {
width: 1em;
height: 1em;
/* maybe even... */
color: currentColor;
}
How to style each re-use differently
To re-use an element mutliple times and style each occurance differently; leverage CSS Variables.
Do it by overriding SVG fill
colors with CSS Variables.
<svg style="display: none">
<symbol id="robot" viewBox="0 0 340 536">
<path d="..." fill="#fff" />
<path d="..." fill="#D1312C" />
<path d="..." fill="#1E8F90" style="fill: var(--primary-color)" />
<path d="..." fill="#1E8F90" style="fill: var(--primary-color)" />
<path d="..." fill="#fff" />
<path d="..." fill="#6A4933" style="fill: var(--tertiary-color)" />
<path d="..." fill="#1E8F90" style="fill: var(--primary-color)" />
<path d="..." fill="#6A4933" style="fill: var(--tertiary-color)" />
<path d="..." fill="#fff" />
<path d="..." fill="#6A4933" style="fill: var(--tertiary-color)" />
<path d="..." fill="#F2B42B" style="fill: var(--secondary-color)" />
<path d="..." fill="#fff" />
<!-- rest of the shapes -->
</symbol>
</svg>
References
https://24ways.org/2014/an-overview-of-svg-sprite-creation-techniques/
https://tympanus.net/codrops/2015/07/16/styling-svg-use-content-css/