Using SVG

Published by Chris Coyier

SVG is an image format for vector graphics. It literally means Scalable Vector Graphics. Basically, what you work with in Adobe Illustrator. You can use SVG on the web pretty easily, but there is plenty you should know.

Why use SVG at all?

  • Small file sizes that compress well
  • Scales to any size without losing clarity (except very tiny)
  • Looks great on retina displays
  • Design control like interactivity and filters

Getting some SVG to work with

Design something in Adobe Illustrator. Here's a Kiwi bird standing on an oval.

Notice the artboard is cropped up right agains the edges of the design. Canvas matters in SVG just like it would in PNG or JPG.

You can save the file directly from Adobe Illustrator as an SVG file.

As you save it, you'll get another dialog for SVG Options. I honestly don't know much about all this. There is a whole spec for SVG Profiles. I find SVG 1.1 works fine.

The interesting part here is that you can either press OK and save the file, or press "SVG Code..." and it will open TextEdit (on a Mac anyway) with the SVG code in it.

Both can be useful.

Using SVG as an <img>

If I save the SVG to a file, I can use it directly in an <img> tag.

<img src="kiwi.svg" alt="Kiwi standing on oval">

In Illustrator, our artboard was 612px ✕ 502px.

That's exactly how big the image will on the page, left to itself. You can change the size of it though just by selecting the img and changing its width or height, again like you could a PNG or JPG. Here's an example of that:

Check out this Pen!

Browser support

Using it this way has its own set of specific browser support. Essentially: it works everywhere except IE 8 and down and Android 2.3 and down.

If you'd like to use SVG, but also need to support these browsers that don't support using SVG in this way, you have options. I've covered different techniques in different workshops I've done.

One way is to test for support with Modernizr and swap out the src of the image:

if (!Modernizr.svg) {
  $(".logo img").attr("src", "images/logo.png");
}

David Bushell has a really simple alternative, if you're OK with JavaScript in the markup:

<img src="image.svg" onerror="this.onerror=null; this.src='image.png'">

SVGeezy can also help. We'll cover more fallback techniques as this article progresses.

Using SVG as a background-image

Similarly easy to using SVG as an img, you can use it in CSS as a background-image.

<a href="/" class="logo">
  Kiwi Corp
</a>
.logo {
  display: block;
  text-indent: -9999px;
  width: 100px;
  height: 82px;
  background: url(kiwi.svg);
  background-size: 100px 82px;
}

Notice we set the background-size to the size of the logo element. We have to do that otherwise we'll just see a bit of the upper left of our much larger original SVG image. These numbers are aspect-ratio aware of the original size. But you could use a background-size keywords like contain if you want to make sure the image will fit and can't know the parent image will be of the exact right size.

Browser support

Using SVG as background-image has its own special set of browser support, but it's essentially the same as using SVG as img. The only problem browsers are IE 8 and down and Android 2.3 and down.

Modernizr can help us here, and in a more efficient way than using img. If we replace the background-image with a supported format, only one HTTP request will be made instead of two. Modernizr adds a class name of "no-svg" to the html element if it doesn't support SVG, so we use that:

.main-header {
  background: url(logo.svg) no-repeat top left;
  background-size: contain;
}

.no-svg .main-header {
  background-image: url(logo.png);
}

Another clever progressive enhancement flavored technique for using SVG as background-image is by using it in conjunction with multiple backgrounds. SVG and multiple backgrounds have very similar browser support, so if the browser supports multiple backgrounds, it supports SVG, and the declaration will work (and override any previous declaration).

body {
  background: url(fallback.png);
  background-image: url(image.svg), none;
}

The problem with both <img> and background-image...

Is that you don't get to control the innards of the SVG with CSS like you can with the following two ways. Read on!

Using "inline" SVG

Remember how you can grab the SVG code right from Illustrator while saving if you want? (You can also just open the SVG file in a text editor and grab that code.) You can drop that code right into an HTML document and the SVG image will show up just the same as if you put it in an img.

<body>

   <!-- paste in SVG code, image shows up!  -->

</body>

This can be nice because the image comes over right in the document and doesn't need to make an additional HTTP request. In other words, it has the same advantages as using a Data URI. It has the same disadvantages too. A potentially "bloated" document, a big chunk of crap right in the document you're trying to author, and inability to cache.

If you're using a back end language that can go fetch the file and insert it, at least you can clean up the authoring experience. Like:

<?php include("kiwi.svg"); ?>

Optimize it first

Likely not a huge shocker, but the SVG that Adobe Illustrator gives you isn't particularly optimized. It has a DOCTYPE and generator notes and all that junk. SVG is already pretty small, but why not do all we can? Peter Collingridge has an online SVG Optimiser tool. Upload the old, download the new. In Kyle Foster's video, he even takes it the extra mile and removes line breaks after this optimization.

If you're even more hardcore, here is a Node JS tool for doing it yourself.

Now you can control with CSS!

See how the SVG looks a lot like HTML? That's because they are both essentially XML (named tags with angle brackets with stuff inside). In our design, we have two elements that make up the design, an <ellipse> and an <path>. We can jump into the code and give them class names, just like any other HTML element can have.

<svg ...>
  <ellipse class="ground" .../>
  <path class="kiwi" .../>
</svg>

Now in any CSS on this page we can control those individual elements with special SVG CSS. This doesn't have to be CSS embedded in the SVG itself, it can be anywhere, even in our global stylesheet <link>ed up. Note that SVG elements have a special set of CSS properties that work on them. For instance, it's not background-color, it's fill. You can use normal stuff like :hover though.

.kiwi {
  fill: #94d31b; 
}
.kiwi:hover {
  fill: #ace63c; 
}

Even cooler, SVG has all these fancy filters. For instance blurring. Chuck a filter in your <svg>:

<svg ...>
  ...
  <filter id="pictureFilter" >
    <feGaussianBlur stdDeviation="5" />
  </filter> 
</svg>

Then you can apply that in your CSS as needed:

.ground:hover {
  filter: url(#pictureFilter);
}

Here's an example of all that:

Check out this Pen!

Browser support

Inline SVG has it's own set of browser support, but again, it's essentially only an issue in IE 8 and down and Android 2.3 and down1.

One way to handle fallbacks for this type of SVG is:

<svg> ... </svg>
<div class="fallback"></div>

Then use Modernizr again:

.fallback { 
  display: none;
  /* Make sure it's the same size as the SVG takes up */
}
.no-svg .fallback { 
  background-image: url(logo.png); 
}

Using SVG as an <object>

If "inline" SVG just isn't your jam (remember it does have some legit drawbacks like being hard to cache), you can link to an SVG file and retain the ability to affect its parts with CSS by using <object>.

<object type="image/svg+xml" data="kiwi.svg" class="logo">
  Kiwi Logo <!-- fallback image in CSS -->
</object>

For the fallback, Modernizr detection will work fine here:

.no-svg .logo {
  width: 200px;
  height: 164px;
  background-image: url(kiwi.png);
}

This will work great with caching and actually has deeper support than using it any other way. But, if you want the CSS stuff to work, you can't use an external stylesheet or <style> on the document, you need to use a <style> element inside the SVG file itself.

<svg ...>
  <style>
    /* SVG specific fancy CSS styling here */
  </style>
  ...
</svg>

External stylesheets for <object> SVG

SVG has a way to declare an external stylesheet, which can be nice for authoring and caching and whatnot. This only works with <object> embedding of SVG files as far as I've tested. You'll need to put this in the SVG file above the opening <svg> tag.

<?xml-stylesheet type="text/css" href="svg.css" ?>

If you put that in your HTML, the page will barf and not even try to render. If you link up an SVG file that has that in it as an <img> or background-image, it won't barf, but it won't work (the SVG will still render though).

Data URI's for SVG

Another way to use SVG's is to convert them into Data URI's. Data URI's might not save you actual file size, but can be more efficient because the data is right there. It doesn't require an additional HTTPRequest.

Mobilefish.com has an online conversion tool for that. Simply paste in the contents of your SVG file and fill out the form and it will display the results in a textarea for you to copy. Remember to remove line breaks in the data it gives you back. It looks like pure gibberish:

You can use that anywhere we've talked about so far (except inline <svg> because that just doesn't make sense) Just put the gibberish where it says [data] in these examples.

As <img>

<img src="data:image/svg+xml;base64,[data]">

As CSS

.logo {
  background: url(data:image/svg+xml;base64,[data]);
}

As <object>

<object type="image/svg+xml" data="data:image/svg+xml;base64,[data]">
  fallback
</object>

And yep, if you have an embedded <style> in your SVG before you base64 it, it will work if you use it as an <object> still!

Command line thingy for base64ing SVG:

Or alternatively Mathias Bynens has some techniques:

Use openssl base64 < path/to/file.png | tr -d '\n' | pbcopy or cat path/to/file.png | openssl base64 | tr -d '\n' | pbcopy to skip writing to a file and just copy the base64-encoded output to the clipboard without the line breaks.

Automation Tools

  • grunticon:

    From a CSS perspective, it's easy to use, as it generates a class referencing each icon, and doesn't use CSS sprites.

    grunticon takes a folder of SVG/PNG files (typically, icons that you've drawn in an application like Adobe Illustrator), and outputs them to CSS in 3 formats: svg data urls, png data urls, and a third fallback CSS file with references to regular png images, which are also automatically generated and placed in a folder.

  • iconizr:

    A PHP command line tool for converting SVG images to a set of CSS icons (SVG & PNG, single icons and / or CSS sprites) with support for image optimization and Sass output.

Related Stuff

Kyle Foster's An Optimized SVG Workflow, which is worth an embed:

... and the follow up + slides.


1 And speaking of Android 2.3 browser, this. But if you absolutely have to support the native browser, this.


Wanna learn more about SVG?

I have a full course available called Everything You Need to Know about SVG that covers the whole spectrum of SVG from the perspective of a web designer and front end developer.