| name | presentation-factory-animations |
| description | Animation choreography for the deck. Owns slide transitions (between slides), build animations (step-by-step reveal within a slide), scroll-triggered reveals (for scroll-export variants), and hover micro-interactions on player chrome. Generates per-slide animation manifests the html-player reads. Reduced-motion aware, GPU-only properties, speaker-controllable.
|
| type | skill |
| plugin | presentation-factory |
| tags | ["type/skill","plugin/presentation-factory","scope/specialty","topic/animation"] |
| status | active |
Goal
Add animation to a deck where animation clarifies meaning, not where it decorates. Make every animation reduced-motion-safe, GPU-only, and presenter-controllable.
When to invoke
- After
presentation-factory-slide-composer renders slide HTML
- Before
presentation-factory-html-player assembles the player
- When the user requests changes to a slide's build sequence
Required context
references/presentation-factory-foundations.md (Animations and interactions section).
Animation taxonomy
1. Slide transitions (between slides)
The transition between slide N and slide N+1. Subtle by default.
| Transition | When to use | CSS |
|---|
fade | Default for all slides | opacity 240ms ease-out |
slide-up | Section openers (gives forward momentum) | transform: translateY(2vh) → 0, opacity, 320ms ease-out |
cross-dissolve | Within-section content slides | Two-layer crossfade, 200ms |
cut | When transitions feel cluttered (rapid-fire stats sequence) | No animation, instant |
Default: fade. Override per slide in the manifest via transition: slide-up etc.
2. Build animations (within a slide)
Step-by-step reveal triggered by spacebar or right-arrow in presenter mode. Each build step is one logical chunk: one bullet, one stat, one diagram node, one column.
Build steps are declared in slide HTML via data-animate="step-N":
<div class="stat" data-animate="step-1">
<div class="number">$150–275B</div>
<div class="label">Fashion gen-AI upside (McKinsey)</div>
</div>
<div class="stat" data-animate="step-2">
<div class="number">90%</div>
<div class="label">of AI pilots stall</div>
</div>
The player reads these and reveals them in order on keypress. Initial state of data-animate=step-N elements is opacity: 0; transform: translateY(8px). Reveal animation: 240ms cubic-bezier(0.16, 1, 0.3, 1) to opacity: 1; transform: none.
3. Scroll-triggered reveals (scroll-export variant)
If the deck is exported for scroll-based delivery (rare but supported), the same data-animate attributes drive IntersectionObserver-based reveals. The build-step pattern works for both modes.
4. Hover micro-interactions
Player chrome only (not slide content):
- Nav buttons: scale 1.04 on hover, 120ms ease-out
- Thumbnail nav: opacity 0.7 → 1.0 on hover, 80ms
- Drawing tool toggle: subtle pulse when active
Where animation earns its place
Animate to:
- Sequence ideas the speaker is verbally introducing (build steps for stats, bullets, diagram nodes)
- Direct focus during a comparison (left side, then right side, then both highlighted)
- Reveal a punchline (the big number arrives after the setup)
Do NOT animate to:
- Make the deck feel "modern" (it's decoration; the audience notices)
- Spin or bounce text (motion sickness, breaks reduced-motion)
- Auto-cycle through content without speaker control (it competes with the speaker)
- Add particle effects, parallax, 3D rotation (consultancy-tech-startup tells)
Reduced-motion compliance
Every animation must check @media (prefers-reduced-motion: reduce) and fall back to:
- Build animations: instant reveal (still step-by-step, but no opacity/transform transition)
- Slide transitions: instant cut
- Hover micro-interactions: no animation, just the end state
The composer wraps animation CSS in a media query block so the fallback is automatic. The player respects the user's system preference.
GPU-only property rule
Only animate properties that the GPU can composite without re-layout:
transform (translate, scale, rotate)
opacity
filter (sparingly: blur is expensive)
Never animate:
width, height, top, left, padding, margin (causes layout thrash)
color, background-color for large surfaces (causes paint cost on every frame)
If a transition seems to require width/height, use transform: scaleX() instead.
Speaker controls (player reads these)
The player exposes:
- Right arrow / Spacebar: advance one build step. If no more steps, advance to next slide.
- Left arrow / Shift+Spacebar: go back one step. If at first step, go back to previous slide.
- R: replay current slide's animation from step 1
- A: toggle all animations off for current slide (useful for screen recording)
- Shift+A: toggle all animations off for the entire deck
Workflow
- Read
10_Content.md and rendered slide HTML in slides/
- For each slide, decide:
- Transition to this slide from the previous slide (default
fade)
- Build steps within this slide (identify which elements should reveal in sequence)
- Annotate the slide HTML with
data-animate="step-N" attributes on appropriate elements
- Wrap any slide-specific animation CSS in
@media (prefers-reduced-motion: no-preference) blocks
- Generate
WIP/[deck]/slides/animations.manifest.json with the full animation map:
{
"slides": [
{
"number": 1,
"transition_in": "fade",
"build_steps": 0
},
{
"number": 3,
"transition_in": "slide-up",
"build_steps": 3,
"step_descriptions": ["Stat 1 reveal", "Stat 2 reveal", "Stat 3 reveal"]
}
]
}
- Log animation decisions to
WIP/[deck]/compose.log
Output
- Slide HTML files annotated with
data-animate attributes
slides/animations.manifest.json with the choreography map
compose.log entry summarizing animation decisions