Skip to main content

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 viewBox that controls its scale
  • Basic shapes, paths, text, groups, and gradients
  • Styling with CSS and currentColor
  • Reusing shapes with symbol and use
  • Four ways to include SVG, and which to choose
  • Making SVG accessible
  • Animation, performance, security, and common mistakes

The Basics​

index.html
<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>
style.css
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.

index.html
<!-- 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>

Remember:

  • Always set a viewBox. Without one, the SVG will not scale with its box.
  • A smaller viewBox width means zooming in. A larger one means zooming out.
  • preserveAspectRatio decides what happens when the box and the viewBox have different shapes: meet (fit it all, keep the shape), slice (fill the box, crop the edges), or none (stretch, which distorts).
  • For a fluid layout, set width: 100%; height: auto; in CSS, as with images.

Basic Shapes​

ElementDrawsMain attributes
rectA rectanglex, y, width, height, rx (rounded corners)
circleA circlecx, cy (centre), r (radius)
ellipseAn ovalcx, cy, rx, ry
lineA straight linex1, y1, x2, y2 (needs a stroke)
polylineConnected lines (open)points
polygonConnected lines (closed)points
pathAnythingd (a list of drawing commands)
textTextx, y, font-size

Try It: Draw a Shape​

Pick a shape, move its position and size, and read the code it writes.

index.html
<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>

Paths​

A <path> draws with a list of commands in its d attribute. Each is a letter followed by numbers:

CommandMeaning
M x yMove to a point, without drawing
L x yDraw a line to a point
H x, V yDraw a horizontal or vertical line
Q cx cy x yA quadratic curve with one control point
C c1x c1y c2x c2y x yA cubic curve with two control points
A rx ry rotation large sweep x yAn arc
ZClose 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, and dominant-baseline to 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​

WayExampleScripts and CSS from your pageGood for
Inline<svg>…</svg> in the HTMLYes, fullyIcons, charts, anything you style or animate
img<img src="logo.svg" alt="…">No. Scripts inside do not runLogos and illustrations that are just pictures. Safe and cacheable
object<object data="logo.svg" type="image/svg+xml">Only from the same origin, through contentDocumentWhen you need a fallback and a separate file
CSS backgroundbackground: url(logo.svg)NoDecoration

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
Decorativearia-hidden="true" (and focusable="false" for older browsers)
An icon inside a button or linkHide 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.

index.html
<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>

More points:

  • Put the <title> first inside the SVG, and connect it with aria-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?​

SVGCanvas
Made ofElements you can select, style, and listen toOne bitmap you draw into
Sharp at any sizeYesOnly if you redraw at the right resolution
AccessibilityNatural: text and structure existYou must supply everything
Best forIcons, logos, charts, diagrams, UIGames, 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 object or iframe.
  • Never put an untrusted SVG inline in your page, and never put one in an object on your own origin.
  • Show user-supplied SVG with img, where scripts do not run, and sanitise uploads on the server (strip script, event attributes such as onload, foreignObject, and external references).
  • Serve SVG with the right type (image/svg+xml), and add X-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 direction and text-anchor="start"/end carefully, 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​

  1. No viewBox, so the graphic does not scale.
  2. A fixed width and height only, so it overflows small screens.
  3. No accessible name on a meaningful SVG, or no aria-hidden on a decorative one.
  4. Relying on <title> alone for important information.
  5. Putting an untrusted SVG inline or in an object.
  6. Converting text to outlines, so it cannot be translated or read.
  7. Forgetting stroke on a line, so nothing appears.
  8. Using the wrong y direction (y grows down).
  9. Duplicate id values across several inline SVGs, which breaks gradients and aria-labelledby.
  10. Huge, unoptimised exports.
  11. Animating with no pause and no reduced-motion rule.
  12. Icon-only buttons with no name.

Practice Time​

Try these in your editor:

  1. Draw a circle, a rectangle, and a line inside an <svg viewBox="0 0 200 100">.
  2. Change the viewBox to zoom in on one shape.
  3. Make the SVG fill its container with width: 100%; height: auto;.
  4. Draw a triangle with polygon, and a curve with path.
  5. Style a shape with CSS, and add a :hover colour.
  6. Make an icon with fill="currentColor", and change the text colour around it.
  7. Define a symbol once, and use it three times at different sizes.
  8. Add a linearGradient background to a rectangle.
  9. Add role="img", a <title>, and aria-labelledby, then check the name in the accessibility panel.
  10. Bonus: animate a shape with CSS, and add a prefers-reduced-motion rule.

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 path for everything else, and remember that y grows down.
  • Style with CSS, and use currentColor so icons follow your text colour.
  • Reuse shapes with symbol and use, and keep ids unique.
  • Use img for pictures, inline SVG for anything you style or script.
  • Make SVG accessible: role="img" with title, desc for complex graphics, aria-hidden for 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.