- Animated SVG canvases
10. Animated SVG canvases
One SVG file, many slides: the picture stays, parts appear, fail and recover.
## The journey on one screen
<!-- svg: assets/journey.svg placed -->
---
## Payment: declined, then paid
<!-- svg: assets/journey.svg +declined -->
<!-- steps: . -declined,retry,paid -->
- 21:04: the card fails a fraud check
- 21:05: an automatic retry goes through
The directive. <!-- svg: PATH words... --> inlines the SVG (so it can
use the deck's CSS tokens) and sets its state: the root <svg> gets
class="si-canvas" and data-state="words".
State words carry across slides. Plain words replace the state. When all
words start with + or -, they change the state the previous slide with
the same file ended in (after its steps): +declined above means "placed,
plus declined". Steps change it further (see Build steps).
The animation. On two consecutive slides with the same file, the incoming copy starts in the outgoing state and switches to its own, so every CSS transition in the SVG runs: parts fade in, move, change colour. The slide itself does not fade; only the words around the canvas do.
Authoring contract#
-
Give the root
<svg>aviewBox(orwidthandheight, from whichsiderives one) and a class of your own, for exampleclass="journey". -
Colour with the deck tokens and a fallback, so the file also looks right on its own:
fill: var(--si-surface, #fff). -
Show and hide parts with helper attributes (no CSS needed):
Attribute When the state contains the word data-show="paid retry"visible (hidden otherwise); fades data-hide="delivered"hidden; fades. data-hidewins overdata-showdata-alert="declined"shapes inside get the --si-critstroke and pulse; atextwith it turns--si-critdata-ok="paid"shapes inside get the --si-okstroke; atextwith it turns--si-okdata-flow="shipped"the path gets marching dashes data-packets="paid"dots travel along the path ( data-packets-count,data-packets-durseconds,data-packets-r)data-grow="loaded"the element scales up from its base (0 → full height, grown from the bottom) — for bars data-move="sent"the element travels to the end of its own offset-path(set the path in your SVG CSS)data-count="s1:0 s2:4200"the element's text ticks to the number of the active state word (last match wins) Each attribute takes several words (any of them matches).
-
For anything else write CSS inside the SVG with the state selector and a transition, and it animates between slides and steps:
CSS.journey .token { transition: transform 1s ease; } .journey[data-state~="paid"] .token { transform: translateX(235px); } .journey[data-state~="packed"] .token { transform: translateX(470px); }Same specificity, later rule wins: list stages in order so the furthest one applies when several words are present.
-
No need to scope animations to the current slide; animations pause on hidden slides.
-
Never write a less-than sign inside the SVG
<style>(not even in a comment): inline SVG styles are parsed as markup in HTML. -
Ids are prefixed per copy (
c3-box), andurl(#id)/href="#id"references with them.<image href>files are embedded. -
With reduced motion, transitions are instant and packets are hidden.
si new and examples/assets/journey.svg are a complete worked example. To
build one from scratch, run si new-canvas <name> for a working, commented
starter canvas plus a deck that drives it, and follow the step-by-step recipe in
docs/authoring-canvases.md.