Animation model
Frames, transitions, overlays — how multi-frame SVGs are composed.
An animated Domotion SVG is several captured frames stitched together with
CSS keyframes. There's no JavaScript at runtime — the browser plays the
animation natively from the SVG's embedded <style>. This
page explains the model so you know what's possible and where the seams are.
The frame
An animation frame is a single capture (the svgContent
returned from elementTreeToSvg) plus timing and an optional
transition to the next frame:
interface AnimationFrame {
svgContent: string; // from elementTreeToSvg()
duration: number; // hold time in ms
transition?: {
type: "crossfade" | "push-left" | "scroll";
duration: number;
};
overlays?: Overlay[]; // typing, tap ripples
}
You build a list of frames and pass them to
generateAnimatedSvg(config). The result is one SVG with one
@keyframes rule per frame and per overlay, summed timeline,
infinite loop.
Three transition types
| Type | Behavior | Best for |
|---|---|---|
crossfade |
Outgoing frame fades to 0 while incoming frame fades to 1, with overlap so shared pixels stay visible. | State changes within the same screen (form being filled, list updating). |
push-left |
Outgoing frame slides off to the left, incoming slides in from the right. | Stepping through a flow ("step 1 → step 2 → step 3"). |
scroll |
Both frames stay visible; opacity transitions only at the very end. | Scrolling content, where one frame logically continues into the next. |
Crossfade is the smart path
When every transition in a sequence is crossfade (or unset),
Domotion takes a fast path: it merges all frames into one de-duplicated
element tree with per-element visibility timelines. Stable elements
— a static logo, a header that doesn't change — are emitted once with
opacity: 1 throughout. Changing elements get step-end
keyframes that flip them on at the right moment.
Two payoffs:
- No flicker. Without merging, naive cross-fades would redraw every static pixel on every transition; small CSS-paint glitches at the boundary cause a one-frame dark flash. Merged frames just don't redraw those pixels.
- Smaller files. The text "Hello" doesn't appear five times in the SVG — once, with a timeline saying when it's visible.
push-left and scroll can't merge because the
geometry of each frame is different at every instant; they fall back to the
"emit each frame as a clipped group" path.
Overlays
Overlays sit on top of a frame's content for the duration of that frame. Two kinds today:
typing— animates a string being typed character-by-character at a given coordinate. Useful for terminal demos and "user types into a search box" patterns.tap— a Material-style ripple at a coordinate, suggesting a click or tap. Useful for "user clicks here" cues on mobile demos.
See the Typing & tap overlays guide for working examples.
Shared definitions
Glyph paths, gradient defs, and clip paths can be expensive when repeated
across frames. generateAnimatedSvg accepts a
sharedDefs string — markup that gets hoisted into the top-level
<defs> so frames can reference IDs across the
animation. The simplest pattern is to capture all frames first, then pull
their glyph defs out and pass them as sharedDefs; the test
runners and example scripts do exactly this.
Loop semantics
The generated animation always loops infinitely with
animation: ... infinite. Single-shot playback is not supported in
SVG without scripting. If you need playback control, embed the SVG via
<object> and toggle a CSS class on the host page, or wrap
your captures in a JavaScript controller.
Browser support
The output uses standard CSS keyframes and SMIL-free SVG, supported in
every modern browser. The same SVG renders in WebKit (Safari, iOS), Gecko
(Firefox), and Blink (Chrome, Edge). It also renders in image contexts
(<img>, background-image) — though some image
contexts mute animations; embed via <object> or
<iframe> if you find an environment that does.
Next
That's the model. Move on to the Build an animated demo guide for a runnable end-to-end recipe.