Tips for using SVGs in web projects
A brief Twitter exchange with Adam Bradley (of Focal Point, CDN Connect and Foresight.js fame) prompted this post.
SVGs have shown real promise for a few years. Plenty of folks have been evangelising their use and their benefits and these arguments are now more relevant than ever (their small file size and resolution independence is a perfect match for mobile high-resolution devices). Despite this, when trying to utilise them in large web projects there are a few pitfalls I’ve come across that I haven’t found to be documented (apologies in advance for when someone invariably points out they have and I just didn’t find it). This post therefore serves as a few pointers if you are looking to employ them in a web project.
Tools
The main tools for creating, editing and optimising SVGs are:
- Illustrator – expensive but designers are happy there, exported SVG is a little ‘flabby’.
- SVG Edit – browser based but rather capable. Designers hate it though so good luck getting anyone to use it.
- Sketch – inexpensive but the SVG support is pretty basic. Shame, if they went for the SVG side of things in a big way I think they could make big waves. Nice for basic shapes but little use otherwise.
- Inkscape – free but looks like a dogs back wheels so designers tend to gag on sight. However, it’s currently the gold standard in terms of its optimisation of SVGs from directly within a project. Despite this, I favour getting the vectors drawn (or re-drawn) in Illustrator and then optimising elsewhere. More details on that shortly.
SVG versions (beware Tiny 1.2 with MS browsers)
There are a number of different SVG formats available. The Adobe Illustrator help pages have a great overview:
SVG 1.0 and SVG 1.1 Suitable for SVG files that will be viewed on a desktop computer. SVG 1.1 is the full version of the SVG specification, of which SVG Tiny 1.1, SVG Tiny 1.1 Plus, and SVG Basic 1.1 are subsets.
SVG Basic 1.1 Suitable for SVG files that will be viewed on medium powered devices, such as handheld devices. Keep in mind that not all handhelds support the SVG Basic profile. As a result, selecting this option does not guarantee that the SVG file will be viewable on all handhelds. SVG Basic does not support nonrectangular clipping and some SVG filter effects.
SVG Tiny 1.1 and SVG Tiny 1.1+ Suitable for SVG files that will be viewed on small devices, such as mobile phones. Keep in mind that not all mobile phones support the SVG Tiny and SVG Tiny Plus profiles. As a result, selecting either of these options does not guarantee that the SVG file will be viewable on all small devices.
SVG Tiny 1.2 Suitable for SVG files that will be viewed on a variety of devices ranging from PDAs and mobile phones to laptops and desktop computers.
SVG Tiny does not support gradients, transparency, clipping, masks, symbols, patterns, underline text, strike through text, vertical text, or SVG filter effects. SVG Tiny Plus includes the ability to display gradients and transparency, but does not support clipping, masks, symbols, or SVG filter effects.
Furthermore, whilst ‘Tiny 1.2′ is widely supported on mobile devices (including iOS) good ol’ Microsoft don’t support it. Even IE10 on Surface does NOT support SVG Tiny 1.2 profile. Therefore if you need to support the 3 people that will own an MS Surface (that number will probably drop to 2 after a particularly disgruntled customer convinces a store manager to offer a full refund), you’ll need to avoid ‘Tiny 1.2’ format. Instead opt for SVG Basic 1.1 or SVG 1.1.
I got a little excited believing I could optimise SVG delivery by not only converting small SVGs to data URIs but also converting them to SVGZ files first (SVGZ being the gzipped version of an SVG). This simply fails. For example if creating an inline-image with Compass (for example, use background-image: inline-image("file.svg");
), it dutifully makes the conversion but the result fails to render.
Responsive SVGs need proportional dimensions (or CSS overrides)
One of the many benefits of SVGs is their ability to look pin sharp when zoomed (except as background CSS images in Firefox and IE, more of which shortly). However, most of the mainstream SVG capable editors export the SVGs with fixed dimensions. For example, open up an SVG in your text editor of choice. At the top you’ll see something like this:
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="96px" height="25px" viewBox="0 122.5 96 25" enable-background="new 0 122.5 96 25"
xml:space="preserve">
</svg>
If you want the SVG to scale to its containing element (for example the div it lives in) with no additional help, simply change the width and height values to %:
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="100%" height="100%" viewBox="0 122.5 96 25" enable-background="new 0 122.5 96 25"
xml:space="preserve">
</svg>
This way, whether it’s employed inline within an <img>
tag or as a background image in CSS, it will scale on zoom. However, be aware that both Firefox and Internet Explorer 9 and 10 (and IE Mobile) fail to re-render the SVG in CSS based backgrounds. If the user zooms into them they look terrible. Boo!
Careful with background positioning 100% SVGs
It’s also worth knowing that when an SVG with width and height set to 100% is used as a background image in CSS the background-position property does not work consistently. While WebKit uses the top of the containing element to position the background, both IE10 and Opera use the centre. Not great by any means. In this instance it’s necessary to either define width and height in the SVG or use the background-size property to explicitly set the size.
Hat tip to Jeremie in the comments, who provided this concise overview of the way SVGs get rendered by the browser:
Using SVG-as-image (meaning with an HTML img tag or as a CSS background) is always a bit complex but the width and height attributes can always be overwritten with CSS.
For an HTML img tag, the width and height attribute are always overwritten by the width and height CSS properties.
For a CSS background, the width and height attribute from the SVG file can be overwritten with the background-size property. This means it is possible to create SVG sprites that scale in a responsive way.
Despite that, as noted in the comment section, and alluded to above, I have found some minor inconsistency with the position of SVG image sprites across different browsers. Therefore I tend to prefer converting each SVG image to a data URI instead (creates pretty much the same size asset but means that the background image sprite doesn’t need to be moved around).
Comparative sizing
Using Illustrator (CS6), if you want graphics to appear at different sizes relative to one another, place them all in the same view box/canvas (opening an SVG in Illustrator usually places it in a default 960 x 560 canvas which will become the viewbox size).
SVG Optimisation
When using a lot of SVGs in a project (say 50+) its important to consider their optimisation. This is especially important if you intend to convert them to data URIs. Extraneous information in the SVGs will result in extraneous information in the CSS. What should we be looking for?
Image optimisation falls into two camps, visual optimisations and things that can be taken care of automagically. First the visual optimisations that should be done by the designer.
Can it be flatter?
Certain ‘things’ are expensive in KB terms with SVGs. For example, it stands to reason that as they are vector based, the more vector points a graphic contains, the larger the resultant SVG will be in KB terms. However, also pay attention to gradients and blurs. The more gradients can be replaced with flat colours, the smaller the resultant file is likely to be.
Remove hidden vector points
Remove any vector points that aren’t visually relevant. For example if only the tip of a star is showing behind something else, remove all the vector points that’s aren’t visible on the star. Less vector points == less file size. This kind of thing can really save KB across multiple SVGs.
Automagic optimisations
For some time I used Inkscape and its command line utilities to perform the SVG optimisation. However, I have now found that the quickest workflow comes from using a utility called SVGO. Doubled up with Grunticon (a fantastic tool the smart folks over at Filament Group put together) and Grunt (in case you don’t know Grunt is a JavaScript automated task runner) this enables typing one command ‘grunt’ to optimise any number of SVGs and then produce fallback PNG files, and data URI equivalent CSS files.
Anyway, that probably sounds more difficult than it actually is. If you want to try the little ‘mashup’ tool (I called it SVG-Gruntenstein) that I use go and grab it (full instructions there too) it here: https://github.com/benfrain/SVG-Gruntenstein
What difference does all this SVG optimisation make I hear you cry? Well, let’s take a look and see.
Example of optimisation savings
Here’s an example of the kind of economies that can be made, 91 SVG images, inlined into CSS:
- Prior to any optimisation, the CSS file including data URIs of all images was 436KB (Physical 620KB). Minified and gzipped (how large it would be ‘over the wire’) the CSS came out at 192KB.
- Next was a stage of expert ‘designer’ (in this instance it was a privilege to have someone far better than I with Illustrator clean up the SVGs by hand) optimisation. This involved removing unneeded vector points and general cleaning up (the SVGs originated from Fireworks and became SVGs thanks to Aaron Bealls excellent extension). This brought the size down to: 314KB (Physical 467KB).
- Finally, there was a stage of optimisation with Inkscape using the methods above. This took the size to: 257KB (Physical 420KB). Raw minified CSS was 472KB and gzipped it came out at 113KB. The optimisation process took the file size down to 58% of the un-optimised version.
Optimise with SVGO
As already mentioned, SVGO is a tool that can automate the optimisation of SVG files. It can be run from a GUI application, command line, or integrated into a grunt.js task (like SVG-Gruntenstein). If you want to rock it direct from the command line, here are a few pointers.
First of all you will need to install it (it’s node.js based so you will need that too – go to nodejs.org and click download and then clicky-clicky through the install). Now, once Node is installed, from the Terminal/iTerm2:
sudo npm install -g svgo
OK, now we are ready to optimise things.
From Terminal, assuming you have a folder full of SVGs, move to the folder above those SVGs. Then run the following command:
svgo -f YOUR-FOLDER --disable removeViewBox
-f flag is notifying SVGO that the thing we want optimising is a folder full of SVGs).
The --disable removeViewBox
part is disabling one of the default SVGO plugins (the remove ViewBox plugin).
With that command SVGO will automagically optimise all the SVGOs (and fast) and give nice feedback in the terminal about the savings for each SVG.
That’s all there is to it. Can’t recommend SVGO enough!
On SVG rendering and paint times
What difference does it make if the SVG in question has size set with the width and height attributes and yet is displayed differently?
I was curious if making the browser resize the SVG prior to rendering it would cause a significant increase in paint times.
For example, an SVG with width: 960px height: 560px
within that was actually being rendered to screen by the browser at 53px x 30px (using background-size
in the CSS).
These tests were run in Chrome Canary 27.0.1435.0 using the ‘Enable continuous page repainting’ option in dev tools.
There were 72 SVG images as CSS backgrounds that the browser was rendering to screen at a size of 53px x 30px.
The test was to run page repainting option for 20 seconds from clicking refresh and compare results. Results are in ms (milliseconds):
Test 1: SVGs at 960px x 560px (large resize down).
Result: 6.8 – 7.7
Test 2: SVGs at 60px x 35px (minor resize down).
Result: 6.6 – 8
Test 3: SVGs at 53px x 30px (actual size).
Result: 6.4 – 7.9
At this sort of speed I think it’s fairly safe to say there isn’t enough difference in paint times (if any) to be worth spending any time over. However, devices with more limited resource (mobile) may produce visible differences.
It’s interesting to note however how much faster things are with identical images saved out as PNGs and the test re-run:
Test 4: PNGs saved at 47px x 27px (minor resize needed)
Result: 2.3 – 3.7
That’s some difference. All things being equal, using PNGs instead of SVGs more than halves the paint time (presumably as there is no need for Chrome to rasterise the SVG prior to resizing).
Whether opting for SVG or PNG for performance, these results must be tempered against the fact that both these render times (SVG and PNG based) are still acceptable. Personally, if they were 10ms plus in Chrome desktop I would start to get concerned.
Summary
Hopefully this little report from the front-line will be helpful for any others looking to employ and optimise SVGs in web projects.
Hello, great article!
Just a ‘quick’ question, possible long answer 😉
I would like to create a website with an svg logo, but for browsers that don’t support it create a png fallback. What is the best possible solution today?
Thanks!