Skip to main content

SVG 101: Anatomy

Welcome back to SVG 101. Last month, I covered optimization, best practices for embedding, and the viewport. Essentially, everything you need to know about getting set up with Scalable Vector Graphics (SVGs) on the front end of a website / webapp. If you missed it, you can read it here.

In part two of this series, I’ll give an overview of (SVG) ‘anatomy’: Object primitives, their main attributes, masking, clipping, switch statements, and filters.  With that said, now we can begin!

Object primitives

These are just like HTML, where we have primitives such as <div>, <p>, <input>, <button>, <form>, and all their friends. The SVG equivalents are, as you might expect, more graphically orientated. As they don’t have a z-index to play with, their layering is defined exclusively by the document order – but they do have many other attributes for us to set, we’ll get into some of them in the next section. For now, here’s a list of the important primitives.

<line x1="0" y1="0" x2="5" y2="5" stroke-width="1" stroke="blue" stroke-linecap="round" stroke-dasharray="8,3,2,18" />

  • stroke-linecap: this styles the endpoints of the line. It extends from the end of the stroke (it’s additive).
  • stroke-dasharray: this defines a dotted line in a very flexible manner. The first value is “stroke on” for that number of pixels, the next value is stroke off, and so on. For multiple colors we’ll have to start placing lines under each other and possibly doing a few calculations, but it’s doable!

<rect x="0" y="0" width="5" height="5" fill="red" stroke="blue" stroke-width="5" />
This is your basic rectangle (or square as in this example).

<circle cx="80" cy="50" r="40"/>
cx and cy define the center, r sets the radius. You could think of cx as standing for “center x”, as it’s the “x” coordinate for the center of the circle – see what I did there? Same goes for cy.

<ellipse cx="100" cy="50" rx="100" ry="50" fill="red" />
This is basically the same as a circle but with an additional radius: rx is the X axis radius, ry is the Y axis radius.

<text x="15" y="45" font-size="40" fill="red" font-family="Verdana" font-size="55" text-anchor="middle">some text</text>
Unlike the other object primitives, the coordinates for text define the bottom left point from which the glyphs align their baseline. The bigger the font size, the higher up from that point the text reaches. It can also take styling in the same was as other vector shapes: fill, stroke, stroke-width and so on but if you’re looking for intricate detailing you’re probably best building the text as a vector graphic. One handy trick; changing the orientation of the glyphs, for example: setting style="writing-mode: tb; glyph-orientation-vertical: 90;" will give you vertically orientated text. For more detail on text in SVG’s check out

<image xlink:href="filename" x="100" y="100" width="150" height="200" />
This is basically the same as the <rect> primitive, but with an image link. This similarity goes as far as the default scale & position attributes being 0. Unless you set them, the image won’t appear. Keep an eye out for this as it’s a common mistake.

<path d="..." stroke="blue" fill="red" stroke-width="1">
The d attribute contains the drawing instructions for the path. It’s kind of a language all to itself. M defines the start point, its coordinates are defined by the following two numbers. That is followed by various forms of “movements” which are defined by coordinates and path types:

  • L Linearly (the default) goes straight from point to point.
  • Q Quadratically. The first coordinates define the initial direction of the line, the second coordinates define the ending point (of this section of the path).
  • C Cubic Bézier. These are the curves we see in Illustrator, a start, two control points, and an end. If we were to programmatically draw a series of smooth curves we would have to make sure the controls adjacent to a point mirror each other.
  • A is an Elliptical Arc.
  • z, finally, ends the path.

Each path can have multiple ‘segments’ which will show up as multiple instances of ‘M’ in the string. We don’t really need to know much more than that – if you’re drawing intricate paths and polygons, use an editor. We also have <polyline> & <polygon>, they’re basically <path> with different names for better semantics.

Organizational tags

The primitives above define the visible shapes drawn on the canvas, but they’re only half the story – we also have a collection of functional objects we can use behind the scenes to do some more advanced work.

Group: <g>
To group objects, they are the SVG equivalent of <div> tags. In the same way that CSS properties applied to an HTML element will “cascade” down through it’s children, the attributes we set on <g> tags will also “cascade” through it’s children. This happens for most elements in SVG. These tags can also be nested which is great for organizing components within your vector, after all – that’s what they’re for.

Definition: <defs>
These tend to be placed towards the top of an SVG. They’re used to hold the markup for elements that will be used repeatedly elsewhere in this SVG, or in other SVGs, even SVGs on different pages. As it’s purpose is only to define parts of a vector, the markup within a <defs> tag is not rendered. They are the SVG equivalent of a “template”.

Use: <use x="" y="" height="" width="" xlink:href="urlForSvgfile.svg#targetID">
If you were wondering how we “clone” the markup within a <defs> tag, this is it. It works by calling the ID of a tag within <defs>, or any other tag – but cloning content from within <defs> is generally a better idea. One thing to be aware of is that inherited styles (as in styles defined on a parent of the element you’re cloning) will not be cloned. By cloning into a different part of the document, that content will no longer be a child of it’s original parent. If you’re working with modular CSS however, this shouldn’t be a worry. It’s actually a good thing – if we’re using markup in several locations but wish to treat it differently, we can add a class to the <use> tag and style from there. One caveat; the selectors you use can’t cross from the document into the cloned content. For example, if a <rect> inside a <defs> tag has class="inside" and a <use> tag that is cloning it has class="outside", then this CSS selector will not work: .outside .inside { ... }.

Here are a few examples to make this more interesting. Have a look at the HTML to see what’s going on in detail. In summary, the front row:

1 = styling by stylesheet (the class is on the <use> tag)
2 = styling by inline style on the <use> tag
3 = styling by attribute on the <use> tag
4 = styling by stylesheet with a selector that “bridges” the <use> tag (it doesn’t work).

The back row: all styling by stylesheet, the class is in the cloned content – it works!

See the Pen pjEeRE by Iain J McCallum (@ijmccallum) on CodePen.

Symbol: <symbol>
This is the star of the SVG icon system idea I mentioned way back at the end of the best practices section. It allows us to define “template” vectors within a <defs> tag and <use> them throughout our site. This is similar to a <g> tag, which actually used to be used for SVG icon systems. However, they had a downside; every time you called a <g> from within a <defs> tag you had to set the viewBox to get the right ratio. With <symbol>, we can define that in the <symbol> itself, meaning we can <use> without worry. Check out the examples below, note the thin “icon” doesn’t leave huge chunks of white space around it.

See the Pen gawmNa by Iain J McCallum (@ijmccallum) on CodePen.

Rectangles, I know, very inspiring.

Thanks to this article on CSS-Tricks for clarifying the advantages of the system for me!

Marker: <marker>
The <marker> tag allows us to add vectors to the end of a line (or path). The vector in question can take any form within the tag. Generally these guys will be inside a <defs> tag and the line that wants it will reference the ID from a marker-end attribute. We went over something similar with the line-cap attribute, this is just a more advanced version that allows us to go beyond the predefined set of line-caps.

SVG Font: <font>
Just to be aware – this one exists, but unless you’re really really into typography, it’s not likely to be one we’ll use. It allows us to define a font within the SVG itself – as in build a glyph for each character (defined using the d attribute in a <glyph> tag). The example here should give you a rough idea of the structure for an SVG font but it’s a pretty specialist subject so I’ll let you dive into that one on your own – maybe even write a big summery article and let us know!

Metadata <metadata> / <title> / <desc>
Data about data. If this is something you’re concerned with you’ll likely know more about it, but it’s good to note that these exist and are great for accessibility. You can use them at the top of the document and within each element.


As you have seen in the object primitives, and in some of the organizational tags, SVG elements have their own set of attributes. I’ll only be covering some of them as there are a lot. Most should make sense without explanation (stroke, stroke-width, Fill, opacity, etc), so I’ll just cover some of the more interesting ones. Before starting though, it’s worth noting that SVG elements can use CSS in the same ways as HTML (inline, inline sheets, and external sheets) although the “include” for external sheets looks a little different (and we have to include it before our opening <svg> tag): <?xml-stylesheet type="text/css" href="svg-stylesheet.css" ?>.

fill-rule, this allows us to choose how the “inside” of a shape is worked out: evenodd or nonzero. They both draw a line from the center of the area in question and count the number of paths that are crossed.

For evenodd, if the resulting number is even then the area is not filled. If you think of this in terms of a single simple shape like a square, the line from the center that radiates out will only cross the bounding path once therefore the count will be 1, an odd number.

nonzero is different. It takes into account the direction of the path: counterclockwise paths get a value of -1, clockwise get 1. I would have coded up some examples but that’s already been done in this sitepoint article, go and enjoy!

transform, this works like the CSS version except the element’s origin is the canvas origin (top left): transform="translate(-100,-50),scale(1.5)".

Gradient, we define the colors within a gradient using “Stop colors”. The position of each within the gradient is determined by offset which takes a value between 0 and 1. Generally these will live within a <defs> tag and be given an id by which any primitive might refer to it through the fill attribute. For example, where a gradient object has been given an id of “g”:

<path d="M 100 200 200 200 150 100 z"
stroke="black" stroke-width="2" fill="url(#g)" />

When it comes to defining the actual gradients, we have two types to choose from:

First up: linearGradient. It’s probably what you’re thinking, a horizontal progression of colors:

<linearGradient id="g">
<stop offset="0" stop-color="white"/>
<stop offset="1" stop-color="black" stop-opacity="0.5"/>

Secondly we have radialGradient, it’s a bit more complex as it allows us to define curved gradients!

<radialGradient id="g" cx="30%" cy="60%" r="31" fx="26%" fy="34%" spreadMethod="reflect" gradientUnits="userSpaceOnUse">
  <stop offset="0" stop-color="white"/>
  <stop offset="1" stop-color="black"/>
  • cx, cy the outermost circle / limit. (in the example below, the black dot).
  • r is the radius.
  • fx, fy the focus position. (in the example below, the white dot).
  • spreadMethod, for when the gradient ends before the bounds of the shape.
    • “pad” use the last color and expand to the edge.
    • “reflect” run the gradient in reverse then forward alternately until the edge. (This is the setting in the example below)
    • “repeat” run the gradient again and again repeatedly until the edge.

See the Pen yYOXPJ by Iain J McCallum (@ijmccallum) on CodePen.

There’s a bit more on gradients, and patterns, but the above should cover the basics.

Pattern <pattern>
Like gradients, we can create a pattern object within the <defs> tag and use it’s id in the fill attribute of our target element.

    <pattern id="Pattern" x="0" y="0" width="10" height="10" patternUnits="userSpaceOnUse" patternTransform="rotate(30)">
        ... // pretty much anything goes here

The pattern element acts almost like the viewport – the width and height define how big the ‘view’ is of the pattern’s content and the x / y define the top left starting point for that box / view. Any of the pattern content that’s outside that box won’t be shown. It’s the content of this view / box that gets repeated in the fill of any elements that call it. When we define the pattern within the defs tag we have access to some interesting and confusing attributes & values.

First, the values:

  • userSpaceOnUse This references the global coordinate system.
  • objectBoundingBox This references the element that calls the pattern, when applied it transforms the pattern units into % values based on the size of the containing element. “1” will now be equal to 100% of the width of the containing element.

Now the attributes to which we can apply these values:

  • patternContentUnits This sets the coordinate system used for the contents of the pattern, as if reaching into the referenced pattern and tweaking the content of the pattern tag. It affects the elements that are within the pattern tag.
    • userSpaceOnUse (the default): The content of the pattern will be defined by the global coordinate system. This means that wherever you call the pattern, the content of each ‘tile’ will look the same. In the example below the right pair has this option.
    • objectBoundingBox: This sets the coordinate system based on the element that calls the pattern. For example, if you set the stroke width of a shape in the pattern as “1” the stroke will be the width of the calling element, not 1px. In the example below the left pair have this option (the objectBoundingBox not the stroke one!) In the left most example the rectangle’s stroke value is set to 0.1, or 10% of the width / height of the element that has called the pattern which is why it’s looking a little stretched.
  • patternUnits This affects the repetition of the pattern tiles within the pattern element. It is controlled using the width and height attributes of the <pattern> tag but does not affect the contents of the pattern tag.
    • userSpaceOnUse (default), the pattern will repeat independently of the containing element. The width and height on the <pattern> tag translate to “pixel” values (well, not exactly but it’s good for visualizing what’s happening). In the example below: the second has width="100" height="50" and the last has width="25" height="55"
    • objectBoundingBox – the pattern will repeat based on the containing element. The width and height of the <pattern> tag now uses units equivalent to the width and height of the calling element. For example, when we have <pattern width="1" height="1"> a single tile of the pattern will fill the entire containing shape, as in the first example below. Out of interest. the third has width="0.5" height=".25"

      See the Pen NGPzrW by Iain J McCallum (@ijmccallum) on CodePen.

      To get a better grasp of what’s going on, try opening this example up on codepen and playing with the values set on each pattern definition.

  • Example 1 patternContentUnits="objectBoundingBox" patternUnits="objectBoundingBox":
  • Example 2 patternContentUnits="objectBoundingBox" patternUnits="userSpaceOnUse":
  • Example 3 patternContentUnits="userSpaceOnUse" patternUnits="objectBoundingBox":
  • Example 4 patternContentUnits="userSpaceOnUse" patternUnits="userSpaceOnUse":

We also have patternTransform. This allows us to rotate the orientation of the pattern – not it’s contents, the pattern itself. So if our pattern created a grid, the rotation that we’ve applied in this transform would rotate the entire grid without the alignment of individual pattern ‘tiles’ changing.

See the Pen avdRmv by Iain J McCallum (@ijmccallum) on CodePen.

By the way, if you’re into a bit of D3.js (hopefully the subject of a future post) check out for some very well presented patterns.

Masking <mask>
The <mask> tag allows us to “mask” areas of elements, it essentially gives us an alpha channel per element. The appearance of the mask is defined within the <mask> tag in the same way that patterns are defined. The elements within a mask can even fill themselves with patterns! This type of nesting is part of the power behind using SVG, but I’ll leave that for you to play with on your own time, this article is already getting a bit long! The transparency is defined by brightness: black hides content, white shows it, and grey is semi transparent.

We define this within <defs> and, like the <pattern> tag, it allows us to set the space and content coordinate systems, except this time they’re called maskUnits and maskContentUnits. Below is an example similar to the two above, please do jump in and play with the values here too – it’ll help clear up the way these things work.

See the Pen qObMaG by Iain J McCallum (@ijmccallum) on CodePen.

  • 1 A copy of the content in the <mask> so we can see what’s being applied.
  • 2 maskContentUnits="objectBoundingBox" maskUnits="objectBoundingBox" Both the mask and its contents are defined by the containing element. That means the mask only needs <mask x="0" y="0" width="1" height="1" ... and it will fill the containing element. It’s content can then be defined in fractions from the top left corner, in the example above: <circle cx=".5" cy=".5" r=".55" so the circle is centered and expands just past the bounds of the shape (and therefor the <mask> as well).
  • 3 maskContentUnits="objectBoundingBox" maskUnits="userSpaceOnUse" The content is defined in the same way as above, but as the <mask> is now positioned in the global system it has been positioned to match the containing element: x="130" y="40" width="50" height="50"
  • 4 maskContentUnits="userSpaceOnUse" maskUnits="objectBoundingBox" The inverse of the last one. Now the content is positioned in the global system (<circle cx="190" cy="40" r="50"). The mask itself however only needs <mask x="0" y="0" width="1" height="1" ... and it will fill the containing element.
  • 5 maskContentUnits="userSpaceOnUse" maskUnits="userSpaceOnUse" Both the <mask> tags’ position and (independently) the content of the mask must be positioned (similar to the “absolute” positioning of CSS) in order to cover the containing element. Bit of an awkward one here but I guess you could use the <mask> position to mask your mask, to each their own!
  • 6 The shape that’s calling the mask (style="mask: url(#mask<n>)")

In preparing this I’ve found that without a thorough understanding of these coordinate system attributes it can be a bit of a nightmare trying to figure out what’s going on if something doesn’t line up. Fortunately I’ve had to work through these examples and their descriptions so it’s pretty well nailed down in my mind! I can only hope I didn’t bore you to death with it all.

Clipping <clipPath>

You could think of this as an extreme version of <mask>, there are no half way transparencies. The alpha is either on or off. The only attribute that is specific to this element is clipPathUnits which operates in the same way as both the “content” and the “contentUnit” equivalents from patterns and masks, with <clipPath> we cannot separate the two. As a result of this decision, we don’t have a clipPathContentUnits. The example below shows the effects of changing the clipPathUnits.

See the Pen epJQVE by Iain J McCallum (@ijmccallum) on CodePen.

  • objectBoundingBox: <circle cx="0" cy="0" r="1"/> Both the position and units are based of the containing element.
  • userSpaceOnUse: <circle cx="370" cy="40" r="100"/> Both the position and units are based on the global system.

Switch <switch>
This one was a surprise to me. Every element up until now has been familiar, if not fully understood. I was in the process of wrapping up this run through and decided to give a list of SVG tags a quick skim to see if there were any remaining curiosities – and out popped the <switch> tag. It is essentially a switch statement! So we could have language specific areas within our SVG. For example, we could have a different heading element (think group tags here) for Spanish, French and German – the first to match the user’s language setting would be rendered. Pretty cool! We can do more than language tests, in fact we can do quite a few. Keep in mind it’s the first element to pass that will be rendered so order is likely to be important. Here are some of the attributes this tag uses:

  • requiredFeatures: we give a list of features that would be required by the contained graphics, you can check out the list here. This will only really be of interest when you’re building something complex that may need to support older browsers.
  • requiredExtensions: similar to above but (from what I can tell as I’ve never actually used this) generally used with the <foreignObject> element to allow inclusion of non SVG content, but that’s a subject for an entirely different article.
  • systemLanguage: here’s a list of language codes

Some examples:

    <text systemLanguage="en">English</text>
    <text systemLanguage="en">Spanish</text>
     <image media="(min-width: 45em)">
     <image media="(min-width: 18em)">
     <image> //default

You may have picked up on that second example not being exactly “best practice”. Especially given that we can use media queries within a CSS block up top. This would start us into a discussion on responsive SVG had there not already been a pretty cool one that exists on Smashing Magazine. Definitely check that one out, it even goes into SVGs responding to their container width, a technique I’m looking forward to implementing in some upcoming projects!

That’s it for my run through of tags and attributes! As I mentioned at the very start, it’s not a complete list but hopefully it’s enough to give an outline of what’s possible. For your more specific use cases – google away!
Now for the final, and (in my opinion) most exciting section: filters! This is a pretty massive subject so for now, we’ll have to be happy with a short introduction to it. Maybe if you badger me enough in the comments, I’ll write up another full article covering them in more detail.


From the perspective of front end development – SVG filters are magic. They provide us with another visual layer to play with. The kind of layer you may have played with in the Photoshop “filters” menu or in adding “filters” with Instagram. There’s a huge amount of power here! Normally, these kinds of complex effects for a web page would send us straight towards embedding images, and if it’s just a static image – that will probably still be your best bet. But if that graphic has text that should be crawlable, or (better yet) needs to be interactive: SVG filters will start to look mind blowing with their potential!
Note: these are IE10+, and we’re nearly there! After all, Bootstrap 4 has dropped IE8 already!

To begin, let’s look at the set up – it’s pretty much the same as patterns and masking.

    <filter id="filterName" filterUnits="objectBoundingBox | userSpaceOnUse">
        <!-- filter content -->
<text filter="url(#filterName)">Filtered text</text>

The filter content.
The final effect for a filter is usually achieved by combining a series of “filter primitives”. There are 16 of them (briefly described below), each has it’s specific settings but there are a few important attributes that most of them use:

  • in& in2: The input on which to apply the current filter (in2 is almost identical to in, it allows us to bring in a second source but can only be used by specific filters). They each take a few values which are pretty well documented by the Mozilla Developer Network.
  • result: like an ID, this can be referenced by in & in2. Every filter primitive has this one as it’s required for each to be used in the “filter chain” (I just made that up, sound good?).

Using these attributes we can build up a network of filters, something that might be displayed as a flow diagram for clarity when working with more complex effects. So, onto the filters that we have to play with (I’ve noted which can take in and in2 after the name of each tag):

The filter primitives:

  • <feBlend> (in & in2): Blend two inputs, this is the same as the blend mode in Photoshop but with less choice: normal | multiply | screen | darken | lighten. Here’s a good demonstration of each.
  • <feColorMatrix> (in): Applies a transformation matrix to pixel color values.
  • <feComponentTransfer> (in): The parent of 4 child elements that individually manipulate the RGBA color channels: <feFuncR>, <feFuncG>, <feFuncB> and <feFuncA>. We have a few choices of manipulation techniques, checkout the docs on for an in-depth explanation of each.
  • <feComposite> (in & in2): combines two images using the Porter-Duff algorithms. It’s similar to clip paths except this is applied to raster graphics. Until researching this filter primitive, I did not know this existed – absolutely chuffed to find out it does!
  • <feConvolveMatrix> (in): This is a more complex one. It combines each pixel with it’s neighboring pixels to create various effects. For example, embossing, sharpening, and blurring.
  • <feDisplacementMap> (in & in2): This moves (“displaces”) the pixels of the first image by the value found in the equivalent pixel in the second image. We can choose which channel applies for the “x” and “y” values. It’s essentially highly customizable warping.
  • <feFlood>: This simply fills an area with a solid color. Generally used as an addition to other effects which is why it doesn’t support the in or in2 attributes.
  • <feGaussianBlur> (in): Another simple one, this blurs the image by the amount specified in it’s stdDeviation attribute.
  • <feImage>: This pulls in an image and provides output as pixel data, even if that image is a vector graphic.
  • <feMerge>: This is parent to <feMergeNode> tags that each call in another filter primitive. The effect is to take the result of each filter primitive called and layer them on top of each other. Nothing fancy here, just layering.
  • <feMorphology> (in): This makes things thinner or thicker (in filter language: erode or dilate)
  • <feOffset> (in): another simple one, this moves the entire input by the defined “x” and “y” values.
  • <feDiffuseLighting> (in): If you’ve ever come across the concept of using the alpha channel to create the illusion of detail (“bump mapping”), this is that.
  • <feSpecularLighting> (in): the same as above but it gives the illusion of shiny surfaces.
  • <feTile> (in): if the source is smaller than the bounding box in which the final effect is used, this will tile the filter to fill the extra space.
  • <feTurbulence>: this generates “noise” – not exactly like static, but close. As it generates this from scratch (or actually a seed so we can guarantee the same effect) it too lacks support for in or in2.

Further reading

After reading through all that, it’s my hope that you now have a pretty good idea of what’s possible with SVGs, at least at a basic (static) level. From here, it’s onto animation and interactions! That’s actually where I’d say further reading would be of the most value: getting things moving and reactive (not a pun!). It’s doable with plain JavaScript, but this is the point where various libraries can start giving you apparent superpowers. Even if you focus on static SVGs – there are libraries that will allow you to programmatically generate markup. The possibilities… Whichever direction you go, I think even a loose awareness of the basics will be invaluable. So before we go, here’s a small list of SVG related resources that might be of interest to you:

Finally, if you’ve skipped to the bottom looking for the big conclusion, check out Chris Coyier’s great talk, “The Wonderful World of SVG”.

It’s a super-fast skim over the possibilities of using SVG and it’s brilliant – so my conclusion is go watch that! (and if you don’t have 40 mins – watch it at 2x, I only found that feature in YouTube recently, an amazing time saver!) Now go make something cool with it and tweet us a link – we want to see it!