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.
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.
What You'll Learn
💡 Think of It Like This
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.
1. Inline SVG & the viewBox Coordinate System
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.
2. Shapes, fill & stroke
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.
Element
Key attributes
Use for
< rect >
x, y, width, height, rx
Boxes, rounded cards
< circle >
cx, cy, r
Dots, avatars, badges
< path >
d (M, L, Q, C, Z)
Anything — icons, curves
3. Styling SVG with CSS & Custom Properties
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.
4. Animating SVG with CSS (the draw effect)
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.
5. Reuse with < symbol > & < use >
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.
6. Accessibility — Make Graphics Announceable
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.
🎯 Your Turn #1 — Draw and colour a shape
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.
🎯 Your Turn #2 — Theme with CSS and draw the line
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.
🧩 Mini-Challenge — An accessible icon sprite from scratch
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.
⚠️ Common Errors (and the fix)
- Missing viewBox. Without viewBox the browser has no coordinate grid to scale, so the graphic clips or refuses to resize when you change width/height. Fix: add viewBox="0 0 W H" matching the coordinates you drew in (e.g. 0 0 24 24 ).
- No accessible name. A meaningful icon with no label is invisible to screen readers — they announce nothing, or read raw markup. Fix: add role="img" plus aria-label (or a < title > ); for decoration use aria-hidden="true" .
- fill attribute won't change on hover. Setting color: red on an SVG does nothing — shapes use fill / stroke , not color directly. Fix: style fill in CSS, or set fill="currentColor" so color drives it.
- Draw effect does nothing. Animating stroke-dashoffset on a shape with a fill and no stroke shows no change. Fix: set fill: none , give it a stroke , and make stroke-dasharray ≥ the path length.
- Using SVG for a photo. SVG describes shapes, not pixels — there's nothing to "describe" in a photograph, so it bloats or can't be done. Fix: use a raster format ( < img > with PNG/JPG/WebP) for photos; keep SVG for icons, logos and line art.
📋 Quick Reference
Goal
Use this
Set the coordinate grid
< svg viewBox="0 0 24 24" >
Rectangle / rounded box
< rect x y width height rx / >
Circle
< circle cx cy r / >
Any shape / line
< path d="M.. L.. Q.. Z" / >
Outline only
fill="none" stroke="..." stroke-width="2"
Inherit text colour
stroke="currentColor" / fill="currentColor"
Theme from CSS
fill: var(--brand);
Draw a line on
stroke-dasharray + dashoffset → 0
Reuse an icon
< symbol id > … < use href="#id"/ >
Accessibility
role="img" aria-label="…" / aria-hidden="true"
❓ Frequently Asked Questions
🎉 Lesson Complete
You can now draw, style, animate and label vector graphics in pure markup. The essentials:
- ✅ Inline SVG shapes are DOM elements — styleable, scriptable, inspectable
- ✅ viewBox sets the coordinate grid; width/height set the pixels — that's what makes SVG resolution-independent
- ✅ Draw with rect / circle / path ; paint with fill and stroke
- ✅ Style from CSS and theme with var() / currentColor
- ✅ stroke-dasharray + stroke-dashoffset → 0 makes a line draw itself (prefer CSS over SMIL)
- ✅ < symbol > + < use > = define once, reuse everywhere
- ✅ Add role="img" + aria-label (or aria-hidden for decoration)
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.
Continue this course
- Previous: Canvas
- Next: Responsive Navigation