# An SVG is markup, not a picture

> Spot the problems in a generated SVG and get an agent to return a clean one that scales, recolors with the theme and can be read by a screen reader.

Module: SVG and icons · Level: beginner · 7 min · https://school.agents.org.in/visual/svg/clean-svg

Here is the same bell icon twice. One came straight out of a generator. The other was cleaned up in about a minute. Flip between them, then pick a color.

*[Interactive demo: Same bell. The generated one ignores the box CSS gives it and ignores the color you pick. The cleaned one does both, in a third of the bytes.]*

The generated file looks fine at its own size. Ask it to be bigger and it refuses. Ask it to turn blue and it stays dark gray. Those are not drawing problems. They are **markup** problems, and they are the whole subject of this lesson.

## Why the markup matters

An SVG (Scalable Vector Graphics: an image written as text (shapes, lines and curves), so it stays sharp at any size and you can style and animate its parts with CSS) is an image written as text. Every circle and curve in it is an element on the page, the same kind of thing as a button or a heading. That is what makes it special: you can color it with CSS, animate its parts, and give it a name a screen reader can say.

It also means the quality of the text decides everything that comes later. Whether it follows dark mode. Whether someone can animate the arm without redrawing the mascot. Whether it weighs 2 KB or 200 KB.

> **In plain words.** A PNG is a grid of colored dots. An SVG is a set of instructions: "move here, draw a curve to there, fill it with this color." Instructions can be edited, restyled and moved; dots can only be repainted.

## The five fixes that matter most

**Step: viewBox, and no fixed size.** The viewBox (The attribute that sets an SVG's own coordinate grid, like "0 0 24 24". With it, the drawing scales to whatever size CSS gives it; without it, the icon is stuck at one size) (`viewBox="0 0 24 24"`) sets the drawing's own grid. With it, the icon fills whatever box CSS gives it. Hard-coded `width="24" height="24"` on the root is how icons refuse to scale. Keep the viewBox, drop the fixed size.

**Step: Color from outside.** Fill a one-color icon with currentColor (A CSS keyword meaning "whatever the text color is here". An icon filled with currentColor changes color with the text around it, including in dark mode) and it takes the text color around it. Illustrations with several colors use CSS variables with a fallback: `fill="var(--mascot-skin, #f2c9a0)"`. Dark mode becomes a variable flip, not a second file.

**Step: A name, or nothing.** If the graphic means something, give it `role="img"` and a title (A <title> as the first child of an SVG gives it a name a screen reader can read aloud. Pair it with role="img"; decorative SVGs get aria-hidden="true" instead) as its first child. If it is decoration, hide it with `aria-hidden="true"`. Never meaning with no name; never a name on decoration.

**Step: Sensible numbers.** Whole numbers on a sensible grid (24 for icons, 100 or 1000 for illustrations), one or two decimals at most. `12.000000` is six characters saying nothing.

**Step: Groups that mean something.** `<g id="arm-left">` can be animated by anyone. A hundred unnamed `<g>`s cannot, including by you next week. Order groups back to front, like layers in a design tool.

## Shapes, paths and repeats

Simple static shapes can stay as `<rect>` and `<circle>`: they are small and easy to read. Anything that will animate or change shape should be a path (The SVG element that draws any shape from a list of commands in its d attribute: M moves the pen, L draws a line, C draws a curve, Z closes the shape), a list of drawing commands, because a morph needs matching command lists (lesson three).

Repeats belong in one place. Gradients, masks and filters go in `<defs>`; a shape used five times goes in a `<symbol>` and is placed with `<use>`. One edit then changes every copy.

And never paste a photo inside an SVG. A base64 PNG wrapped in SVG is a PNG with worse compression.

## Shrink it, carefully

SVGO (A free tool that shrinks SVG files by removing editor junk and rounding numbers. It needs a few settings turned off so it keeps the viewBox, titles and the ids you animate) strips editor junk and rounds numbers. Run it with `--multipass`, but switch off the parts that break things: keep the viewBox, keep `<title>` and `<desc>`, keep the ids and `data-*` attributes you animate against. When several SVGs sit inline on one page, prefix their ids so two gradients called `g1` don't collide.

```bash
npx svgo icon.svg --multipass
```

Then look at it. A shrunken file that renders differently is not smaller, it is broken.

*(Inline `<style>` inside an SVG becomes global CSS the moment you paste the SVG into a page. Scope it under the SVG's id, or move it out.)*

## The tells of a generated SVG

Once you know them, you can spot them in the source in seconds:

**Generated (avoid):**

- `width` and `height` on the root, no viewBox
- every color a hard-coded hex
- nests of empty, unnamed `<g>`
- circles converted to 8-decimal paths
- the same gradient defined four times
- an embedded PNG

**Clean (do this):**

- viewBox only, size from CSS
- `currentColor` or CSS variables
- a few groups, each named
- primitives where static, paths where they move
- one gradient in `<defs>`
- no rasters

Each of these is cheap to fix and **expensive to leave in**, because every later step (theming, animating, shipping) pays for it again.

## Get your agent to do it

You don't have to fix these by hand. Hand the file to your agent with the rules spelled out.

**Clean an SVG (paste into your agent):**

```text
Clean up this SVG for production:
- keep viewBox, remove width/height on the root
- monochrome: fill="currentColor"; multi-color: CSS variables with fallbacks
- if it carries meaning: role="img" + <title> first, aria-labelledby; if decorative: aria-hidden="true" focusable="false"
- round coordinates to 1 decimal, remove empty groups and editor metadata
- name the groups by what they are (arm-left, eyes)
- no embedded rasters, gradients only in <defs>
Return the file and a short table of what changed and why.
```

**Check yourself:** An icon stays 24px no matter what width CSS gives it, and stays gray in dark mode. What is the likely fix?

- [ ] Redraw it at 48px and export again
- [x] Add a viewBox, remove the fixed width and height, and fill it with currentColor
- [ ] Wrap it in a div with overflow: hidden
- [ ] Convert it to a PNG with a transparent background

*Why:* Fixed width/height without a viewBox stops scaling, and a hard-coded hex ignores the theme. viewBox plus currentColor fixes both without redrawing anything.

## In the skill

- https://school.agents.org.in/skill/svg/MOC-svg.md
- https://school.agents.org.in/skill/svg/svg-creation.md
