Skip to content

test(Dialog): the reduced-motion entrance contract reads a fade that ended before its listener ran - #8924

Merged
cixzhang merged 1 commit into
mainfrom
fix/dialog-reduced-motion-entrance-record
Oct 30, 2026
Merged

cixzhang merged 1 commit into
mainfrom
fix/dialog-reduced-motion-entrance-record

Conversation

@cixzhang

Copy link
Copy Markdown
Contributor

Maintainer impact

The release-time accessibility contracts can go red on a correct Dialog. On a loaded CI runner, Dialog.reduced-motion.a11y.browser.spec.ts sometimes reports that the dialog ran no entrance on a touch screen with reduced motion (animations.length is 0), even though the 200 ms cross-fade did run. A red release check on a healthy commit holds the release and sends maintainers chasing a regression that isn't there.

Intended invariant

The contract is unchanged (DR1, DR2, DR3 in docs/design/reduced-motion.md). With reduced motion on a touch screen, the dialog's entrance is an opacity-only cross-fade of 200 ms or less. With a mouse, it appears at once with no animation. Without reduced motion, the standard entrance still rises and scales in. Every assertion stays as it was.

Change

The spec records each entrance from animationstart by reading target.getAnimations() inside the listener. On a busy machine that listener can run after a 200 ms fade has already finished, and a finished animation (fill mode backwards) is no longer returned by getAnimations(). So nothing was recorded, and the "a cross-fade happened" assertion failed.

getAnimations() stays the primary source. When it is empty at event time, the spec now builds the record from the event itself: it finds the @keyframes rule named by event.animationName to get the animated properties, and reads the duration and delay from the dialog's computed animation longhands. The fine-pointer case still expects no animationstart at all, so the fallback cannot hide an animation that shouldn't run.

Test only: no product code and no Changeset.

Evidence

All runs were in Chromium against the Storybook built for the failing commit, with Playwright 1.61.1.

  • Failure before the change: with the CPU saturated (32 busy loops and 24 workers), the touch/reduced-motion cases fail exactly as in CI (Expected: > 0, Received: 0) on all three stories. Default fails 30/40, fullscreen 33/40, alert dialog 33/40. A probe showed animationstart firing with the fade's 0.2s keyframes while getAnimations() was already empty.
  • Success after the change: under the same saturation, all three stories pass 40/40 (120/120). The whole file passes 140/140 (20 repeats) under saturation, and 70/70 (10 repeats) at normal load (4 workers).
  • Product/runtime behavior verified unchanged: Dialog source and styles are untouched. Before this change, the same contract passed at normal load (110/110) and in earlier release checks.

Scope

  • No intended public API, product behavior, visual direction, or policy change.
  • Product changes discovered during the work were removed or split.
  • Public text and artifacts contain no internal Meta context.

Testing

  • pnpm exec playwright test packages/core/src/Dialog/__tests__/Dialog.reduced-motion.a11y.browser.spec.ts --project chromium --repeat-each=10 at normal load, and with --repeat-each=40 --fully-parallel -g 'coarse pointer' under CPU saturation, before and after the change.
  • prettier --check and eslint on the file are clean.

Credit: @vjeux wrote this contract in #8327. This change only makes its measurement robust to slow runners.

Posted by Cindy's agent on her behalf.

…ended before its listener ran

On a loaded runner the animationstart listener can run after the 200 ms
touch-screen cross-fade has finished, when getAnimations() no longer
returns it, so the contract saw no entrance. getAnimations() stays the
primary source; when it is empty, the entrance is read from the @Keyframes
rule the event names and the computed animation longhands. Assertions are
unchanged. Test only.

Original contract by @vjeux in #8327.
@vercel

vercel Bot commented Oct 30, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
astryx Ready Ready Preview Oct 30, 2026 3:38am UTC

Request Review

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Meta Open Source bot. label Oct 30, 2026
@astracat-bot

astracat-bot Bot commented Oct 30, 2026 •

Copy link
Copy Markdown

AI review status for this pull request.

Review status Updated
🟢 Reviewed (for maintainers only) Oct 30, 2026, 4:01 AM UTC

@astracat-bot astracat-bot Bot 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.

Recommended for approval: the fallback records a reduced-motion fade that ended before the animationstart listener ran, and every DR1-DR3 assertion is unchanged. When the listener runs late, a correct Dialog now passes, while a 300 ms or travelling entrance still fails through the new path. No changes are needed.

[Automated review]

@cixzhang
cixzhang merged commit 2743132 into main Oct 30, 2026
53 checks passed
@cixzhang
cixzhang deleted the fix/dialog-reduced-motion-entrance-record branch October 30, 2026 04:55

This branch was successfully deployed

1 active deployment
Preview — 1f904bcc Deployed Oct 30, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Meta Open Source bot.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant