How SVG Fragment Identifiers Work

Avatar of Chris Coyier
Chris Coyier on (Updated on )

📣 Freelancers, Developers, and Part-Time Agency Owners: Kickstart Your Own Digital Agency with UACADEMY Launch by UGURUS 📣

I’ve talked a good bit about SVG’s <use> around here and using it to build an icon system. The beauty of <use> is that you can reference just a part of some SVG defined elsewhere and draw just that part somewhere else. That ability allows you to build a whole system out of it, solving the “many images in one request, because that’s super efficient” problem that we’ve solved in the past with CSS sprites and icon fonts.

But <use> means inline SVG. It doesn’t help you when you want to use a part of a larger SVG in SVG-as-<img> or SVG-as-background-image. That’s where fragment identifiers come in.

Getting the SVG Ready For It

One way to do this is to lay out the SVG (sprite, I guess, let’s just call it a sprite) like a graphical “CSS” sprite.

We’re specifically doing it this way, because we ultimately are going to shift some viewBox numbers around to only reveal a part of this image, just like we used to do with CSS sprites.

In this little mini demo, we’re using three icons that are 32×32 each. So the document is 32×96. We could think of the viewBox shenanigans this way:

Whole document viewBox 0 0 32 96
Just show top icon viewBox 0 0 32 32
Just show middle icon viewBox 0 32 32 32
Just show bottom icon viewBox 0 64 32 32

The viewBox attribute goes: left, top, width, height. Note that second attribute, the top, notching up by 32 each time. We’re just showing a different part of the whole document.

Adding those special viewBox’s into the SVG itself

You can drop those special, specific viewBox values into the SVG in a <view> element, that is specifically for this:

<view id="icon-clock-view" viewBox="0 0 32 32" />
<view id="icon-heart-view" viewBox="0 32 32 32" />
<view id="icon-arrow-right-view" viewBox="0 64 32 32" />

Now we’ll be able to reference and use those values from elsewhere.

The <view> elements can be stand-alone like this, or they can actually wrap other elements, in which case that viewBox takes hold when the ID matches, so

<!-- this viewBox takes over if current fragment identifer is #match-me -->
<view id="match-me" viewBox="0 64 32 32">
  <rect ...>
</view>

Demo of this kind of thing in the spec.

Syntax for HTML

To apply those special viewBox values to SVG-as-<img>, you could do it like this:

<!-- top icon -->
<img src="sprite.svg#svgView(viewBox(0, 0, 32, 32))" alt="">

Or, if you set up <view> elements already, you can just reference them by name:

<!-- middle icon -->
<img src="sprite.svg#icon-heart-view" alt="">

Syntax for CSS

You can declare a special viewBox right in the image path in the CSS:

.icon-clock {
  background: url("sprite.svg#svgView(viewBox(0, 0, 32, 32))") no-repeat;
}

Or reference a <view> element if you have those set up:

.icon-clock {
  background: url(sprite.svg#icon-clock-view) no-repeat;
}

Although… if you’re using SVG through CSS this way and went through the trouble to set up the SVG all spaced out like this, you might wanna just use the CSS sprite technique and shift the background around:

.icon-heart {
  background: url("sprite.svg") no-repeat;
  background-size: 32px 96px;
  background-position: 0 -32px;
}

I just wanna stack the icons on top of each other.

If the icons all have the same viewBox and you essentially just want to hide/show them as needed, this can get a little easier.

Design them all in the same space (or use a build tool that does it or whatever). Here, I’m putting each icon in its own group with a unique ID.

The trick to making the hide/show work then is embedding some CSS that 1) hides everything 2) reveals the one with a matching fragment identifier. CSS is up for the job, because of the :target selector.

All together in the SVG:

<defs>
  <style>
    g {
      display: none;
    }
    g:target {
      display: inline;
    }
  </style>
</defs>

<g id="icon-clock">
  <path d="M20.6,23.3L14,16.7V7.9h4v7.2l5.4,5.4L20.6,23.3z M16-0.1c-8.8,0-16,7.2-16,16s7.2,16,16,16s16-7.2,16-16S24.8-0.1,16-0.1z
		 M16,27.9c-6.6,0-12-5.4-12-12s5.4-12,12-12s12,5.4,12,12S22.6,27.9,16,27.9z"/>
</g>
<g id="icon-heart">
  <path d="M32,11.2c0,2.7-1.2,5.1-3,6.8l0,0L19,28c-1,1-2,2-3,2s-2-1-3-2L3,18c-1.9-1.7-3-4.1-3-6.8C0,6.1,4.1,2,9.2,2
		c2.7,0,5.1,1.2,6.8,3c1.7-1.9,4.1-3,6.8-3C27.9,1.9,32,6.1,32,11.2z"/>
</g>
<g id="icon-arrow-right">
  <path d="M32,15.9l-16-16v10H0v12h16v10L32,15.9z"/>
</g>

Browser Support

Can I Use tracks support for fragment identifier. Unfortunately, it’s not quite as simple as works-or-not because depending on the browser it might work in HTML and not CSS or have quirks.

Here’s my test page:

See the Pen SVG Fragment Identifiers in HTML and CSS by Chris Coyier (@chriscoyier) on CodePen.

I haven’t made super detailed browser support notes for this, but here are the highlights:

  • Firefox does everything right.
  • IE 11 does everything right. IE 9 & 10 are a little weird with background-position (squishing) but otherwise works with all.
  • Current versions of Chrome/Safari/Opera (38/8/25) handle all the HTML <img> techniques well, but none of the CSS techniques, including the background-position one. Pre-Blink Opera was the same, interestingly enough. Update: If the <svg> referenced has correct width and height attributes on it, all the methods work as expected. At least it does now, as of Chrome (56) / Opera.
  • The only thing iOS 8.1 can handle is <img> referencing a <view>.
  • The only thing Android 4.4 gets right is background-position (the one that’s not really using fragment identifiers at all). Android 5 matches current Chrome/Safari/Opera as above.

Linkage!