An SVG is markup, not a picture
beginner·7 min·Markdown for agents
What this lesson teaches
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.
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.
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24">
<g>
<g>
<path d="M12.000000 2.250000 C8.272078 2.250000 5.250000 5.272078 5.250000 9.000000 L5.250000 13.500000 L3.750000 16.500000 L20.250000 16.500000 L18.750000 13.500000 L18.750000 9.000000 C18.750000 5.272078 15.727922 2.250000 12.000000 2.250000 Z" fill="#2B2B2B"/>
</g>
<g>
<path d="M9.750000 19.500000 C9.750000 20.742641 10.757359 21.750000 12.000000 21.750000 C13.242641 21.750000 14.250000 20.742641 14.250000 19.500000 Z" fill="#2B2B2B"/>
</g>
<g></g>
</g>
</svg>- viewBox, so CSS sets the size
- No fixed width or height
- currentColor, follows the text color
- role and title, a screen reader can name it
- One decimal, not six
- No empty or nested-for-nothing groups
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 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.
The five fixes that matter most
viewBox, and no fixed size
The (
viewBox="0 0 24 24") sets the drawing's own grid. With it, the icon fills whatever box CSS gives it. Hard-codedwidth="24" height="24"on the root is how icons refuse to scale. Keep the viewBox, drop the fixed size.Color from outside
Fill a one-color icon with 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.A name, or nothing
If the graphic means something, give it
role="img"and a as its first child. If it is decoration, hide it witharia-hidden="true". Never meaning with no name; never a name on decoration.Sensible numbers
Whole numbers on a sensible grid (24 for icons, 100 or 1000 for illustrations), one or two decimals at most.
12.000000is six characters saying nothing.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 , 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
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.
$ npx svgo icon.svg --multipassThen 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
widthandheighton 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
- viewBox only, size from CSS
currentColoror 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 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.
Quick check
An icon stays 24px no matter what width CSS gives it, and stays gray in dark mode. What is the likely fix?
In the skill
The nodes your agent reads when it does this work.
- MOC — SVGAuthoring clean, editable, token-aware SVG; animating it with the right engine for where it lives; morphing paths that actually interpolate; and turning AI video or frame sequences into one editable animated SVG. The vector layer under icons, mascots, illustrations, and launch motion.
svg/MOC-svg - Creating SVGAuthor SVG that is clean, editable, and on the token system — viewBox always, paths when it will animate, currentColor and CSS variables, defs and symbols, named layers, SVGO with the right flags off, title and desc — and the tells of generated SVG.
svg/svg-creation
Connected lessons
What this lesson builds on and leads to, across every course.