{"slug": "a-friendly-introduction-to-svg", "title": "A Friendly Introduction to SVG", "summary": "SVG (Scalable Vector Graphics) is an image format that uses XML syntax to define drawing instructions, unlike binary formats like JPEG. When used inline in HTML, SVG elements become part of the DOM, allowing them to be styled and animated with CSS and JavaScript. The article explains that SVG provides illustration-focused primitives like `<circle>` and `<line>`, which offer capabilities such as easily drawing diagonal lines that are difficult to achieve with standard HTML.", "body_md": "[Introduction]\n\nSVGs are one of the most exciting technologies we have access to in-browser. We can do *so many* cool things with SVG. It’s an absolutely critical part of my toolkit.\n\nHere’s a quick montage of things I’ve done with SVG:\n\nBut SVGs are also pretty intimidating. The rabbit hole goes deep, and it’s easy to get overwhelmed.\n\nSo, in this blog post, I want to share the most important fundamentals, to provide a solid foundation you can build on. I’ll show you *why* SVGs are so cool, and share a few tricks you can start using right away. ✨\n\n[Link to this heading](#hello-svg-1)Hello SVG\n\nAt its core, SVG (“Scalable Vector Graphics”) is an image format, like `.jpg`\n\nor `.gif`\n\n. We can pop them into an `<img>`\n\ntag, like any other image:\n\n```\n<img\n  alt=\"return to homepage\"\n  src=\"/images/home.svg\"\n/>\n```\n\n**This works, but it’s not what makes SVGs so cool and interesting.** The *real* magic happens when we use *inline SVGs.*\n\nMost image formats like `.jpg`\n\nare binary formats; if you tried to open them in a text editor, you’d see a bunch of gobbledygook. SVGs, by contrast, are specified using XML syntax, just like HTML! Instead of specifying the R/G/B color for each pixel, SVGs contain the set of drawing instructions required to render the illustration.\n\nSomewhat magically, we can drop the raw SVG code right into an HTML document:\n\nCode Playground\n\n```\n<div class=\"wrapper\">\n  <p>\n    Check out this SVG:\n  </p>\n\n  <svg width=\"100\" height=\"100\">\n    <circle\n      fill=\"hotpink\"\n      r=\"30\"\n      cx=\"50\"\n      cy=\"50\"\n    />\n  </svg>\n</div>\n```\n\nIn HTML, we’re given a set of primitives that are all document-related: paragraphs and headings and lists, the same primitives you get in Microsoft Word. SVG is the same sort of deal, but all of the primitives are for *illustrations*, things like `<circle>`\n\nand `<polygon>`\n\nand `<path>`\n\n.\n\n**The really cool thing is that SVGs are first-class citizens in the DOM.** We can use CSS and JavaScript to select and modify SVG nodes, as if they were HTML elements.\n\nCheck this out:\n\nCode Playground\n\n```\n<style>\n  circle {\n    fill: hotpink;\n    transition: r 400ms, cy 600ms;\n  }\n  button:hover circle,\n  button:focus-visible circle {\n    r: 50px;\n    cy: 100px;\n  }\n</style>\n\n<button>\n  <svg width=\"100\" height=\"100\">\n    <circle\n      r=\"30\"\n      cx=\"50\"\n      cy=\"50\"\n    />\n  </svg>\n</button>\n```\n\nMany SVG attributes, like the circle’s color (`fill`\n\n) and radius (`r`\n\n), moonlight as CSS properties. This means I can change them in CSS, and even use CSS transitions to animate them! 🤯\n\n**This is what makes SVG so powerful.** It’s like an alternate-reality version of HTML that focuses on illustration instead of documentation, and we can use our existing CSS/JS skills to make them dynamic.\n\n[Link to this heading](#basic-shapes-2)Basic shapes\n\nAs we saw above, SVG contains its own set of UI primitives. Instead of `<div>`\n\nand `<button>`\n\n, we have shapes like `<circle>`\n\nand `<polygon>`\n\n. Let’s go through them.\n\n[Link to this heading](#lines-3)Lines\n\nPerhaps the most straightforward shape is `<line>`\n\n:\n\nIt’s such a basic thing, but already, we’ve done something we can’t easily do in HTML. The only way to draw a diagonal line in HTML is to create a long thin DOM node and rotate it, which quickly turns into an advanced math problem if you need that line to start and end in specific places.\n\nIn SVG, lines are comparatively easy. We specify the start point (`x1`\n\nand `y1`\n\n) and the end point (`x2`\n\nand `y2`\n\n), and we get a straight line between those two points!\n\n[Link to this heading](#rectangles-4)Rectangles\n\nRectangles are positioned using their top/left corner, specified using `x`\n\nand `y`\n\n. They grow from that position, using `width`\n\nand `height`\n\n:\n\nAt first glance, this looks like a `<div>`\n\nwith a `border`\n\n, but there are a few core differences.\n\nFirst, the stroke is drawn on the *center* of the path, not on the inside or the outside:\n\nThis is true for all shapes, not just `<rect>`\n\n. And unfortunately, this isn’t configurable; we can’t specify that a specific shape should have its stroke drawn on the inside or the outside.\n\n**Another interesting thing to note:** check out what happens when we reduce either the `width`\n\nor the `height`\n\nto 0. You might expect it to essentially become a straight line, but instead, the whole shape disappears:\n\nIn the SVG specifications, these sorts of shapes are known as “degenerates” (which feels pretty harsh to me!). When a two-dimensional shape like `<rect>`\n\nonly stretches across one dimension, it’s considered invalid and doesn’t get rendered.A few years ago, the behaviour was inconsistent; some browsers would still paint degenerate shapes while others wouldn’t. Fortunately, all modern browsers follow the specification these days.\n\nFinally, we can round the corners of our rectangle using the `rx`\n\nand `ry`\n\nproperties, similar to `border-radius`\n\n:\n\n[Link to this heading](#circles-5)Circles\n\nThe size of a circle is dictated by its radius, `r`\n\n. We control the position of the circle by specifying a center point with `cx`\n\nand `cy`\n\n:\n\nLike with `<rect>`\n\n, circles will disappear entirely when their radius is set to 0.\n\n[Link to this heading](#ellipses-6)Ellipses\n\nAn `<ellipse>`\n\nis just like a `<circle>`\n\n, except we can choose different values for its horizontal and vertical radius. This lets us create ovals:\n\n[Link to this heading](#polygons-7)Polygons\n\nThe `<polygon>`\n\nelement lets us create multi-sided shapes like this:\n\nThe `points`\n\nattribute takes a list of X/Y points; the browser will draw a line between each point, and from the final point back to the first.\n\n**I found this a bit confusing when I was learning SVGs.** In my brain, the term “polygon” refers to something very specific: shapes that have rotational symmetry like triangles and squares and hexagons and octagons:\n\nIt turns out that these are “regular” polygons, or equilateral polygons. They’re a subset of a broader polygon world.\n\nTo create *regular* polygons, we need to use trigonometry. It’s a bit beyond the scope of this blog post, but I’ll put the calculations in this playground if you’d like to learn more:\n\nThere are a couple more primitive shapes, like `<polyline>`\n\nand `<text>`\n\n, but I think we’ve covered enough for an intro blog post. Let’s move on.\n\n[Link to this heading](#scalable-svgs-8)Scalable SVGs\n\nUp until now, we’ve been using “absolute” coordinates for things. This means that our SVGs *must* be a very specific size, otherwise things break:\n\n```\n<svg width=\"100\" height=\"220\">\n  <circle\n    cx=\"150\"\n    cy=\"110\"\n    r=\"60\"\n    stroke=\"var(--gold)\"\n    stroke-width=\"10\"\n  />\n</svg>\n```\n\nIn this demo, our circle is meant to sit in the center of a 300px-wide element. When the element is given a smaller width, however, the circle doesn’t shrink. It gets cropped.\n\nThis isn’t how most images work! When we render a `.jpg`\n\n, the photo will scale up and down with the element’s size.\n\nOne (not great) solution for this is to dynamically recalculate all of the values based on the width:\n\n```\n<svg width=\"100\" height=\"73.3\">\n  <circle\n    cx=\"50\"\n    cy=\"36.7\"\n    r=\"20\"\n    stroke=\"var(--gold)\"\n    stroke-width=\"3.3\"\n  />\n</svg>\n```\n\nI’m doing some math in JavaScript to calculate all of those geometry/presentational properties, based on the presumed “full size” width of 300px. So if the width is actually 150px, all of those values get multiplied by 0.5.\n\nAnd it *works,* but it’s a huge pain, even for a very simple illustration like this. Fortunately, there’s a much better way to solve this problem.\n\nCheck this out:\n\n```\n<svg\n  width=\"100\"\n  viewBox=\"0 0 300 220\"\n>\n  <circle\n    cx=\"150\"\n    cy=\"110\"\n    r=\"60\"\n    stroke=\"var(--gold)\"\n    stroke-width=\"10\"\n  />\n</svg>\n```\n\nThe `viewBox`\n\nattribute defines an *internal coordinate system*. When it’s provided, our `<circle>`\n\ns and `<rect>`\n\ns and `<polygon>`\n\ns will stop inheriting the raw pixel values of the DOM and instead use this internal coordinate system.\n\nThe `viewBox`\n\nattribute takes four numbers, but really, we can think of it as two pairs of two numbers.\n\nThe first two numbers allow us to change *which part* of the SVG we’re viewing. **Touch and drag on top of the <rect> to see what I mean:Using your mouse/trackpad, click and drag on top of the <rect> to see what I mean:Using the X/Y sliders below, change the viewBox’s position to see what I mean:**\n\n```\n<svg\n  width=\"300\"\n  height=\"300\"\n  viewBox=\"0 0 300 300\"\n>\n  <rect\n    x=\"0\"\n    y=\"0\"\n    width=\"200\"\n    height=\"200\"\n  />\n</svg>\n```\n\nIf you’re unable to use a pointer device, you can also use the sliders along the bottom for the same effect.If you’re unable to use a pointer device, you can also use the sliders along the bottom for the same effect.\n\nThe view*Box* actually kinda works like the view*port*. This blog post, for example, is much taller than the browser window, so a portion of the lesson is shown in the viewport, and you can change which part you’re looking at by scrolling. It’s the same sort of idea with `viewBox`\n\n.\n\nLet’s talk about the *second* pair of values used for the `viewBox`\n\n. These two values allow us to specify the width and height of the viewable area.\n\nTry using the new “ViewBox Size” slider to see what happens.**For this demo, try scrolling up/down while your cursor is over the <rect>.** Alternatively, you can use the “ViewBox Size” slider below:Try using the new “ViewBox Size” slider to see what happens.\n\n```\n<svg\n  width=\"300\"\n  height=\"300\"\n  viewBox=\"-40 -40 300 300\"\n>\n  <rect\n    x=\"0\"\n    y=\"0\"\n    width=\"200\"\n    height=\"200\"\n  />\n</svg>\n```\n\nThe second pair of values that we pass to `viewBox`\n\ncontrols *how much* of the infinite SVG field we’re actually looking at.\n\nNow, it doesn’t change the size of our SVG — that’s controlled with the `width`\n\n/ `height`\n\nattributes, or with CSS. Instead, it effectively changes the zoom level.\n\nIn the demo above, our SVG is 300px by 300px. If we set the `viewBox`\n\nto `\"0 0 300 300\"`\n\n, we’ll have a perfect 1:1 ratio between the internal coordinate system and standard DOM coordinate system (pixels).\n\nBut suppose we set the `viewBox`\n\nto `\"0 0 150 150\"`\n\n. The SVG is still 300px by 300px, but now it’s only displaying a 150×150 zone of our infinite SVG canvas. This effectively zooms in by 2x, doubling the size of the shapes inside our SVG.\n\nKeeping with the viewport analogy (since they really are quite similar), this is equivalent to using the browser zoom function (`Ctrl` `+`) to zoom up to 200%. It doesn’t change the size of the browser window, but it scales everything up within the viewport to 2x its original size.\n\nSo, we’ve seen how the `viewBox`\n\nattribute can be used to slide the viewable area around (by changing the first two numbers), or to zoom in/out (by changing the last two numbers).\n\n**To be honest with you, I’m not sure I’ve ever done either of these things.** The only realistic use case I can conceive of for shifting and zooming the viewBox is if you have a gigantic chart with lots of detail and you want to guide users through it by jumping from one section to another.\n\nI showed you this stuff to help you understand how the `viewBox`\n\nworks. *In practice,* we usually keep the viewBox values static, so that our image always shows the exact same thing no matter what size we’re rendering our SVG at. This allows us to use the same SVG at different sizes in different contexts.\n\n[Link to this heading](#presentational-attributes-9)Presentational Attributes\n\nIn SVG, our shapes can either be filled in with the `fill`\n\nattribute, outlined with the `stroke`\n\nattribute, or both.\n\nThe `fill`\n\nattribute is pretty self-explanatory, so let’s focus on strokes. They’re *kinda* like HTML borders, but *way* more powerful.\n\n**Try flipping between the different variants here to get a sense of what’s possible:**\n\n```\n<style>\n  circle {\n    stroke: hsl(45deg 100% 50%);\n    stroke-width: 6px;\n    stroke-dasharray: 20, 14;\n    stroke-linecap: butt;\n  }\n</style>\n\n \n\n<svg viewBox=\"0 0 200 200\">\n  <circle cx=\"100\" cy=\"100\" r=\"50\" />\n</svg>\n```\n\nWe control the presentation of the stroke using a handful of `stroke`\n\nCSS properties. We can also set them as inline attributes (so, instead of setting `stroke-width: 5px`\n\nin CSS, we could also set `stroke-width=\"5\"`\n\nin the SVG itself).\n\nHere’s a quick breakdown of what these properties do:\n\n`stroke`\n\n— sets the color of the stroke. Defaults to`transparent`\n\n.`stroke-width`\n\n— sets the width of the stroke, in pixels.`stroke-dasharray`\n\n— sets the width of each segment and the gap between them. If we pass two values (eg.`10, 20`\n\n), we’re saying we want a 10px dash with 20px gap between them. We can even specify a*repeating dash pattern*by specifying more than 2 values.`stroke-linecap`\n\n— controls how each dash should be capped. If the dash is 0px thick, we’ll get little circles with`round`\n\n, little squares with`square`\n\n, or nothing at all with the default value,`butt`\n\n.\n\n[Link to this heading](#animated-strokes-10)Animated strokes\n\nSo, because presentational SVG attributes like `stroke-width`\n\nare actually CSS properties, we can animate them like anything else in CSS!\n\nIn the demo above, for example, I’m smoothly interpolating between the different stroke styles using basic CSS transitions:\n\n```\ncircle {\n  transition:\n    stroke 1200ms,\n    stroke-width 900ms,\n    stroke-dasharray 1500ms,\n    stroke-linecap 1000ms;\n}\n```\n\nHow cool is that?!\n\nThere’s another stroke property that is *particularly* useful for animations: `stroke-dashoffset`\n\n. This property allows us to slide the dashes around the shape:\n\nWe can do *all sorts of stuff* with this property. For example, we can have our dashes run around our shapes like little marathon runners:\n\nFor a seamless effect, you’ll want to set `stroke-dashoffset`\n\nequal to the combined length of the dash + gap; otherwise, you’ll notice a flicker when the animation loops, as the dashes jump back to their original offset. You’ll also want to experiment with different gap sizes, to find a value that repeats nicely given the circumference of your shape.\n\nOr, by animating the dash length and the offset, we can create this fancy spinner:\n\n(Nothing is loading here! I’m demonstrating the spinner itself.)\n\nFinally, maybe the most famous trick is to create the illusion of an SVG drawing itself:\n\nThe clever trick here is that we have a single dash that is the same length as the entire circumference of our shape (763px, in this particular case), and a huge gap between each dash (1000px). We draw the shape by sliding this dash into place, by animating the `stroke-dashoffset`\n\n.\n\n**How do we figure out the circumference of the shape?** We can use JavaScript to calculate it for us:\n\n``` js\nconst element = document.querySelector('polygon');\n\n// 👇 This is the magical method that calculates the circumference:\nconst pathLength = element.getTotalLength();\n\nelement.style.strokeDasharray = `${pathLength}, 1000`;\n```\n\nThis is the ideal solution to this problem, since it gives us the precise length, but I’ve also solved this problem in the past with trial-and-error, guesstimating the length until it looked right.\n\n[Link to this heading](#the-power-of-svgs-11)The power of SVGs\n\nMy goal with this blog post is to give you a high-level understanding of what SVGs are, and also share some cool tricks you can start using in your own work. **But there’s so much more that we can do with SVGs.** We’ve only scratched the surface here!\n\nI’ve spent the past 18 months working on a comprehensive course all about whimsical animation, and SVGs are a core part of that course. I’ve learned so much about animation in the almost-20-years I’ve been building for the web, and my goal in this course is to share all of my secrets with you! 😄\n\n**Registration is now open!** You can learn more here:\n\n### Last updated on\n\nMay 5th, 2026", "url": "https://wpnews.pro/news/a-friendly-introduction-to-svg", "canonical_source": "https://www.joshwcomeau.com/svg/friendly-introduction-to-svg/", "published_at": "2025-07-21 13:30:00+00:00", "updated_at": "2026-05-22 14:53:45.988651+00:00", "lang": "en", "topics": ["developer-tools"], "entities": ["SVG"], "alternates": {"html": "https://wpnews.pro/news/a-friendly-introduction-to-svg", "markdown": "https://wpnews.pro/news/a-friendly-introduction-to-svg.md", "text": "https://wpnews.pro/news/a-friendly-introduction-to-svg.txt", "jsonld": "https://wpnews.pro/news/a-friendly-introduction-to-svg.jsonld"}}