SVG `use` with External Reference, Take 2

SVG has a <use> element which essentially means: go find the chunk of SVG that has this #identifier and kinda clone it and put it right here. It's an essential indgredient to an SVG icon system. There are some things to know about this that we haven't covered before.

As a reminder, it looks like this:

<!-- Reference IN THIS SAME DOCUMENT -->
<svg>
  <use xlink:href="#icon-1"></use>
</svg>

<!-- EXTERNAL reference -->
<svg>
  <use xlink:href="sprite.svg#icon-1"></use>
</svg>

That that #icon-1 identifier likely references a symbol in that file, like...

<svg xmlns="http://www.w3.org/2000/svg">
  
  <symbol id="icon-1" viewBox="0 0 1024 1024">
    <title>Kinda like alt text</title>
    <path class="path-1" d="..."></path>
  </symbol>

  ...

</svg>

The beauty of an external reference is that it leverages browser cache well.

Also that it's really simple to work with. You put the correct file path to the file (the "SVG sprite") and reference an identifier and it just works. You can have your server set to serve that file with all the correct headers such that the browser will hang onto it like it would any other asset you want to cache.

The problem was IE, but it's being fixed.

Most browsers that supported inline SVG supported this, so it was almost usable, with the major exception of IE. But they have now fixed that in Microsoft Edge. Edge doesn't have massive market share yet, but it is the future on Windows, so eventually we'll be able to start using this without any extra work.

Because of the lack of perfect support, there are two major workaround choices.

  1. Include the SVG sprite in all the HTML documents themselves. Works great. Tends to be very fast. But... kinda bloats the page cache. And to get the best support, you need to include it at the top of the document, which means slightly delayed rendering for likely more important content. Or...
  2. Ajax for the sprite. Which then can leverage browser caching. It can be tricky to do this though without a bit of FONI though (Flash of No Icons).

Once we can start using external references directly, it's not exactly the same

This concept eluded me, and is why I wanted to write this article.

I thought the external reference was the ultimate solution because it could do everything inline SVG referencing SVG in the same document could do. But unfortunately, it can't. SVG references this way has it's own separate DOM. It goes beyond the regular Shadow DOM boundary that all <use> is subject to.

With this:

<svg class="icon-1">
  <use xlink:href="#icon-1"></use>
</svg>

You could write CSS (in the same stylesheet you use for the rest of the site) to color it:

/* This works.
  It will cascade this fill through the shapes,
  as long as there are no presentational fill 
  attributes on the shapes themselves. */
.icon-1 {
  fill: red;
}

You can still do this with an externally referenced <use>, actually. But you can't style individual shapes like you could before.

/* You could reach individual shapes 
   to style because they share the same DOM. 
   But this WON'T WORK with externally referenced SVG. */
.path-1 {
  fill: yellow;
}

/* This won't work either way,
   because it crosses a shadow DOM boundary */
.icon-1 /* ~shadow~ */ .path-1 {
  fill: yellow;
}

You can't get your hands on the internal shapes at all when you externally reference. For example, from the HTML document:

<script>
  var shape = document.querySelectorAll(".path-1");
  console.log(shape);
  // [ ] (empty set)
</script>

Here's a gist that kinda belabors the point.

It's still pretty cool.

The fact that cascading in a single color still works makes it pretty useful. Most icons tend to be single-color. And you can have different single colors still.

Weird Future Stuff

Tab Atkins in his crazy future thinking ways has documented some potential future ideas called SVG params:

.foo {
  background-image: url("http://example.com/image.svg" param(--color var(--primary-color)));
}

That's a CSS example, but presumably something inline SVG could use as well.