Svg Mastery
SVG (Scalable Vector Graphics) is an XML-based markup format for drawing shapes, paths, text, and gradients that stay perfectly crisp at any size, and because it lives in the DOM you can style and animate it directly with CSS.
Learn Python, JavaScript, Java and more with free interactive lessons, real projects and AI-powered help. Beginner-friendly.
Part of the free HTML & CSS course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
By the end of this lesson you'll be able to draw crisp, resolution-independent graphics in pure markup, style and theme them with CSS custom properties, animate a line so it appears to draw itself, build a reusable icon sprite, and make every graphic accessible to screen readers.
A PNG is a photograph; an SVG is a recipe. A photo of a cake is a fixed grid of coloured dots — blow it up on a billboard and you see the dots. A recipe says "a 20cm round tin, two layers" — hand it to a baker and they can make the cake at any size, perfectly, every time.
SVG stores your graphic as a recipe in text: "draw a circle of radius 50 at (60, 60), fill it green." The browser is the baker. Because it's a recipe, not a snapshot, it stays sharp at any zoom, every ingredient is a real DOM element you can style and animate, and you can change one line to re-colour the whole thing.
Inline SVG means writing the svg tag straight into your HTML instead of pointing an img at a file. The payoff is that every shape inside becomes a real DOM element — you can target it with CSS, hook up events, and inspect it in DevTools.
The single most important attribute is viewBox="minX minY width height" . It sets up the SVG's own little coordinate grid: viewBox="0 0 100 100" means "inside here, the world is 100 units wide and tall." The width / height (or CSS) decide how big that grid is drawn on screen — so a 100-unit grid can be painted at 50px or 500px and the coordinates inside never change. Note (0, 0) is the top-left , and y grows downward .
Run this. The two squares are drawn with identical coordinates but different on-screen sizes — proof the viewBox is independent of pixels.
A handful of elements cover most graphics: rect (x, y, width, height, plus rx for rounded corners), circle (cx, cy centre and r radius), and the do-anything path , whose d attribute is a mini drawing language: M move to, L line to, Q / C curves, Z close.
Every shape has two paint properties: fill (the inside) and stroke (the outline, sized with stroke-width ). Set fill="none" for an outline-only shape.
Because each shape is a DOM element, the same CSS you already know works on it — just remember the properties are fill , stroke and stroke-width rather than background and border . A CSS rule beats a fill="..." attribute, so you can leave a sensible colour in the markup and override it for hover or dark mode.
The killer combo is custom properties : store the colour in a --var and read it with var() inside fill . Now one variable re-themes the icon, and stroke="currentColor" makes an icon inherit the surrounding text colour automatically.
The famous "line draws itself" effect is a trick with two stroke properties. stroke-dasharray turns a solid line into dashes; make the single dash as long as the whole path and you get one big dash. stroke-dashoffset then slides that dash along the line — push it fully off the end and the line looks empty. Animate the offset back to 0 and the line appears to draw itself.
It only affects the stroke , so the shape needs fill: none and a stroke. For the exact length use path.getTotalLength() in JS; here a value safely larger than the path works fine.
SMIL note: SVG also has its own animation tags — animate and animateTransform , called SMIL. Prefer CSS (shown here) for almost everything: it's better supported, easier to control from JavaScript, and lives with the rest of your styles. Reach for SMIL only for the rare attribute CSS can't animate.
Copy-pasting the same icon markup ten times is wasteful and a nightmare to update. Instead, define each icon once inside a symbol id="..." (give the symbol its own viewBox ), then stamp it anywhere with use href="#id" / . This is an "SVG sprite" — one definition, many instances.
Each use can be a different size and colour, and because the shapes use currentColor , the icon takes the surrounding text colour. Fix the definition once and every copy updates.
A screen reader can't "see" your shapes, so you must describe them. For a meaningful graphic (a logo, an informative chart), add role="img" and an accessible name — the simplest is aria-label="Company logo" on the svg . A title as the first child also names it and shows as a tooltip; pair it with aria-labelledby for the most reliable support.
For a decorative graphic that adds nothing for a non-sighted user, do the opposite: aria-hidden="true" so the screen reader skips it entirely. The worst option is leaving a meaningful icon unlabelled and silent.
Finish this inline SVG so it shows a yellow circle with a smiling mouth. Fill in the blanks marked ___ , then run it and check the expected result in the comments.
Two jobs: colour the icon from a custom property instead of the markup, and make the underline draw itself. Add the var() , the dash properties, and the keyframe target, then verify against the comments.
Support is faded now — only an outline is given. Build a tiny icon sprite and use it twice. Lean on the worked examples in sections 5 and 6 if you get stuck.
You can now draw, style, animate and label vector graphics in pure markup. The essentials:
Next up: Responsive Navigation , where you'll build a menu that adapts from desktop bar to mobile drawer.
Practice quiz
Why does SVG stay sharp at any size while PNG/JPG can go blurry?
- SVG files are larger
- SVG compresses better
- SVG stores drawing instructions (vectors) the browser redraws at any size; raster formats store fixed pixels
- PNG has no transparency
Answer: SVG stores drawing instructions (vectors) the browser redraws at any size; raster formats store fixed pixels. SVG is a vector format describing shapes as instructions, so it re-renders crisply at any scale; raster formats store a fixed pixel grid that blurs when enlarged.
What does the viewBox attribute define on an <svg>?
- The internal coordinate grid (its own unit system)
- The file size
- The CSS background
- The animation speed
Answer: The internal coordinate grid (its own unit system). viewBox="minX minY width height" sets up the SVG's coordinate grid; width/height (or CSS) decide how big that grid is drawn on screen.
In viewBox="0 0 24 24", where is the point (0, 0)?
- The center
- The bottom-left corner
- It depends on the browser
- The top-left corner, with y growing downward
Answer: The top-left corner, with y growing downward. SVG's origin (0, 0) is the top-left, and the y-axis increases downward — the same convention as the screen.
Which attributes define a <circle> in SVG?
- x, y, width, height
- cx, cy (center) and r (radius)
- d only
- points
Answer: cx, cy (center) and r (radius). A <circle> uses cx and cy for its center and r for its radius. A <rect> uses x, y, width, height (and rx for rounded corners).
In a <path>, what does the 'M' command do in the 'd' attribute?
- Moves the pen to a new point (move to)
- Draws a line
- Mirrors the shape
- Closes the path
Answer: Moves the pen to a new point (move to). M is 'move to' (lift and reposition the pen). L is 'line to', Q/C are curves, and Z closes the path back to the start.
What is the idiomatic way to make an icon inherit the surrounding text colour?
- fill="inherit"
- color: auto
- stroke="currentColor" (or fill="currentColor")
- fill="text"
Answer: stroke="currentColor" (or fill="currentColor"). currentColor resolves to the element's CSS color, so fill/stroke="currentColor" makes an icon track the parent's text colour automatically.
Which wins when there's a conflict: a fill="red" attribute or a CSS fill rule?
- The fill attribute always wins
- The CSS rule overrides the presentation attribute
- Whichever comes first in the file
- Neither applies
Answer: The CSS rule overrides the presentation attribute. A presentation attribute like fill="red" is the weakest source, so any CSS fill rule overrides it (an inline style attribute would beat both).
How does the stroke-dasharray 'draw itself' animation work?
- It animates the fill color
- It rotates the path
- It uses opacity keyframes
- Make one dash as long as the path, offset it off the end, then animate stroke-dashoffset to 0
Answer: Make one dash as long as the path, offset it off the end, then animate stroke-dashoffset to 0. Set stroke-dasharray and stroke-dashoffset to the path length so the line looks empty, then animate stroke-dashoffset to 0 to slide the dash into view.
The draw-on effect requires the shape to have which setting?
- fill set to a solid colour
- fill: none plus a stroke (it only affects the stroke)
- no stroke at all
- a viewBox of 0 0 100 100
Answer: fill: none plus a stroke (it only affects the stroke). stroke-dasharray/offset animate the stroke only, so the shape needs a stroke and usually fill: none for the line to appear to draw itself.
How do you make a MEANINGFUL inline SVG icon accessible to screen readers?
- Leave it unlabelled
- Add aria-hidden="true"
- Add role="img" and an accessible name (aria-label or a <title>)
- Wrap it in a <noscript>
Answer: Add role="img" and an accessible name (aria-label or a <title>). A meaningful graphic needs role="img" plus a name via aria-label or a <title>. A purely decorative SVG should instead use aria-hidden="true" so readers skip it.