Inline SVG spriting and currentColor

A very common use for SVGs are icon systems. When building these I've always gone by a general rule: Add decorative images as background images in CSS and add meaningful images in HTML with appropriate alternative text. This time however, I needed to make an icon system with the flexibility of being in many different colours. So I turned to the currentColor variable in CSS. currentColor refers to the current text color of its element. It can be used on any declaration that accepts a color value.

We can style the fill of an SVG with currentColor like this: fill: currentColor; This means that the fill colour of the SVG will be inherited from the color value of its parent. It saves time and keeps CSS to a minimum. Here's a definition of currentColor from MDN's website:

The currentColor keyword represents the calculated value of the element's color property. It allows to make the color properties inherited by properties or child's element properties that do not inherit it by default.

It seemed pretty hard to apply currentColor to the SVG fill property of a background image, so I turned to inlining SVG sprites instead.

SVG spriting

The sprite is made up of an SVG container and several elements with content. The main elements we see in an SVG are: <svg>  <g> <defs><symbol> <use> <path> 

The <g> element stands for "group" and is used to group elements. Grouping elements is useful when applying styles or animations to a whole set of elements.

<defs> can be used to define elements that can be rendered elsewhere with a reference.

The <symbol> element is used to group elements together. It is never displayed, but it defines a template which can be rendered elsewhere with the <use> element. An important advantage of <symbol> is that it accepts a viewBox attribute enabling more control over the positioning of its content inside its container. The viewBox attribute contains a value of four numbers: min-xmin-ywidth and height.

<use> element allows us to render an element defined elsewhere. It requires a reference to the element passing an ID into the xlink:href attribute, like this xlink:href="#icon-twitter"

 <path> is probably the most powerful as it makes up the shape itself. There's a lot more to understand about this, here's MDN's description.

This is what my sprite looks like:

<svg aria-hidden="true" style="position: absolute; width: 0; height: 0;" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
 <defs>
     <symbol id="icon-book" width="100%" height="100%" viewBox="0 0 48 48">
         <title>Book</title>
         <path d="M0 42a4 4 0 0 0 4 4h36a4 4 0 0 0 4-4v-6h1.8c1.22 0 2.2-.98 2.2-2.2V23.2c0-1.22-.98-2.2-2.2-2.2H44v-6a4 4 0 0 0-4-4H7c-1.1 0-2-.9-2-2s.9-2 2-2h29.5a2.5 2.5 0 0 0 0-5H4a4 4 0 0 0-4 4v36zm34-13.5a2.5 2.5 0 0 1 5 0 2.5 2.5 0 0 1-5 0z"/>
     </symbol>
     <symbol id="icon-locationPin" width="100%" height="100%" viewBox="0 0 20 20">
         <title>Location Pin</title>
         <path d="M10 2.01C7.238 2.01 5 4.237 5 7c0 4.773 5 11 5 11s5-6.228 5-11c0-2.76-2.238-4.99-5-4.99zm0 7.75a2.7 2.7 0 1 1 0-5.4 2.7 2.7 0 0 1 0 5.4z"/>
     </symbol>
     <symbol id="icon-mail" width="100%" height="100%" viewBox="0 0 20 20">
         <title>Mail</title>
         <path d="M1.574 5.286l7.5 4.03c.252.135.578.198.906.198.328 0 .654-.064.906-.2l7.5-4.028c.49-.263.95-1.286.054-1.286H1.52c-.896 0-.434 1.023.054 1.286zm17.04 2.203c-.556.288-7.388 3.848-7.728 4.026s-.578.2-.906.2-.566-.022-.906-.2-7.133-3.74-7.688-4.028c-.39-.204-.386.035-.386.22V15c0 .42.566 1 1 1h16c.434 0 1-.58 1-1V7.708c0-.184.004-.423-.387-.22z"/>
     </symbol>
 </defs>
</svg>

Even though the <symbol> elements and content do not get rendered, the <svg> element wrapping it still does, so I have hidden it by adding some styles (in the example above).

Now I can use the icons as many times as I like in a page using <use>  like below:

<svg class="Icon" aria-hidden="true">
    <use xlink:href="#icon-book"></use>
</svg>
<h4>Weekly classes</h4>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit.</p>

As the icon is used for decoration only (it does not add significant value), I have used <aria-hidden="true"> to make sure it is hidden from screen readers. However, if it was a meaningful image I would use <role="img"> instead and make sure that the description inside <title> offers a clear explanation of the image.

When I inspected the element in the browser for the first time, my question was: where is content of the <use> element displayed? What's rendered on the screen is the content from the symbol. <use> provides a clone of the symbol's content rather than actual content. So, the content lives in the Shadow DOM.

Finally, I can add the fill:currentColor;  rule to the .Icon class, which I added to the <svg> element above.

.Icon {
    fill: currentColor;
}

Codepens:

Reading: