HTML SVG
A photograph is made of pixels. Zoom in far enough, and it turns into squares. An SVG (Scalable Vector Graphics) is different: it is a list of instructions. "Draw a circle here, this big, in this colour." Zoom in as far as you like, and the circle stays perfectly sharp, because the computer simply draws it again.
SVG is written in markup that looks like HTML, can be placed right inside your page, can be styled with CSS, and can be changed with JavaScript. It is the best choice for icons, logos, charts, diagrams, and illustrations.
What You Will Learnβ
- Writing an SVG, and the
viewBoxthat controls its scale - Basic shapes, paths, text, groups, and gradients
- Styling with CSS and
currentColor - Reusing shapes with
symbolanduse - Four ways to include SVG, and which to choose
- Making SVG accessible
- Animation, performance, security, and common mistakes
The Basicsβ
- HTML Code
- Browser Output
<svg viewBox="0 0 200 100" role="img" aria-labelledby="logo-title">
<title id="logo-title">A blue circle beside the word Hello</title>
<circle cx="50" cy="50" r="30" fill="#2563eb" />
<text x="95" y="58" font-size="24" fill="#0f172a">Hello</text>
</svg>
svg {
width: 100%;
max-width: 20rem;
height: auto;
}
An <svg> element is a canvas of its own with its own coordinates. The shapes inside it (circle, rect, text, and so on) are real elements, so CSS and JavaScript can reach every one.
Coordinates and viewBoxβ
Inside an SVG, the top-left corner is (0, 0). The x value grows to the right, and the y value grows downwards, which is the opposite of a school graph.
The viewBox attribute is the most important one. It has four numbers: min-x min-y width height. It defines the private coordinate space you draw in, and the browser scales that space to fit the real size of the <svg> element.
<svg viewBox="0 0 200 100" width="400"> <!-- shown twice as big, but nothing is redrawn by you -->
That is why SVG is "scalable": you draw once, in convenient numbers, and CSS decides the final size.
Try It: Move the Cameraβ
Change the viewBox numbers and see which part of the drawing is shown. Then change preserveAspectRatio, and watch how the picture fits a box of a different shape.
- HTML Code
- Browser Output
<!-- The whole picture -->
<svg width="280" height="160" viewBox="0 0 200 100">β¦</svg>
<!-- Zoomed in on the house -->
<svg width="280" height="160" viewBox="50 30 80 40">β¦</svg>
<!-- Fill the box and crop, instead of leaving gaps -->
<svg width="280" height="160" viewBox="0 0 200 100"
preserveAspectRatio="xMidYMid slice">β¦</svg>
<svg width="280" height="160"
viewBox="0 0 200 100"
preserveAspectRatio="xMidYMid meet">
The picture is always drawn in the same coordinates. The viewBox chooses which part of that space the 280 by 160 box shows, like a camera frame.
Remember:
- Always set a
viewBox. Without one, the SVG will not scale with its box. - A smaller
viewBoxwidth means zooming in. A larger one means zooming out. preserveAspectRatiodecides what happens when the box and theviewBoxhave different shapes:meet(fit it all, keep the shape),slice(fill the box, crop the edges), ornone(stretch, which distorts).- For a fluid layout, set
width: 100%; height: auto;in CSS, as with images.
Basic Shapesβ
| Element | Draws | Main attributes |
|---|---|---|
rect | A rectangle | x, y, width, height, rx (rounded corners) |
circle | A circle | cx, cy (centre), r (radius) |
ellipse | An oval | cx, cy, rx, ry |
line | A straight line | x1, y1, x2, y2 (needs a stroke) |
polyline | Connected lines (open) | points |
polygon | Connected lines (closed) | points |
path | Anything | d (a list of drawing commands) |
text | Text | x, y, font-size |
Try It: Draw a Shapeβ
Pick a shape, move its position and size, and read the code it writes.
- HTML Code
- Browser Output
<svg viewBox="0 0 200 100">
<circle cx="60" cy="40" r="15" fill="#38bdf8" stroke="#0f172a" stroke-width="3" />
<rect x="100" y="25" width="60" height="30" rx="8" fill="#f97316" />
<line x1="20" y1="85" x2="180" y2="85" stroke="#0f172a" stroke-width="2" />
</svg>
<svg viewBox="0 0 200 100">
<circle cx="60" cy="40" r="15"
fill="#38bdf8" stroke="#0f172a" stroke-width="3" />
</svg>
Pathsβ
A <path> draws with a list of commands in its d attribute. Each is a letter followed by numbers:
| Command | Meaning |
|---|---|
M x y | Move to a point, without drawing |
L x y | Draw a line to a point |
H x, V y | Draw a horizontal or vertical line |
Q cx cy x y | A quadratic curve with one control point |
C c1x c1y c2x c2y x y | A cubic curve with two control points |
A rx ry rotation large sweep x y | An arc |
Z | Close the shape |
An uppercase letter uses absolute coordinates. A lowercase letter is relative to the current point (l 10 0 means "10 to the right").
<path d="M 20 80 Q 60 10 100 80 T 180 80" fill="none" stroke="#2563eb" stroke-width="4" />
You rarely write long paths by hand. Draw in a design tool, export the SVG, and then tidy it (see Performance below).
Styling SVGβ
Shapes take their look from presentation attributes, such as fill and stroke, or from CSS. CSS wins when both are present.
<svg viewBox="0 0 100 100" class="badge">
<circle cx="50" cy="50" r="40" />
</svg>
.badge circle {
fill: #2563eb;
stroke: #1e3a8a;
stroke-width: 4;
}
.badge circle:hover {
fill: #1d4ed8;
}
Useful properties: fill, stroke, stroke-width, stroke-linecap, stroke-linejoin, stroke-dasharray, opacity, transform, font-family, font-size.
currentColor: Icons That Follow Your Textβ
Set fill="currentColor" (or stroke="currentColor"), and the shape uses the text colour of its parent. Then one icon works in every theme, including dark mode and hover states.
<button class="icon-button">
<svg viewBox="0 0 24 24" width="20" height="20" aria-hidden="true" focusable="false">
<path fill="currentColor" d="M12 2 2 7l10 5 10-5-10-5Z" />
</svg>
<span>Layers</span>
</button>
.icon-button { color: #1d4ed8; }
.icon-button:hover { color: #1e3a8a; } /* the icon changes with the text */
Grouping and Reuseβ
<g>groups shapes, so you can style or move them together:<g fill="#f97316" transform="translate(20 10)">β¦</g>.<defs>holds things you define but do not draw directly (gradients, patterns, symbols).<symbol>and<use>let you draw a shape once and reuse it many times, like a stamp:
<svg style="display:none">
<symbol id="icon-star" viewBox="0 0 24 24">
<path fill="currentColor" d="M12 2l3 7 7 .6-5.3 4.7L18.2 22 12 18l-6.2 4 1.5-7.7L2 9.6 9 9z" />
</symbol>
</svg>
<svg class="icon" width="24" height="24" aria-hidden="true"><use href="#icon-star" /></svg>
<svg class="icon" width="48" height="48" aria-hidden="true"><use href="#icon-star" /></svg>
Using <use href> to point at a symbol in the same page is simple and dependable. Pointing at a separate .svg file has more rules (it must be from the same origin, and some older browsers differ), so test it, or inline the sprite.
Use display:none carefully on the sprite: some tools hide gradients and masks inside it. Hiding it with width="0" height="0" style="position:absolute" is a safer alternative.
Gradientsβ
<svg viewBox="0 0 200 60">
<defs>
<linearGradient id="sunset" x1="0" y1="0" x2="1" y2="0">
<stop offset="0" stop-color="#f97316" />
<stop offset="1" stop-color="#be123c" />
</linearGradient>
</defs>
<rect width="200" height="60" rx="10" fill="url(#sunset)" />
</svg>
Check the text contrast over a gradient at its lightest point.
Text in SVGβ
<text x="10" y="30" font-size="20" fill="#0f172a">Revenue</text>
- SVG text is real text: it can be selected, searched, and read by screen readers, and it scales with the drawing. Prefer it to text converted into outlines.
- There is no automatic line wrapping. Break lines yourself with
<tspan>, or place the text in HTML next to the SVG. - Use
text-anchor="middle"to centre text on a point, anddominant-baselineto adjust vertical alignment. - Text scales with the SVG, so a small SVG on a phone can make the text tiny. Check readability at every size.
Four Ways to Include SVGβ
| Way | Example | Scripts and CSS from your page | Good for |
|---|---|---|---|
| Inline | <svg>β¦</svg> in the HTML | Yes, fully | Icons, charts, anything you style or animate |
img | <img src="logo.svg" alt="β¦"> | No. Scripts inside do not run | Logos and illustrations that are just pictures. Safe and cacheable |
object | <object data="logo.svg" type="image/svg+xml"> | Only from the same origin, through contentDocument | When you need a fallback and a separate file |
| CSS background | background: url(logo.svg) | No | Decoration |
A simple rule: use img for pictures, and inline SVG for anything interactive or themed. See Object for the third option, and Embed for why embed is rarely the right choice.
Accessibilityβ
An SVG is not automatically understood by assistive technology. You must decide what it is.
| The SVG is⦠| Do this |
|---|---|
| A meaningful image (a logo, a simple chart) | role="img", with <title> and aria-labelledby |
| A complex image (a detailed chart or diagram) | A title, a <desc> (aria-describedby), and a data table or text version on the page |
| Decorative | aria-hidden="true" (and focusable="false" for older browsers) |
| An icon inside a button or link | Hide the icon with aria-hidden="true", and give the button visible text or an aria-label |
| Interactive (clickable parts) | Use real <a> or <button> elements, or give shapes tabindex, a role, and keyboard handlers |
Try It: Label an SVGβ
Try each way of labelling the same chart, and see what a screen reader might say.
- HTML Code
- Browser Output
<svg viewBox="0 0 120 60" role="img"
aria-labelledby="t" aria-describedby="d">
<title id="t">Sales chart</title>
<desc id="d">Bars for April, May and June: 40, 48 and 60 units.</desc>
<rect x="15" y="30" width="20" height="25" />
<rect x="50" y="24" width="20" height="31" />
<rect x="85" y="10" width="20" height="45" />
</svg>
<svg viewBox="0 0 120 60">
<rect β¦/> <rect β¦/> <rect β¦/>
</svg>
A screen reader user may hear: Nothing helpful. Depending on the screen reader, it may be skipped, or announced as just βgraphicβ or βimageβ with no name, or its numbers may be read one by one.
Exact announcements differ between screen readers and browsers, so test with the ones your visitors use. For an important chart, also give a data table.
More points:
- Put the
<title>first inside the SVG, and connect it witharia-labelledby. Relying on the title alone (without the ARIA attributes) is less dependable across screen readers. - Do not use a tooltip alone to give information. The
<title>also shows a tooltip in some browsers, but touch and keyboard users never see it. - Do not rely on colour alone in charts. Add labels, patterns, or shapes.
- Keep contrast high: at least 3 to 1 for graphics that carry meaning, and 4.5 to 1 for text.
- Keep focus visible on any interactive part of the graphic.
Animation and Motionβ
SVG can be animated with CSS (the simplest), JavaScript, or SVG's own animation elements.
.spinner {
transform-origin: 50% 50%;
animation: spin 1.2s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
@media (prefers-reduced-motion: reduce) {
.spinner { animation: none; }
}
Always provide a way to pause anything that moves for more than about 5 seconds, avoid flashing more than three times a second, and respect prefers-reduced-motion. See Accessible Media.
Performanceβ
- SVG is small for simple graphics, but a detailed illustration or map with thousands of points can be heavy.
- Clean up exported files. Design tools add comments, metadata, and extra precision. A tool such as SVGO can often cut the size by half with no visible change.
- Reduce path precision (two decimal places are usually plenty).
- Do not inline huge SVGs in your HTML many times. Use a sprite, or load a file with
img. - For very large numbers of moving objects (thousands of particles), canvas may be faster.
- Compress with gzip or Brotli on the server. SVG is text, so it compresses very well.
SVG or Canvas?β
| SVG | Canvas | |
|---|---|---|
| Made of | Elements you can select, style, and listen to | One bitmap you draw into |
| Sharp at any size | Yes | Only if you redraw at the right resolution |
| Accessibility | Natural: text and structure exist | You must supply everything |
| Best for | Icons, logos, charts, diagrams, UI | Games, effects, thousands of moving objects |
Security and Privacy Notesβ
- SVG can contain scripts and links. An uploaded SVG file is not a harmless picture. It can run JavaScript if opened directly, or inline, or in an
objectoriframe. - Never put an untrusted SVG inline in your page, and never put one in an
objecton your own origin. - Show user-supplied SVG with
img, where scripts do not run, and sanitise uploads on the server (stripscript, event attributes such asonload,foreignObject, and external references). - Serve SVG with the right type (
image/svg+xml), and addX-Content-Type-Options: nosniff. - Build SVG text with
textContent, not by pasting visitors' words into markup, so they cannot inject elements. - Watch for external references. An SVG that loads fonts, images, or styles from other sites can reveal who is viewing it.
- Do not hide secrets in SVG. Comments, metadata, and hidden layers are all public. Remove editor metadata (author names, file paths) before publishing.
Global Audience Notesβ
- Keep text as real text so it can be translated and read in any language, and avoid text converted to outlines.
- Leave room for longer text. Translations can be much longer, and SVG does not wrap lines.
- Right-to-left languages: set
directionandtext-anchor="start"/endcarefully, and test mirrored layouts. Icons that show direction (arrows, back buttons) may need to flip. - Use fonts that cover the scripts you need, or a system font stack.
- Do not rely on colour meanings that differ between cultures, and add labels as well.
- SVG is light, which helps on slow connections and old phones. Prefer it to large PNG files for line art.
Common Mistakes to Avoidβ
- No
viewBox, so the graphic does not scale. - A fixed
widthandheightonly, so it overflows small screens. - No accessible name on a meaningful SVG, or no
aria-hiddenon a decorative one. - Relying on
<title>alone for important information. - Putting an untrusted SVG inline or in an
object. - Converting text to outlines, so it cannot be translated or read.
- Forgetting
strokeon aline, so nothing appears. - Using the wrong y direction (y grows down).
- Duplicate
idvalues across several inline SVGs, which breaks gradients andaria-labelledby. - Huge, unoptimised exports.
- Animating with no pause and no reduced-motion rule.
- Icon-only buttons with no name.
Practice Timeβ
Try these in your editor:
- Draw a circle, a rectangle, and a line inside an
<svg viewBox="0 0 200 100">. - Change the
viewBoxto zoom in on one shape. - Make the SVG fill its container with
width: 100%; height: auto;. - Draw a triangle with
polygon, and a curve withpath. - Style a shape with CSS, and add a
:hovercolour. - Make an icon with
fill="currentColor", and change the text colour around it. - Define a
symbolonce, anduseit three times at different sizes. - Add a
linearGradientbackground to a rectangle. - Add
role="img", a<title>, andaria-labelledby, then check the name in the accessibility panel. - Bonus: animate a shape with CSS, and add a
prefers-reduced-motionrule.
Quick Recapβ
Click a question to check your answer.
1. What does the viewBox do?
It defines the private coordinate space you draw in (min-x min-y width height), and the browser scales it to fit the SVG's real size. It is what makes SVG scalable.
2. Which direction does y grow in SVG?
Downwards. The point (0, 0) is the top-left corner.
3. Why use fill="currentColor" on icons?
The icon takes the surrounding text colour, so it follows hover states and themes (including dark mode) with no extra CSS.
4. How should you label a meaningful SVG?
Give it role="img", a <title>, and aria-labelledby. For complex graphics, add a <desc> with aria-describedby, and a data table or text version. Hide decorative ones with aria-hidden="true".
5. When should you use img instead of inline SVG?
When the SVG is just a picture. Scripts inside an img do not run, so it is safer, and it is simple to cache. Use inline SVG when you need to style, animate, or script it from your page.
6. Why is an uploaded SVG file risky?
SVG can contain scripts and external references. Show untrusted SVG with img, and sanitise uploads on the server. Never put them inline or in an object on your own origin.
Key Takeawaysβ
- SVG is a list of drawing instructions in markup. It stays sharp at any size.
- Always set a
viewBox, and let CSS control the final size. - Use basic shapes and
pathfor everything else, and remember that y grows down. - Style with CSS, and use
currentColorso icons follow your text colour. - Reuse shapes with
symbolanduse, and keep ids unique. - Use
imgfor pictures, inline SVG for anything you style or script. - Make SVG accessible:
role="img"withtitle,descfor complex graphics,aria-hiddenfor decoration, and a text version of important data. - Treat untrusted SVG as code: sanitise it, and show it with
img. - Respect reduced motion, optimise exported files, and keep text as real text.
Next, draw with pixels and script in Canvas, or return to the Embedded Content overview.