Using SVG | CSS-Tricks (original) (raw)

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?

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 against 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

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 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 echo file_get_contents("kiwi.svg"); ?>

A little PHP-specific thing here… it was demonstrated to me that file_get_contents() is the correct function here, not include() or include_once() as I have used before. Specifically because SVG sometimes is exported with <?xml version="1.0" encoding="UTF-8"?> that as the opening line, which will cause the PHP parser to choke on it.

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

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 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 URL’s for SVG

Another way to use SVG’s is to convert them into Data URL’s. Data URL’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 network request.

Mobilefish.com has an online conversion tool for that base64ing them, but I generally don’t think that’s a good idea for SVG. 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.

This converter is my fav, as it leaves the SVG as mostly readable text:

As

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

As CSS

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

Relevant note here: regular CSS doesn’t care if you put quotes around the data URI, but Sass does, so I’ve quoted it above (Thx Oziel Perez).

As

<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!

Format for Data URI

All the examples above have base64 as the encoding, but data URI’s do not have to be base64. In fact in the case of SVG, it’s probably better NOT to use base64. Primarily because the native format of SVG is much more repetitive than base64 ends up, it gzips better.

<!-- base64 -->
data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL...

<!-- UTF-8, not encoded -->
data:image/svg+xml;charset=UTF-8,<svg ...> ... </svg>

<!-- UTF-8, optimized encoding for compatibility -->
data:image/svg+xml;charset=UTF-8,%3Csvg xmlns='http://...'

<!-- Fully URL encoded ASCII -->
data:image/svg+xml;charset=US-ASCII,%3Csvg%20xmlns%3D%22http%3A//...

Command line thingy for base64ing SVG:

@chriscoyier @hkfoster maybe you could take a shortcut with >>> echo -n `cat logo.svg` | base64 | pbcopy

— Benny Schudel (@bennyschudel) March 2, 2013

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.

And a drag-and-drop tool.

Automation Tools

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.