Inline SVG is one of those things that I’ve used for years, but never quite taken the time to understand. It’s easy enough to just copy it into an HTML page and hack it to bend it to my will. I’m very fond of inline SVG for icons in web applications for a few reasons
- It is relatively inexpensive, meaning you can create some pretty icons for image buttons without a heavy download and it compresses well.
- It scales to any size (except tiny, and what qualifies as tiny depends on how complex your image is, as in you’re not going to have a lot of detail in a pixel?).
- You can modify it dynamically with CSS or JavaScript.
I’ve seen some alternatives proposed by developers over the years to scratch some of the same itch, but not anything I like as much as inline SVG, for instance:
- Custom font-based solutions like Font Awesome: the problem with these, for me, is that many enterprises block custom font downloads in group policy, because of very real security concerns.
- Emoticons – size-wise, these are certainly appealing, you get some nice images for only a character each, and they’re built into the browser, no image download required. But you have a limited set of icons to choose from, and if they don’t have what you need, you find yourself using an image that doesn’t quite match the action of your button, and that can understandably confuse your users. And historically, different browsers used different characters for the same icon, as in they got ahead of the spec. And ultimately, to make everybody happy, the spec often ended up using either character for that image, which is messy.
So I’ll stick with inline SVG, and given that it’s well past time I dug a little deeper into understanding them. This blog post is my attempt to get started with that.
So now that we’re settled on SVG, let’s take a look at some:
1 2 3 4 5 6 7 8 | <svg id="mysvg" viewBox="0 0 1024 1024" class="mysvg"> <path d="M 928 0 h -832 c -52.8 0 -96 43.2 -96 96 v 832 c 0 52.8 43.2 96 96 96 h 832 c 52.8 0 96 -43.2 96 -96 v -832 c 0 -52.8 -43.2 -96 -96 -96 Z M 896 648 c 0 137 -111.4 248 -249.4 248 h -268.8 c -138 0 -249.8 -111 -249.8 -248 v -272 c 0 -137 111.8 -248 249.8 -248 h 125.8 c 138 0 248.4 103 248.4 240 c 1.8 25.6 25 48 51.2 48 h 43 c 27.6 0 49.6 29 49.6 56.4 v 175.6 Z" /> <path d="M 704 640 c 0 35.2 -28.8 64 -64 64 h -256 c -35.2 0 -64 -28.8 -64 -64 v 0 c 0 -35.2 28.8 -64 64 -64 h 256 c 35.2 0 64 28.8 64 64 v 0 Z" /> <path d="M 576 384 c 0 35.2 -28.8 64 -64 64 h -128 c -35.2 0 -64 -28.8 -64 -64 v 0 c 0 -35.2 28.8 -64 64 -64 h 128 c 35.2 0 64 28.8 64 64 v 0 Z" /> </svg> |
This is the format of an SVG file, but you can also copy and paste it directly into your HTML and that’s what inline SVG looks like. This will produce a blog icon that looks like:
And if you’re new to the concept, you’re probably saying to yourself “really, that’s what you want me to put into my nice clean HTML? And I’m supposed to figure out what all that garbage is inside those paths and code that by hand? That ain’t happening.” To which I say, it ain’t happening for me either. But just FYI, it isn’t rocket science either. What you’re defining inside an SVG is lines/curves, generally connected together to form some shape(s), possibly some geometric stuff like circles and ellipses, and properties like stroke (the color of the line) and fill (the inside color of a shape).
And nobody I know codes these by hand. If you’re just getting started with these, you can download tens of thousands of them from the web. You may have to register on a site to download SVG files. You may even have to pay a few bucks to download more than a few of them. You can also produce your own, but you use a vector graphics image editor like Adobe Illustrator. It’s not that hard, lots of Youtube content to show you how to get started.
The next thing we need to talk about is the viewBox property. This property just defines the top/left coordinates of my image inside the SVG, and the width and height of my image. This makes a lot more sense when storing the SVG in a separate file, as it allows you to put multiple images inside a single file, and slice them out much like the mulit-image sprites we’ve been using in HTML since a little before Moses parted the red sea. For inline SVG, I think it makes more sense to always start your image at top/left 0/0 and 1024×1024. I personally like a big canvas, which makes it easier to do fine details on more complex images, and I can always resize them using CSS. In fact, that’s what I’ve already done above, a 1024×1024 image would really screw up the look and feel of this blog post, so I’ve applied the following CSS:
1 2 3 4 | .mysvg { width: 256px; height: 256px; } |
Note that the SVG above has class=”mysvg“, so I just reference that class in my CSS just like I would do with any other HTML element. And I’ve just resized the SVG to 256×256. I can scale it as much as I like with no loss of clarity (within limits when shrinking). For instance, if I change the style to:
1 2 3 4 | .mysvg { width: 4px; height: 4px; } |
I get something that looks like this:
Which is…I think not good is the technical term? For all intents and purposes, this is just a bullet because 4×4 is just not a big enough canvas to adequately show the details of this image. Interestingly though, if you hit Ctrl+ enough times (8 to 10 times) to zoom in the browser, you’ll see that it is still in fact a blog icon, it just can’t convey the details of the image at 4×4. So the detail is still technically there, but at 16 pixels that knowledge is very little consolation.
Now lets look at one more demonstration about the viewBox property. If I change it to viewBox=”512 512 500 500“, I get something like:
Deconstructing the viewBox, the first two numbers say that my image starts at coordinates x=512, y=512, which since my image is 1024×1024, means the dead center of my image. So at best, I can only show the lower left quarter of my image. But I’m actually showing a little less than that because the second two parameters of the viewBox say to only display 500×500 of the image, so I’m clipping 12 pixels off the right and bottom of the image just to make the point. That’s why the rounded corner on the lower right doesn’t look right.
Now you may be wondering why, if we’re only displaying a little under a quarter of the image, the result looks to be about the same size of my original icon. The reason is that we used the same class=”mysvg” as our original image, the only thing I changes was the viewBox. Which means that whatever slice of the image we choose to display, it will be scaled to 256×256 by the above CSS (not that CSS, the one before that).
And if you’re wondering why you need to know all these details about the viewBox, the answer is because for inline SVG, you really need to specify a viewBox. For instance, If I take the same SVG but remove both the class and viewBox properties, I get:
But I really don’t know what you’re seeing above, I only know what it looks like in my Edge Chromium browser, which isn’t great. Somehow, it’s decide that my intent was to display it with viewBox=”0 0 300 150“. Somehow I don’t see it as likely that 300×150 is some default from the spec. And it isn’t calculated by my paths, because it’s clipping most of my image. So I assume with no viewBox, what you’ll see is going to be pretty browser specific and you can’t count on a sensible decision by the browser (hehe, that’s an oxymoron if I ever saw one).
Also, I gather, if you don’t specify a viewBox, you can’t count on CSS scaling to properly work on all parts of the image.
Finally, the reason it needs to be inline SVG is because if the SVG is read in from a file, the individual parts of the SVG are not part of the DOM, so you can’t apply CSS to them.
You can also specify things like the stroke and fill of the SVG in CSS, which changes the color of the lines and the color of the fill area respectively, so if I add:
1 2 3 4 5 6 7 8 9 10 11 12 | .mysvg3 { height: 32px; width: 32px; fill: rgb(252,85,32); margin: 5px; } .mysvg4 { height: 32px; width: 32px; fill: darkblue; margin: 5px; } |
I get something like:
Now that’s a blog icon. Uhh, ahhh…color! Pretty exciting, eh?
Now, how do I get that same image in a page twice. You could do:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <svg id="mysvg" viewBox="0 0 1024 1024" class="mysvg3"> <path d="M 928 0 h -832 c -52.8 0 -96 43.2 -96 96 v 832 c 0 52.8 43.2 96 96 96 h 832 c 52.8 0 96 -43.2 96 -96 v -832 c 0 -52.8 -43.2 -96 -96 -96 Z M 896 648 c 0 137 -111.4 248 -249.4 248 h -268.8 c -138 0 -249.8 -111 -249.8 -248 v -272 c 0 -137 111.8 -248 249.8 -248 h 125.8 c 138 0 248.4 103 248.4 240 c 1.8 25.6 25 48 51.2 48 h 43 c 27.6 0 49.6 29 49.6 56.4 v 175.6 Z" /> <path d="M 704 640 c 0 35.2 -28.8 64 -64 64 h -256 c -35.2 0 -64 -28.8 -64 -64 v 0 c 0 -35.2 28.8 -64 64 -64 h 256 c 35.2 0 64 28.8 64 64 v 0 Z" /> <path d="M 576 384 c 0 35.2 -28.8 64 -64 64 h -128 c -35.2 0 -64 -28.8 -64 -64 v 0 c 0 -35.2 28.8 -64 64 -64 h 128 c 35.2 0 64 28.8 64 64 v 0 Z" /> </svg> <svg id="mysvg" viewBox="0 0 1024 1024" class="mysvg4"> <path d="M 928 0 h -832 c -52.8 0 -96 43.2 -96 96 v 832 c 0 52.8 43.2 96 96 96 h 832 c 52.8 0 96 -43.2 96 -96 v -832 c 0 -52.8 -43.2 -96 -96 -96 Z M 896 648 c 0 137 -111.4 248 -249.4 248 h -268.8 c -138 0 -249.8 -111 -249.8 -248 v -272 c 0 -137 111.8 -248 249.8 -248 h 125.8 c 138 0 248.4 103 248.4 240 c 1.8 25.6 25 48 51.2 48 h 43 c 27.6 0 49.6 29 49.6 56.4 v 175.6 Z" /> <path d="M 704 640 c 0 35.2 -28.8 64 -64 64 h -256 c -35.2 0 -64 -28.8 -64 -64 v 0 c 0 -35.2 28.8 -64 64 -64 h 256 c 35.2 0 64 28.8 64 64 v 0 Z" /> <path d="M 576 384 c 0 35.2 -28.8 64 -64 64 h -128 c -35.2 0 -64 -28.8 -64 -64 v 0 c 0 -35.2 28.8 -64 64 -64 h 128 c 35.2 0 64 28.8 64 64 v 0 Z" /> </svg> |
And you get:
That works, but it’s pretty awful. Say you’re coding some social media type app and you want to add a like button. Ten posts to a page? Ten like buttons, each repeating the entire SVG from scratch? That’s not a great solution. That’s where symbol comes in. You can do:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <svg id="mysvg" viewBox="0 0 1024 1024"> <symbol id="blog-symbol" viewBox="0 0 1024 1024" style="display:none"> <path d="M 928 0 h -832 c -52.8 0 -96 43.2 -96 96 v 832 c 0 52.8 43.2 96 96 96 h 832 c 52.8 0 96 -43.2 96 -96 v -832 c 0 -52.8 -43.2 -96 -96 -96 Z M 896 648 c 0 137 -111.4 248 -249.4 248 h -268.8 c -138 0 -249.8 -111 -249.8 -248 v -272 c 0 -137 111.8 -248 249.8 -248 h 125.8 c 138 0 248.4 103 248.4 240 c 1.8 25.6 25 48 51.2 48 h 43 c 27.6 0 49.6 29 49.6 56.4 v 175.6 Z" /> <path d="M 704 640 c 0 35.2 -28.8 64 -64 64 h -256 c -35.2 0 -64 -28.8 -64 -64 v 0 c 0 -35.2 28.8 -64 64 -64 h 256 c 35.2 0 64 28.8 64 64 v 0 Z" /> <path d="M 576 384 c 0 35.2 -28.8 64 -64 64 h -128 c -35.2 0 -64 -28.8 -64 -64 v 0 c 0 -35.2 28.8 -64 64 -64 h 128 c 35.2 0 64 28.8 64 64 v 0 Z" /> </symbol> </svg> <svg class="mysvg3"> <use xlink:href="#blog-symbol" /> </svg> <svg class="mysvg4"> <use xlink:href="#blog-symbol" /> </svg> |
Symbol just lets me encapsulate and name some part of an SVG. I can then use that part by using a use element and referencing the symbol by name. I can use this to reuse the same paths within a given SVG, or I can reference the symbol in a totally different SVG, as I’ve done here twice. In this case, I put a different class on each, so I get two different colors just like above:
Now we’re talking.
Here’s a more complete example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 | <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>SVG Icons at various sizes</title> <style type="text/css"> .icon-outer { margin: 0 0 20px 0; } .svg-icon-googleplus { fill: rgb(222,80,68); } .svg-icon-facebook { fill: rgb(57,87,146); } .svg-icon-twitter { fill: rgb(28,160,242); } .svg-icon-blog { fill: rgb(252,85,32); } .svg-icon-linkedin { fill: rgb(10,102,194); } .svg-icon-github { fill: rgb(56,56,56); } .svg-icon-small { width: 16px; height: 16px; margin: 2px; border-radius: 3px; } .svg-icon-medium { width: 32px; height: 32px; margin: 2px; border-radius: 6px; } .svg-icon-large { width: 64px; height: 64px; margin: 2px; border-radius: 9px; } </style> </head> <body> <div class="icon-outer" style="display: none;"> <svg xmlns="http://www.w3.org/2000/svg"> <symbol id="svg-googleplus" viewBox="0 0 1024 1024"> <title>Google +</title> <path d="M 512 0 c -282.8 0 -512 229.2 -512 512 s 229.2 512 512 512 s 512 -229.2 512 -512 s -229.2 -512 -512 -512 Z M 384 768 c -141.6 0 -256 -114.4 -256 -256 s 114.4 -256 256 -256 c 69.2 0 127 25.2 171.6 67 l -69.6 66.8 c -19 -18.2 -52.2 -39.4 -102 -39.4 c -87.4 0 -158.8 72.4 -158.8 161.6 s 71.4 161.6 158.8 161.6 c 101.4 0 139.4 -72.8 145.2 -110.4 h -145.2 v -87.8 h 241.8 c 2.2 12.8 4 25.6 4 42.4 c 0 146.4 -98 250.2 -245.8 250.2 Z M 832 512 v 64 h -64 v -64 h -64 v -64 h 64 v -64 h 64 v 64 h 64 v 64 h -64 Z" /> </symbol> <symbol id="svg-facebook" viewBox="0 0 1024 1024"> <title>Facebook</title> <path d="M 928 0 h -832 c -52.8 0 -96 43.2 -96 96 v 832 c 0 52.8 43.2 96 96 96 h 416 v -448 h -128 v -128 h 128 v -64 c 0 -105.8 86.2 -192 192 -192 h 128 v 128 h -128 c -35.2 0 -64 28.8 -64 64 v 64 h 192 l -32 128 h -160 v 448 h 288 c 52.8 0 96 -43.2 96 -96 v -832 c 0 -52.8 -43.2 -96 -96 -96 Z" /> </symbol> <symbol id="svg-twitter" viewBox="0 0 1024 1024"> <title>Twiter</title> <path d="M 1024 226.4 c -37.6 16.8 -78.2 28 -120.6 33 c 43.4 -26 76.6 -67.2 92.4 -116.2 c -40.6 24 -85.6 41.6 -133.4 51 c -38.4 -40.8 -93 -66.2 -153.4 -66.2 c -116 0 -210 94 -210 210 c 0 16.4 1.8 32.4 5.4 47.8 c -174.6 -8.8 -329.4 -92.4 -433 -219.6 c -18 31 -28.4 67.2 -28.4 105.6 c 0 72.8 37 137.2 93.4 174.8 c -34.4 -1 -66.8 -10.6 -95.2 -26.2 c 0 0.8 0 1.8 0 2.6 c 0 101.8 72.4 186.8 168.6 206 c -17.6 4.8 -36.2 7.4 -55.4 7.4 c -13.6 0 -26.6 -1.4 -39.6 -3.8 c 26.8 83.4 104.4 144.2 196.2 146 c -72 56.4 -162.4 90 -261 90 c -17 0 -33.6 -1 -50.2 -3 c 93.2 59.8 203.6 94.4 322.2 94.4 c 386.4 0 597.8 -320.2 597.8 -597.8 c 0 -9.2 -0.2 -18.2 -0.6 -27.2 c 41 -29.4 76.6 -66.4 104.8 -108.6 Z" /> </symbol> <symbol id="svg-blog" viewBox="0 0 1024 1024"> <title>Blog</title> <path d="M 928 0 h -832 c -52.8 0 -96 43.2 -96 96 v 832 c 0 52.8 43.2 96 96 96 h 832 c 52.8 0 96 -43.2 96 -96 v -832 c 0 -52.8 -43.2 -96 -96 -96 Z M 896 648 c 0 137 -111.4 248 -249.4 248 h -268.8 c -138 0 -249.8 -111 -249.8 -248 v -272 c 0 -137 111.8 -248 249.8 -248 h 125.8 c 138 0 248.4 103 248.4 240 c 1.8 25.6 25 48 51.2 48 h 43 c 27.6 0 49.6 29 49.6 56.4 v 175.6 Z" /> <path d="M 704 640 c 0 35.2 -28.8 64 -64 64 h -256 c -35.2 0 -64 -28.8 -64 -64 v 0 c 0 -35.2 28.8 -64 64 -64 h 256 c 35.2 0 64 28.8 64 64 v 0 Z" /> <path d="M 576 384 c 0 35.2 -28.8 64 -64 64 h -128 c -35.2 0 -64 -28.8 -64 -64 v 0 c 0 -35.2 28.8 -64 64 -64 h 128 c 35.2 0 64 28.8 64 64 v 0 Z" /> </symbol> <symbol id="svg-linkedin" viewBox="0 0 1024 1024"> <title>LinkedIn</title> <path d="M 928 0 h -832 c -52.8 0 -96 43.2 -96 96 v 832 c 0 52.8 43.2 96 96 96 h 832 c 52.8 0 96 -43.2 96 -96 v -832 c 0 -52.8 -43.2 -96 -96 -96 Z M 384 832 h -128 v -448 h 128 v 448 Z M 320 320 c -35.4 0 -64 -28.6 -64 -64 s 28.6 -64 64 -64 c 35.4 0 64 28.6 64 64 s -28.6 64 -64 64 Z M 832 832 h -128 v -256 c 0 -35.4 -28.6 -64 -64 -64 s -64 28.6 -64 64 v 256 h -128 v -448 h 128 v 79.4 c 26.4 -36.2 66.8 -79.4 112 -79.4 c 79.6 0 144 71.6 144 160 v 288 Z" /> </symbol> <symbol id="svg-github" viewBox="0 0 1024 1024"> <title>GitHub</title> <path d="M 512.008 12.642 c -282.738 0 -512.008 229.218 -512.008 511.998 c 0 226.214 146.704 418.132 350.136 485.836 c 25.586 4.738 34.992 -11.11 34.992 -24.632 c 0 -12.204 -0.48 -52.542 -0.696 -95.324 c -142.448 30.976 -172.504 -60.41 -172.504 -60.41 c -23.282 -59.176 -56.848 -74.916 -56.848 -74.916 c -46.452 -31.778 3.51 -31.124 3.51 -31.124 c 51.4 3.61 78.476 52.766 78.476 52.766 c 45.672 78.27 119.776 55.64 149.004 42.558 c 4.588 -33.086 17.852 -55.68 32.506 -68.464 c -113.73 -12.942 -233.276 -56.85 -233.276 -253.032 c 0 -55.898 20.004 -101.574 52.76 -137.428 c -5.316 -12.9 -22.854 -64.972 4.952 -135.5 c 0 0 43.006 -13.752 140.84 52.49 c 40.836 -11.348 84.636 -17.036 128.154 -17.234 c 43.502 0.198 87.336 5.886 128.256 17.234 c 97.734 -66.244 140.656 -52.49 140.656 -52.49 c 27.872 70.528 10.35 122.6 5.036 135.5 c 32.82 35.856 52.694 81.532 52.694 137.428 c 0 196.654 -119.778 239.95 -233.79 252.624 c 18.364 15.89 34.724 47.046 34.724 94.812 c 0 68.508 -0.596 123.644 -0.596 140.508 c 0 13.628 9.222 29.594 35.172 24.566 c 203.322 -67.776 349.842 -259.626 349.842 -485.768 c 0 -282.78 -229.234 -511.998 -511.992 -511.998 Z" /> </symbol> </svg> </div> <div class="icon-outer"> <svg class="svg-icon-small svg-icon-googleplus"> <use xlink:href="#svg-googleplus" /> </svg> <svg class="svg-icon-small svg-icon-facebook"> <use xlink:href="#svg-facebook" /> </svg> <svg class="svg-icon-small svg-icon-twitter"> <use xlink:href="#svg-twitter" /> </svg> <svg class="svg-icon-small svg-icon-blog"> <use xlink:href="#svg-blog" /> </svg> <svg class="svg-icon-small svg-icon-linkedin"> <use xlink:href="#svg-linkedin" /> </svg> <svg class="svg-icon-small svg-icon-github"> <use xlink:href="#svg-github" /> </svg> </div> <div class="icon-outer"> <svg class="svg-icon-medium svg-icon-googleplus"> <use xlink:href="#svg-googleplus" /> </svg> <svg class="svg-icon-medium svg-icon-facebook"> <use xlink:href="#svg-facebook" /> </svg> <svg class="svg-icon-medium svg-icon-twitter"> <use xlink:href="#svg-twitter" /> </svg> <svg class="svg-icon-medium svg-icon-blog"> <use xlink:href="#svg-blog" /> </svg> <svg class="svg-icon-medium svg-icon-linkedin"> <use xlink:href="#svg-linkedin" /> </svg> <svg class="svg-icon-medium svg-icon-github"> <use xlink:href="#svg-github" /> </svg> </div> <div class="icon-outer"> <svg class="svg-icon-large svg-icon-googleplus"> <use xlink:href="#svg-googleplus" /> </svg> <svg class="svg-icon-large svg-icon-facebook"> <use xlink:href="#svg-facebook" /> </svg> <svg class="svg-icon-large svg-icon-twitter"> <use xlink:href="#svg-twitter" /> </svg> <svg class="svg-icon-large svg-icon-blog"> <use xlink:href="#svg-blog" /> </svg> <svg class="svg-icon-large svg-icon-linkedin"> <use xlink:href="#svg-linkedin" /> </svg> <svg class="svg-icon-large svg-icon-github"> <use xlink:href="#svg-github" /> </svg> </div> </body> </html> |
This example just uses everything I’ve talked about in this post to produce 6 social media icons in 3 different sizes, like so:
That’s about all the fun I can have with inline SVG in a single blog post. More accurately, that’s about all I know on the subject. As I discover more, I may write additional posts and turn this into a series.
References