Making SVG move
intermediate·8 min·Markdown for agents
What this lesson teaches
Choose the right animation engine for where an SVG lives, draw a line on with two CSS properties, and stop rotations from orbiting the corner.
Press replay. Then grab the slider and drag it yourself.
stroke-dasharray: 1;
stroke-dashoffset: 1 → 0;
That line is not being drawn. It is already all there. It is hidden behind one very long dash, and the dash is sliding out of the way. That trick, and a handful like it, covers most of the SVG animation you will ever need.
First question: where does the file live?
Before easing, before duration, ask where the SVG renders. That decides which tools can even reach it.
| Where it renders | What can animate it |
|---|---|
| Inline in the page (React, HTML), reacting to clicks and state | CSS transitions or the Web Animations API |
| Inline, but driven by a drag, a spring or a long timeline | Motion or GSAP, whichever the project already uses |
Loaded as <img>, a background image, in an email, on social, in Figma | Only what is inside the file: CSS @keyframes in its own <style>, or tags |
| A rich After Effects export that already needs a player | or Rive, and only then |
The third row catches people out. An SVG loaded as an image is sealed: your page's CSS and JavaScript can't touch it. Animation has to travel inside the file. CSS keyframes are the most portable choice; SMIL still wins for moving something along a path with <animateMotion>.
Drawing a line on
The trick in the demo uses , which turns a stroke into dashes, and stroke-dashoffset, which slides them along.
Measuring a path's length is fiddly, so skip it. Set pathLength="1" on the path and the browser treats the whole line as one unit long, whatever its real size.
path {
stroke-dasharray: 1; /* one dash, as long as the whole path */
stroke-dashoffset: 1; /* pushed fully out of view */
stroke-linecap: round; /* soft ends while it draws */
animation: draw 1600ms cubic-bezier(0.65, 0, 0.35, 1) forwards;
}
@keyframes draw { to { stroke-dashoffset: 0; } }Drawing is one of the few places an ease-in-out curve is right: the line is moving across the screen on its own, not answering a click.
The corner problem
Now press rotate, a few times.
default: (0, 0)transform-box: fill-box;
transform-origin: center;In HTML, a rotation turns around the element's center. In SVG, it turns around (0, 0), the top-left corner of the whole drawing. So every rotation and scale you copy from HTML orbits the corner.
The fix is two lines. tells the browser to measure from the shape's own box, and then transform-origin means what you expect.
.gear { transform-box: fill-box; transform-origin: center; }
.arm { transform-box: fill-box; transform-origin: 10% 20%; } /* the shoulder */For a mascot, put the origin where the part would hinge in life: a shoulder, a wrist, a lid.
Most "my SVG animation goes haywire" bugs are this one.Keep it cheap
The rules from motion apply unchanged. transform and opacity are cheap: the browser moves a finished picture. Animating d, points, r, stroke-width or a filter makes it repaint every frame. Fine for one small icon; ruinous for a whole scene.
- Move groups, not paths. Animate
<g id="arm">,<g id="eyes">, not three hundred separate paths. - Stagger by 30 to 80ms. Paired parts (two eyes, two arms) share duration and easing.
- Budget the file. About 2 KB for an animated icon, 10 to 30 KB for a hero mascot. Reuse shapes with
<use>, move them with transforms instead of redrawing.
Reduced motion travels with the file
Some people turn on "reduce motion" in their system settings because movement makes them unwell. Inline SVG respects it only if you wrote the media query. A self-contained file can carry its own:
@media (prefers-reduced-motion: reduce) {
.gear, path { animation: none; }
}That query works even inside a file loaded as <img>. SMIL can't read it, so a SMIL-only file needs a still fallback image.
A copied animated icon with its own keyframes and no reduced-motion query is the most common miss in generated UI. Check every one.
Ask for it precisely
Animate this SVG. It is used as <img> in our marketing page, so the animation must live inside the file. - CSS @keyframes in the file's own <style>, selectors scoped under the root id - draw the outline on with pathLength="1", stroke-dasharray: 1, stroke-dashoffset 1 → 0, round linecaps - the lid rotates around its hinge: transform-box: fill-box with transform-origin at the hinge - only transform, opacity and stroke-dashoffset animate - @media (prefers-reduced-motion: reduce) shows the finished drawing - keep it under 10 KB; report the final size
Quick check
A logo SVG ships as an <img> tag on your landing page. Your agent added the animation to the page's global CSS file. What happens?
In the skill
The nodes your agent reads when it does this work.
Connected lessons
What this lesson builds on and leads to, across every course.