SVG in HTML: diagrams that scale and follow your colours

Inline SVG is a drawing written into the HTML as markup rather than attached as a picture. It scales without blurring, takes its colours from the page's CSS, is read by screen readers when labelled, and travels with the file because it is part of the file.

SVG in HTML means writing a drawing directly into the page as an <svg> element, so the browser draws it from markup rather than displaying a picture file.

The markup. The highlighted line is the part this term is about.
The markup. The highlighted line is the part this term is about.

Every diagram on this site is inline SVG for four reasons: it stays sharp at any zoom, its colours can come from CSS variables and so follow dark mode, its text is text for screen readers and search, and it is embedded by definition, so it cannot go missing when the file is sent.

This guide covers why inline for diagrams, why viewBox is the attribute that matters, labelling, and when a raster image is still right.

<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
  <circle cx="50" cy="50" r="40" fill="none" stroke="currentColor" stroke-width="8"/>
</svg>

That is a ring. It is text, it is in your document, and it will be sharp on any screen at any zoom.

Why inline SVG in HTML for diagrams

Inline SVG PNG file Base64 PNG
Extra request No Yes No
Sharp when zoomed Yes No No
Colours from CSS Yes No No
Follows dark mode Yes No No
Size for a diagram Small Medium Large
Text inside is searchable Yes No No
Good for photographs No Yes Acceptable
Inline SVG goes straight into the HTML as markup. It scales without blurring, takes its colours from the page's CSS, is read by screen readers when labelled, and travels with the file because it is part of the file.
Inline SVG goes straight into the HTML as markup. It scales without blurring, takes its colours from the page's CSS, is read by screen readers when labelled, and travels with the file because it is part of the file.

The third and fourth rows are the ones that matter most in practice. A PNG diagram stays light-themed when the page goes dark, which looks like a mistake. An SVG whose strokes use CSS variables simply follows.

Every diagram on this site is inline SVG for that reason.

A screenshot of the page ✗ Text is not selectable ✗ Numbers cannot be copied ✗ Charts stop being interactive ✗ Goes stale the moment data changes The live page ✓ Text selects and copies ✓ Tables can be read by tools ✓ Charts still respond to hover ✓ Update the source, link is current
A chart as a picture versus a chart as markup. Only one of them can be recoloured, read aloud, or copied from.

viewBox is the important attribute

<!-- scales to its container -->
<svg viewBox="0 0 720 200" xmlns="http://www.w3.org/2000/svg">

<!-- locked to one size -->
<svg width="720" height="200">

viewBox="0 0 720 200" sets an internal coordinate space 720 by 200. Everything inside is drawn in those units, and the whole thing scales to fit its container — so one piece of markup works at 360px and at 1400px.

Adding fixed width and height attributes defeats that. Size it with CSS instead:

svg { display: block; width: 100%; height: auto; }

Colours from CSS

<svg viewBox="0 0 200 40">
  <rect width="140" height="20" fill="var(--accent)"/>
  <line x1="0" y1="34" x2="200" y2="34" stroke="var(--line)" stroke-width="1.5"/>
</svg>

var() works inside SVG attributes. So does currentColor, which takes the element's text colour — useful for icons that should match surrounding text without any extra rules.

Writing a diagram by hand

Five elements cover most of it:

<svg viewBox="0 0 400 120" xmlns="http://www.w3.org/2000/svg">
  <!-- box -->
  <rect x="10" y="30" width="150" height="60" rx="10"
        fill="var(--bg-hover)" stroke="var(--line)" stroke-width="1.5"/>

  <!-- centred label -->
  <text x="85" y="66" text-anchor="middle" font-size="14" fill="var(--fg)">Source</text>

  <!-- arrow -->
  <defs>
    <marker id="ah" viewBox="0 0 10 10" refX="9" refY="5"
            markerWidth="7" markerHeight="7" orient="auto">
      <path d="M0 0 L10 5 L0 10 z" fill="var(--line)"/>
    </marker>
  </defs>
  <line x1="165" y1="60" x2="230" y2="60" stroke="var(--line)"
        stroke-width="1.5" marker-end="url(#ah)"/>
</svg>

Two details that catch people. text-anchor="middle" centres text on its x coordinate — without it, x is the left edge and every label sits off to one side. And the y of a text element is its baseline, not its top, so vertical centring is roughly middle + font-size / 3.

Accessibility

<figure role="group" aria-label="Signups by source: search 998, direct 527, referrals 176">
  <svg viewBox="0 0 720 160" aria-hidden="true">…</svg>
  <figcaption>Signups by source, week of 7 Sep. Total 2,119.</figcaption>
</figure>

The label carries the actual values, because a screen reader can do nothing with the shapes. Mark the SVG itself aria-hidden so its contents are not read out as a stream of coordinates.

The id collision trap

Markers, gradients and clip paths are referenced by id, and ids are global to the page. Two SVGs both defining #ah means the second silently uses the first one's definition — usually invisible, occasionally baffling. Prefix ids per diagram if you have several on a page.

When to reach for which

Content Use Why
Diagram, flow, chart Inline SVG Scales, follows CSS colours, no request
Icon Inline SVG Same, and currentColor matches surrounding text
Logo Inline SVG, or a file if reused across pages Sharp at every size
Screenshot PNG or WebP Pixels, not shapes
Photograph WebP or JPEG Same
Anything needing dark mode Inline SVG A fixed image cannot follow

That last row is the decisive one in practice. A PNG diagram stays light-themed while the page goes dark, and there is no fix short of shipping two images — see dark mode CSS.

Three traps

id collisions. Markers, gradients and clip paths are referenced by id, and ids are global to the page. Two diagrams both defining #arrow means the second silently uses the first one's definition. Prefix per diagram.

Text baselines. The y of a <text> element is its baseline, not its top. Vertical centring is roughly middle + font-size / 3, which is why hand-written labels often sit slightly high.

Fixed dimensions. width and height attributes lock the SVG to one size and defeat the viewBox. Size it in CSS instead, and add max-width: 100% so it cannot overflow on a phone — the same rule that prevents most mobile overflow.

Inline, as an image, or as a data URI

The same drawing can arrive three ways. Inline in the HTML, it can be styled by the page's CSS and read by a screen reader, and it is part of the file.

In an <img src="chart.svg">, it is a separate file that can be cached and reused across pages, but the page's CSS cannot reach inside it and it goes missing if the file does not travel.

As a data URI in an img, it travels with the page but is still sealed off from the page's styles.

For a diagram in a document that will be sent, inline wins; for an icon used on fifty pages of a site, a file wins.

Keeping inline SVG small

Drawing tools export SVG with editor metadata, absolute coordinates to six decimal places, and a class per shape. Run the export through an optimiser and it usually shrinks by more than half with no visible change. Then check the result has a viewBox and no width and height attributes, because optimisers preserve whatever the tool wrote.

Using inline SVG: 4 steps

  1. Draw it with a viewBox and no fixed width or height attributes. The viewBox defines the drawing's own coordinates; the element then scales to whatever container it sits in.
  2. Take colours from CSS variables. fill: var(--accent). The drawing follows the page's palette and its dark mode.
  3. Label it. role="img" and an aria-label that states the values, or a <title> element inside the svg.
  4. Keep it inline for diagrams and charts; use an image for photographs. SVG is shapes and text. A photograph is pixels and belongs in an <img>, at a full address or embedded.

Questions people ask

Why use SVG instead of an image file?

It is usually smaller for diagrams, stays sharp at any zoom, is part of the file with no extra request, and its colours can come from CSS so it follows dark mode.

What is a viewBox?

The internal coordinate system. With a viewBox and no fixed width or height, the SVG scales to whatever container it is in.

How do I make an SVG accessible?

A title element inside it for a name, and role="img" with an aria-label on the wrapper for anything conveying information.

When is a raster image better?

For photographs. SVG describes shapes; a photograph is millions of pixels with no shapes to describe.

Keep reading

SVG stroke-dasharray and stroke-dashoffset, with live examplesHow stroke-dasharray and stroke-dashoffset cut an SVG stroke into dashes, draw a line on scrCSS fill and stroke on SVG, including holes and outlinesStyle SVG shapes from CSS with fill and stroke, cut holes with fill-rule evenodd, fade them The SVG tag in HTML, from viewBox to pathsHow the svg tag in HTML works: the viewBox coordinate system, rect, circle, line, polygon anBase64 images in HTML: how and when to embed a pictureA base64 image in HTML is the file's bytes written as text inside the img tag, so the picturImages not showing in an HTML page: the 5 causes and the fix for eachImages not showing in your HTML file? The five causes, how to tell them apart in the NetworkHTML charts without JavaScript, and how to share themHTML charts without JavaScript: a bar chart in fourteen lines of CSS, when SVG is the right Dark mode CSS: one media query and a set of variablesDark mode CSS is one media query, prefers-color-scheme: dark, that redefines the page's coloHTML projects without CSSNine HTML projects that need no CSS at all, what each one teaches, and why a page built on bHTML QR code scannerBuild a QR scanner with getUserMedia and BarcodeDetector, with a library fallback. Why the cUse Mermaid in a single HTML fileUse Mermaid in a plain HTML file: one module script turns diagram text into SVG. Live examplD3.js in a single HTML fileDraw a D3.js chart in one HTML file with a script tag, an svg and a few lines of code. Live Lottie in React and in a single HTML fileThe lottie-react example needs npm and a bundler. The same Lottie animation runs in one HTMLSVGSVGElement: the JavaScript object behind the svg tagSVGSVGElement is the JavaScript object behind an inline svg tag. A short guide with live exaBuild a parcel viewer in one HTML5 fileBuild a parcel viewer in one HTML5 file: draw parcels from GeoJSON as SVG, click for detailsThe SVG path element in HTML, and SVGPathElement in JavaScriptWhat the SVG path element is in HTML and what SVGPathElement gives you in JavaScript: measur