Skip to content

Document animated snapshots - #347

Open
trotzig wants to merge 4 commits into
mainfrom
claude/animated-snapshots-docs
Open

Document animated snapshots#347
trotzig wants to merge 4 commits into
mainfrom
claude/animated-snapshots-docs

Conversation

@trotzig

@trotzig trotzig commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Why

animate (animated APNG snapshots) shipped in happo v6.15.0 but has no user docs yet. The options, hooks and driver API from the work adapting it to Airbnb's motion-capture findings are about to land as well.

What changed

  • New guide: docs/animated-snapshots.md, under Guides in the sidebar.
    • Getting started: a quick start, what can and can't be captured, and where animate can be set (target, Storybook story, page) plus the shorthands.
    • Options: a complete table with type, default and the version each option arrived in, a description of each, and guidance on choosing fps, duration, maxDuration and maxFrames.
    • Features: triggers (declarative and function), the virtual clock, animations that start late (discovery), chained animations (stages), sampling, limiting the capture with root, expectations and onExpectationFailure, and reduced motion.
    • Page-side API: the motion environment (data-happo-animate, and scoping your own "disable animations" CSS), the story hooks (setup, function trigger, verify) with the full trace reference, and how to write a driver (handle reference, guidelines, examples for Lottie, a custom frame loop, an async renderer and finishing a stage), plus the built-in video driver.
    • Reference: what Happo does to keep captures deterministic, what comes back (.apng, frameCount, durationMs), browser and integration support, troubleshooting, and TypeScript types.
  • configuration.md: a short ### Target animate section pointing to the guide, and a note under prefersReducedMotion about the per-capture override. I left the file's existing formatting untouched. It doesn't currently pass prettier --check on main, and reformatting it would have added about 100 lines of unrelated churn.

Versions

Options available today are labelled v6.15.0 (animate) and v6.16.0 (the nested prefersReducedMotion). Everything else (discovery, sampling, root, expect, onExpectationFailure, stages, drivers, the hooks and registerAnimationDriver) is labelled v6.17.0, on the assumption that happo/happo#594, happo/happo#595 and happo/happo#596 ship in the next minor release. Please adjust if they land elsewhere, and hold this PR until then. The worker side is happo/happo-snap#1322 through #1326.

Testing

  • docusaurus build succeeds. None of the broken-link or broken-anchor warnings it reports involve the new page or configuration.md; they all come from existing pages.
  • prettier --check passes on the new page and sidebars.json.

🤖 Generated with Claude Code

Adds an "Animated snapshots" guide covering every animate option (with
types, defaults and the version each arrived in), where animate can be set,
triggers, the virtual clock, discovery, chained stages, sampling, root,
expectations, reduced motion, the motion environment, story hooks and the
verify trace, and how to write a driver, with examples, plus the built-in
video driver, determinism guarantees, support matrix and troubleshooting.

configuration.md gets a short `animate` target option pointing to it, and
a note under `prefersReducedMotion` about the per-capture override.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Comment thread docs/animated-snapshots.md Outdated
- **`maxDuration`** — the ceiling for a derived duration, and the window used by
`mode: 'always'`.
- **`fps`** — samples per _second_ of animation. 500 ms at `fps: 6` is 3 frames,
not 6.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove the "500 ms at ..." part. It's confusing. People understand fps.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed in 7eaea34. The bullet now just says "samples per second of animation".

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Unresolved release-timing and animated snapshot documentation inaccuracies remain.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Adds user-facing documentation for Happo animated snapshots, including configuration, hooks, drivers, and troubleshooting.

Changes:

  • Adds the animated snapshots guide.
  • Links animation configuration from configuration.md.
  • Adds the guide to the Guides sidebar.
File summaries
File Summary Final review findings
sidebars.json Adds the animated snapshots guide to Guides. None.
docs/configuration.md Documents target animation settings and reduced-motion overrides. None.
docs/animated-snapshots.md Provides comprehensive animated snapshot documentation. Moderate (2 votes): gate v6.17.0 guidance until its dependencies ship or mark it unreleased; this also applies at line 134. Moderate (1 vote): correct the SMIL mode: 'always' guidance. Moderate (1 vote): avoid requiring explicit duration for SMIL. Moderate (1 vote): clarify that maxDuration is the fallback when duration is 'auto'. Nit (1 vote): use StoryAnimateConfig instead of StoryAnimateOptions. Moderate (1 vote): convert milliseconds to seconds in the Lottie example.
Review details

Suppressed comments (6)

docs/animated-snapshots.md:134

  • The type cell includes the function form of trigger, but this row dates all forms to v6.15.0. The function form is story-only and introduced with the v6.17.0 story-hook API (also noted in the section below), so users on v6.15/v6.16 may try to use a callback that is not available. Split the row or annotate the function form's v6.17.0 version.
| `trigger`              | `AnimateTrigger` \| function \| `null`                                  | `null`                           | v6.15.0 |

docs/animated-snapshots.md:153

  • These bullets say mode: 'always' is needed for SMIL, but the AnimateOptions.mode contract lists SMIL among the animations handled by mode: 'auto'; always is needed for animations with no reported duration, notably a virtual-clock requestAnimationFrame loop. As written, this sends SMIL users to an unnecessary mode and window.
- **`mode`** — `'off'` takes a still. `'auto'` captures an animation when there
  is one Happo can drive, and a still otherwise. `'always'` captures even when
  nothing on the page reports how long it runs, using `maxDuration` as the
  window — needed for `requestAnimationFrame` loops and SMIL.

docs/animated-snapshots.md:205

  • This guidance also tells users to hand-set duration for SMIL, although the API describes SMIL as a duration-bearing animation that mode: 'auto' can capture. That conflicts with the automatic-duration default and makes the recommendation unnecessarily brittle; reserve it for animations such as requestAnimationFrame loops and long videos, or document the actual SMIL exception.
- **`duration`** — leave it at `'auto'`; a hand-set duration goes stale when the
  CSS changes. Set it for SMIL, `requestAnimationFrame` loops, and long videos,
  which can't report a useful one.

docs/animated-snapshots.md:157

  • duration is documented as the explicit capture window, and the virtual-clock example below supplies duration: 2000 together with mode: 'always'. Saying that mode: 'always' uses maxDuration as its window without qualifying this makes that example contradictory and can lead users to capture the 4000 ms default instead of the requested 2000 ms. Describe maxDuration as the fallback when duration remains 'auto'.
- **`maxDuration`** — the ceiling for a derived duration, and the window used by
  `mode: 'always'`.

docs/animated-snapshots.md:852

  • The TypeScript section says StoryAnimateOptions is the type accepted by parameters.happo.animate, but the exported field type is StoryAnimateConfig, which also includes the documented true, false, and 'auto' shorthands. Please name the config type here so typed users do not miss those accepted forms.
`StoryAnimateOptions` is `AnimateOptions` plus the story-only hooks, and is what
`parameters.happo.animate` accepts.

docs/animated-snapshots.md:664

  • The handle contract below says seek(ms) receives milliseconds, but lottie-web's goToAndStop(value, false) interprets value as seconds. Passing timeMs directly seeks 1000× too far, so this example will render the end state for nearly every sampled frame; convert to seconds or pass a frame number with true.
        seek: timeMs => animation.goToAndStop(timeMs, false),
  • Files reviewed: 3/3 changed files
  • Comments generated: 1
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +6 to +7
_Available since happo v6.15.0. Options marked below as v6.17.0 need that
version or later._

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed that this can't go live before the release. Rather than gating it in the page, the plan is to merge this PR only once happo/happo#594–#596 have shipped, and to adjust the v6.17.0 labels if they land in a different version. The PR description says so.

Comment thread docs/animated-snapshots.md Outdated
chrome: {
type: 'chrome',
viewport: '1024x768',
animate: { clock: 'virtual' },

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can I use this setting in a test suite with both requestAnimationFrame driven animations and pure CSS animations?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. While the clock steps a story's requestAnimationFrame loop, the same capture still seeks its CSS animations, transitions and element.animate() animations to each frame's time. A story with both is captured in step, and a CSS-only story works too, through the clock's capture path.

The catch is where you turn capture on. If the target sets mode: 'auto' alongside clock: 'virtual', every story gets a maxDuration window, because the clock counts as something to drive. Static stories still end up as stills, but only after being captured frame by frame. So arm the clock with capture off on the target and turn capture on per story. A story can also opt back into plain seeking with clock: 'off', which it needs for stages. I added a "Mixing clock-driven and CSS animations" section saying this, and fixed the bullet that claimed a requestAnimationFrame story needs mode: 'always': under the clock, 'auto' falls back to maxDuration too (7eaea34).

Comment thread docs/animated-snapshots.md Outdated
chrome: {
type: 'chrome',
viewport: '1024x768',
animate: 'auto',

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we put prefersReducedMotion: false here as well?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, wait, that gets auto-overridden when there is an animation story, correct?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It isn't auto-overridden. animate's prefersReducedMotion defaults to null, which inherits the target's setting, and targets prefer reduced motion by default. That was deliberate in happo/happo-snap#1321, because the reduced-motion version is worth a snapshot too. Only a story that sets it explicitly gets it applied before render.

So yes, it belongs in the quick start. Without it, a site whose components respect the preference gets nothing but stills from animate: 'auto', which is confusing on first try. I added it with a paragraph on why, noting that it only affects animated captures and that you can leave it out to capture the reduced-motion version (7eaea34).

- Quick start sets prefersReducedMotion: false, with why, since pages that
  honour reduced motion otherwise leave nothing to capture.
- The trigger function form gets its own v6.17.0 row.
- mode/maxDuration/duration describe what actually happens: SMIL and the
  virtual clock capture under 'auto' with maxDuration as the window when
  duration is 'auto', and maxDuration caps an explicit duration too.
- Drop the '500 ms at fps: 6' aside.
- New section on mixing clock-driven and CSS animations on one target.
- TypeScript: parameters.happo.animate takes a StoryAnimateConfig.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@trotzig

trotzig commented Sep 11, 2026

Copy link
Copy Markdown
Contributor Author

Went through the suppressed findings from the Copilot review (7eaea34):

  • Function trigger dated v6.15.0: fixed. It now has its own "trigger as a function (story only)" row marked v6.17.0, and the trigger row lists only AnimateTrigger | null.
  • SMIL doesn't need mode: 'always': fixed. 'auto' does capture SMIL. It can't report a length, though, so the window falls back to maxDuration, and the duration advice for SMIL stays, now with that reason.
  • maxDuration as the fallback when duration is 'auto': fixed. It's described as the ceiling for every window, explicit or derived, and as the window used when duration is 'auto' but nothing reports a length.
  • StoryAnimateConfig: fixed. The TypeScript section now says parameters.happo.animate takes a StoryAnimateConfig (options or shorthands), and it's included in the import example.
  • Lottie goToAndStop in seconds: no change. With isFrame false, lottie-web multiplies the value by frameMult = animationData.fr / 1000 (build/player/lottie.js), so it takes milliseconds, which is what seek receives.

@lencioni lencioni left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Partial review, flushing feedback now and I'll continue reviewing.

Comment thread docs/animated-snapshots.md Outdated

`animate` can be set on a target, on a Storybook story, and on a page in the
[pages integration](full-page.mdx). Story and page options merge over the
target's, field by field.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It isn't clear to me exactly what this field by field merging means

edit: I see now that the code below is an example of this merging, but it isn't super clear that the document is set up like this. Maybe add some copy here that says see example below?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rewrote this. It now says options merge "one field at a time" and points to a new How options merge subsection with a worked example. (9ac88b2)

Comment thread docs/animated-snapshots.md Outdated
## Where to configure it

`animate` can be set on a target, on a Storybook story, and on a page in the
[pages integration](full-page.mdx). Story and page options merge over the

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if you are using the custom integration type? What options exist for you besides target?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added In the custom integration: animate goes on each happoCustom.registerExample() call, and the client already passes it through. The page also notes that Cypress and Playwright only take a target-level animate. All three are in the support table, linking to their docs. Hooks and the motion environment are marked as Storybook-only. (9ac88b2)

Comment thread docs/animated-snapshots.md Outdated

A story can add to the target's options — a target with
`{ mode: 'auto', fps: 15 }` and a story with `{ maxFrames: 60 }` capture with
all three — or opt out with `animate: false`.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could more clearly show the resulting configuration that would be used instead of just saying "all three"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new How options merge example spells out the result: target { mode: 'auto', fps: 15 } plus story { maxFrames: 60 } gives { mode: 'auto', fps: 15, maxFrames: 60 }. (9ac88b2)

Comment thread docs/animated-snapshots.md Outdated

What each one does:

- **`mode`** — `'off'` takes a still. `'auto'` captures an animation when there

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of having a table followed by a list that duplicates each of the values, I think we should structure this more like we document similar things like on the configuration page https://docs.happo.io/docs/configuration where each option is a heading.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. The table and the list are gone, and each option has its own heading with its version, type and default. The details for trigger, discovery, stages, sampling, root and expect now live under their option. The larger topics (the virtual clock, the motion environment, hooks and drivers) are still their own sections. (9ac88b2)

Comment thread docs/animated-snapshots.md Outdated
## Choosing values

Start with `animate: 'auto'` and change nothing else. The defaults are meant to
be boring, and most animations don't need tuning.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Boring?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reworded to: "The defaults work for typical UI animations like toasts, menus and spinners, and most animations don't need tuning." (9ac88b2)

Comment thread docs/animated-snapshots.md Outdated
```js title="ProgressRing.stories.js"
export const Filling = {
parameters: {
happo: { animate: { mode: 'always', duration: 2000, fps: 10 } },

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be nice to show an example of how someone might tie an animation duration in the story configuration to the actual duration in the component so they don't drift.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added under duration. The component exports ENTER_DURATION_MS and uses it in its animation, and the story imports the same constant for animate.duration. (9ac88b2)


```js
// 7 frames across the first half of the window, 3 across the rest
animate: { sampling: { split: 0.5, front: 7, tail: 3 } }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have thoughts about this API--I think we could make this more flexible, but I'm not sure if that's important or useful.

@lencioni lencioni Sep 11, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

animate: { sampling: [
  { stop: 0.5, frames: 7 },
  { stop: 1, frames: 3 },
] }
animate: { sampling: [
  { stop: 0.25, frames: 7 },
  { stop: 0.75, frames: 3 },
  { stop: 1, frames: 7 },
] }

Something like this maybe? We could take inspiration from the CSS linear gradient API.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this. A list of stops is more general than split/front/tail (which is just the two-stop case), and it will feel familiar from linear-gradient. sampling hasn't shipped yet (it's in happo/happo-snap#1323 and happo/happo#594, aimed at v6.17.0), so now is the cheapest time to change it. I haven't changed it in this PR: the API needs deciding first, then changes in happo-snap and the client, and the docs would follow.

Comment thread docs/animated-snapshots.md Outdated

`root` scopes the animations, not the image: the snapshot still shows the whole
story. Animations outside `root` are held on their first frame, as in a still.
If `root` matches nothing, nothing is captured and you get a still.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This option is a bit confusing to me, and I think it could benefit from an example.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added an example: a story renders an app shell with a spinner in its header, and root points at the notification panel. The page explains what happens: the panel is stepped through the capture, the spinner is held on its first frame, and the image still covers the whole story. (9ac88b2)

Comment thread docs/animated-snapshots.md Outdated

_Available since happo v6.17.0._

A capture that finds nothing falls back to a still. That's what makes

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's another usage of "a still". See my feedback earlier about this. I think we could improve this throughout the document.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed throughout. It now says "still image", or "still screenshot" where it's about the capture itself. (9ac88b2)

Comment thread docs/animated-snapshots.md Outdated

A capture that finds nothing falls back to a still. That's what makes
`mode: 'auto'` safe across a whole target, but for a story that exists to show
an animation it's the wrong kind of quiet: the animation broke, the snapshot is

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"the wrong kind of quiet" is a weird thing to say

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rewrote it: "it's not what you want for a story that exists to show an animation: if the animation breaks, the snapshot becomes a perfectly plausible still image and nothing fails." (9ac88b2)

Comment thread docs/animated-snapshots.md Outdated
after a click, content that arrives after a fetch — isn't there yet, and would
be missed.

`discovery` keeps looking for a while, one frame at a time, and takes over each

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of "for a while" we should probably say "keeps looking for settleMs milliseconds" or something.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It now reads: "Happo keeps looking for settleMs milliseconds after the capture starts, checking once per frame". (9ac88b2)

Comment thread docs/animated-snapshots.md Outdated

| Field | Default | Meaning |
| ----------- | ------- | ------------------------------------------------------------- |
| `settleMs` | `0` | How long to keep looking, in ms (up to 10000). `0` looks once |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If discovery finds something new, does that reset the timer, or is this a one-time hard limit? With a name like "settle" I would expect that the timer would reset every time something is found, but the documentation here slightly suggests that may not be the case.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a hard limit. The deadline is set when the capture starts, and finding something doesn't extend it. The docs now say that explicitly. Your reading of "settle" is fair, though. If we'd rather the timer reset on each new find, that's a change in happo-snap to make before this ships.

Comment thread docs/animated-snapshots.md Outdated
_Available since happo v6.17.0._

Some animations are a sequence where each step starts the next: a panel slides
in, and when it's done a list staggers in, and then a Lottie plays. The next

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"and when it's done a list staggers in" would be better as "then a list staggers in"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Applied: "a panel slides in, then a list staggers in, and then a Lottie plays". (9ac88b2)

Comment thread docs/animated-snapshots.md Outdated
- **`'image'`** (default) — the snapshot is replaced with an image describing
the failure: what was expected, and every animation that was found. It shows
up in the report as a diff that explains itself.
- **`'fail'`** — the run fails with the same description.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does it mean for a run to fail here? What is the experience like?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The job fails like any other "Failed on worker" failure, with the expectation's description as the error, and you see it in the Happo dashboard under Snap-requests. The docs now say this and link to the "Failed on worker" section of the debugging page. (9ac88b2)

Comment thread docs/animated-snapshots.md Outdated
the failure: what was expected, and every animation that was found. It shows
up in the report as a diff that explains itself.
- **`'fail'`** — the run fails with the same description.
- **`'warn'`** — the description goes to the run log and the capture is kept.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the run log and how can I find it? This might be a separate document that we need to write and link off to.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. We don't document anywhere where users can see the worker's log, so I dropped "run log" from the page (it now just says Happo logs a warning). Documenting where to find those logs would be a separate page.

Comment thread docs/animated-snapshots.md Outdated

_Available since happo v6.17.0._

Some animations never go through the browser's animation APIs — anything that

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The em-dashes here make this sentence confusing. A little rewrite that avoids the dashes would be good.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rewrote it without the dashes. (9ac88b2)

Comment thread docs/animated-snapshots.md Outdated
```

A driver named there that isn't registered is reported in the run log. To make a
capture fail when a driver finds nothing — a Lottie that failed to load, say —

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The em-dash section here is awkard too. Use parens with "e.g." instead

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed to "(e.g. a Lottie that failed to load)". (9ac88b2)

animate: { mode: 'auto', expect: { drivers: { lottie: 1 } } }
```

### The built-in `video` driver

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it worth mentioning that this is probably going to be useful for animations that use the video tag, and not full on videos that people will sit and watch?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, added. It's meant for short clips that are part of the UI, like a looping background or an animated illustration shipped as a video, not for long-form video people sit and watch. (9ac88b2)

Comment thread docs/animated-snapshots.md Outdated
| `accessibility` | Ignored — accessibility snapshots aren't images |
| Storybook | Yes |
| Pages | Yes |
| Plain HTML/CSS snapshots | Yes, except the virtual clock (no scripts run) |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should add custom integration type to this page. Also these should maybe link off to their respective docs when available.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added the custom integration, Cypress and Playwright to the support table, and each integration row links to its docs. (9ac88b2)

Comment thread docs/animated-snapshots.md Outdated
Comment on lines +874 to +881
AnimateConfig,
AnimateOptions,
AnimateTrigger,
AnimateTrace,
AnimationDriver,
AnimationDriverHandle,
StoryAnimateConfig,
StoryAnimateOptions,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I worry a little about this list drifting from the code and I'm not sure how useful it is to explicitly list each of these types in the docs anyway.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. I replaced the list with a short note naming the three types people are most likely to need. (9ac88b2)

@lencioni

Copy link
Copy Markdown
Contributor

We should probably address animated GIFs and similar types of animations that could exist on a page. Either it will work and yay, or it won't work well and we should be explicit about that.

trotzig and others added 2 commits September 11, 2026 22:06
- One heading per option, like the configuration page, instead of a table
  plus a list repeating it. The Drivers section is now Animation drivers,
  so it doesn't collide with the drivers option.
- Where to configure: the custom integration (per example), Cypress and
  Playwright (target only), and a worked example of how options merge.
- Examples for tying duration to the component and for root.
- discovery: settleMs is a fixed limit, not reset by finding something.
- Animated GIFs, WebP and APNG images, and what the video driver is for.
- onExpectationFailure 'fail' links to failed-on-worker debugging.
- Reframe choosing values around the part of the animation that matters,
  back the file size claim with the one measurement we have, say still
  image rather than a still, and drop the dashes that made sentences hard
  to follow.
- Link to the freezeAnimations CSS, and replace the type list with a note.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ion.md

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@trotzig

trotzig commented Sep 11, 2026

Copy link
Copy Markdown
Contributor Author

Re animated GIFs (and similar): the page now covers them (9ac88b2). Happo shows animated GIFs on their first frame, the same as in any snapshot, whether or not animate is on. Animated WebP and APNG <img>s aren't controlled by Happo at all: they keep playing and can land on a different frame each run, so the page recommends swapping them for a still image in the story. Capturing a GIF's motion would be a new feature (e.g. a built-in GIF driver), if that's something we want.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants