1

The Truth(tm) about encoding SVG in data URIs

 3 months ago
source link: https://www.phpied.com/truth-encoding-svg-data-uris/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

tl;dr: stop worrying and URL-encode #s only.

What?

So you want to have an SVG in CSS. Yup, using data URIs (lookie, a 2009 post). There are reasons not to do this in first place (caching, reuse), but hey, sometimes you're not in a position to make that call.

Base64-encode

So, one way to go is to base64-encode, like:

background: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciLz4=')

Drawbacks: Base64 makes the content larger by 25-30%. And a human code reader cannot tell what's in the image. Which is a nice SVG feature, readability.

URL-encode

The second way is use URL encoding:

background: url('data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%2F%3E')

Drawback: the image payload is even larger. All these %20 spaces quickly add up. It's a bit more readable though.

As-is

A quick testing in modern browsers suggests that the browser can understand the unencoded SVG perfectly well, so new solution: SVG as-is.

background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg"></svg>')

This works fine for this type of MVP SVG but, as I quickly found out, as soon as you add a color like fill="#bad" to the SVG, the # acts like a URL hash and everything after it is no longer part of the SVG. So the image is broken.

Selective URL-encoding

I was curious what other characters may brake the image and looked and asked around. In the webperf slack, Radu pointed to Sass/Bootstrap that's been around forever, battle-tested and so on, and escapes these characters:

< becomes %3c
> becomes %3e
# becomes %23
( becomes %28
) becomes %29

My hash is there, but the others? Digging through github I saw that the encoding was added 5 years ago containing initially only <>#. The parentheses were added later to avoid a bug in a CSS minifier.

So if we use a non-broken CSS minifier, we're left with <>#. I cannot find a reputable source for <> but the rumor is it's for IE support. Well, IE is no more. So we're left with only # to worry about.

For folks using old IEs, the worst that can happen is no background image. Meh, so be it. It's not like all of CSS is broken.

# encoding

And here's the conclusion: in this day and age, encode your # (replace with %28) and enjoy small payloads, readable SVGs and modern browser support.

Future-proof?

The only nagging thing is that MDN will tell you to URL-encode. That's the right way. The fact that browsers are tolerant may be only temporary. But, if browsers suddenly decide to be strict, that'd be a Web-breaking change. Because a zillion web pages have partially encoded SVGs, I mean Sass/Bootstrap is popular. And browsers go to great lengths to avoid breaking the web. So I think it's safe to assume this minimal #-encoding will work for a looong time.

p.s. And, of course, escape the quotes you use in the url(). I'd suggest using single quotes, so the SVG itself can use double quotes. All of these should be ok though:

background: url('data:image/svg+xml,<svg xmlns=\'http://www.w3.org/2000/svg\'></svg>');
background: url("data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\"></svg>");
background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg"></svg>');
background: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'></svg>");

p.p.s. Apologies about the cheesy "the Truth" in the title. I'm aiming to be a tad obnoxious to trick people into proving me wrong. The Truth is what I'm seeking even if I'm wrong temporarily.

Tell your friends about this post on Facebook and Twitter


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK