Repository navigation
Conversation
…ad of appearing at once The shared layer entrance (layerAnimations) dropped its animation under prefers-reduced-motion, so a Tooltip, HoverCard or menu opened by a tap appeared in one frame. On a touch screen Reduce Motion replaces motion with a cross-fade, never a jump: under a coarse pointer the entrance now keeps its opacity ramp and drops only the travel and the scale. Under a mouse it still appears at once, and without Reduce Motion nothing changes. LayerReducedMotion.a11y.chromium.spec.ts proves the three cases in a real engine (media queries), with its own pr-a11y step.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
AI review status for this pull request.
|
PR Analysis Report📚 Storybook Preview🧪 Sandbox PreviewNo new or modified components detected. Bundle Size Summary
Accessibility AuditStatus: No accessibility violations detected. Visual Regression46 of 494 shot(s) changed. A change here is a question, not a failure: check whether the after is the
and 26 more. Generated by PR Enrichment workflow | Storybook | Sandbox | View full report |
There was a problem hiding this comment.
Approval of this change waits on an engineering owner's decision, because the behaviour it adds is not yet governed by a current spec. Fading layers in under a coarse pointer with Reduce Motion is not covered by any current record: design:motion (including DR5) is still draft, and AST-094 FR8 is only proposed in #8052. An engineering owner needs to decide whether every layer that uses layerAnimations should fade in (opacity only, with the theme's entrance timing) when Reduce Motion is on and (pointer: coarse) matches, or point to the current claim that governs it; they record approval by commenting /approve-spec <full head SHA> on this pull request, and the author may do so if they are an engineering owner. Nothing else needs to change: the new contract fails on main's build and passes on this head, and the mouse and no-preference paths are unchanged.
[Automated review]
…ers change at once (#8272) ## Exact decision needed What a person who prefers reduced motion sees when a core component animates a change, on touch and on other pointers, and whether a component may keep its motion. ## Existing authority searched - `design:motion` DR5 (draft): every animated transition has an immediate or minimally moving form. Draft, so not authority. - `spec:AST-013` FR13: reduced-motion behavior is unchanged by server-rendered entrances; it defines none. - `spec:AST-012` FR2: `motion: reduce` and `pointer: coarse/fine` are the adaptation conditions. - No current web record defines component behavior under reduced motion. - Search terms: reduced motion, prefers-reduced-motion, cross-fade, Reduce Motion, motion DR5, spring, travel. Overlapping open pull requests: #7367 (draft `design:motion` DEC-2, hover entrances; no reduced-motion rule), #8214 (layer fade under a finger with Reduce Motion; conforms once its fade is 200 ms or less), #8052 (AST-094 FR8 carries the matching touch rule), #8211, #8239, #8253 (touch indicator and placeholder work; continuous indicators are outside this record). ## Decision New current record `design:reduced-motion`, matching iOS Reduce Motion with Prefer Cross-Fade: - DR1: under `prefers-reduced-motion: reduce`, a core component's entrances, exits, presentation switches, and state changes never move, scale, resize, or spring. - DR2: on a coarse pointer, they cross-fade: opacity only, 200 ms or less. - DR3: on any other pointer, they complete at once. - DR4: no core component, prop, or theme value keeps motion under reduced motion. `spec:AST-057` FR5 names that a swiped row's slide out, spring back, and hold-out follow DR2 under reduced motion. The draft `design:motion` keeps its other rules unapproved; its DR5 points to the new record. A separate record keeps the promotion to this one rule, as `docs/design/README.md` advises for a different approval lifecycle. ## Rejected alternatives - A per-component setting that keeps full motion under reduced motion. ## Claim scope and non-goals Only transitions under reduced motion. Motion without the preference, durations and easing, tokens, continuous indicators such as a spinner, and content the person drags with their own finger are outside this record. ## Evidence and implementation relationship Layers that open under a finger with Reduce Motion appear in one frame today, where iOS cross-fades. The open fix #8214 makes them fade under a coarse pointer and stay immediate under a mouse, which conforms to DR2 and DR3 once its fade is 200 ms or less. The open draft #7367 (`design:motion` DEC-2, hover-summoned layers) does not touch reduced motion. The AST-094 revision in #8052 carries the matching touch rule. No implementation is included. ## Writing rubric results - R1 narration: none. - R2 research in the record: none. - R3 evidence naming a change: none; verification names browser states. - R4 rejected alternatives over one line or outside a decision: one, inside DEC-1, one line. - R5 pull request or issue references remaining: none. - R6 ruled open questions still listing options: none. ## Scope - [x] This PR contains specification records only. - [x] It records observable behavior and preserves equivalent internal implementations. - [x] Any named internal mechanism is intentionally public, names its dependent caller/system, and explains why an equivalent implementation would not satisfy the contract. - [x] Verification states the evidence layer and failure signal without prescribing CI job/workflow topology. - [x] It records one intentional durable decision, not a rescue for a separable tagalong. - [x] The canonical knowledge owner, affected current claims, and overlapping open work are named; any ownership collision is resolved or called out for human decision. - [x] Adjacent API, visual, accessibility, composition, and implementation facts remain outside scope unless this exact decision depends on them. - [x] Public text and artifacts contain no internal Meta context. ## Validation - `pnpm check:knowledge` (CI lint) - Prettier - `git diff --check`
|
This was closed by GitHub's closing-keyword parsing, not by a review: the squash commit of #8272 says The branch now caps the touch cross-fade at 200 ms ( |
…ad of appearing at once (#8322) Supersedes #8214, which #8272's merge closed through its closing keyword; same branch, plus the 200 ms cap that `design:reduced-motion` asks for. ## User impact With Reduce Motion on, a Tooltip, HoverCard, menu or other layer that opens under a finger appeared in a single frame: the shared layer entrance dropped its whole animation, fade included. On a touch screen Reduce Motion replaces motion with a cross-fade, never a jump. ## Evidence iOS 26.2 Safari with Reduce Motion on, a tap on the Tooltip's term (a grey dot drawn by the test page marks the finger); before (main), then after:   - Before: the tooltip appears in a single frame of the 60 fps recording. - After: it fades in where it lands, opacity only, over the theme's fast duration (165 ms with the default motion scale; most of the change in the first 115 ms on the standard ease). Under a mouse with Reduce Motion it still appears at once; under a finger without it the entrance still animates opacity and transform. - Regression proof: the Chromium contract's fade cases fail on main's build, and its slow-theme case (`--duration-fast-max: 400ms`) fails without the cap (0.4 s) and passes with it (0.2 s). ## Problem and solution fit `layerAnimations` (the entrance every layer applies through `layer.render`) set `animation-name: none` under `prefers-reduced-motion: reduce`. That suits a mouse. Under a coarse pointer it is a jump where iOS fades. Under a coarse pointer with Reduce Motion the entrance now keeps its opacity ramp, drops the slide and the scale, and lasts `min(--duration-fast-max, 200ms)`. Under a mouse it still appears at once; without Reduce Motion nothing changes. Components with their own touch presentation (DropdownMenu, Popover in the open stacks) override this entrance inline, so this reaches layers that have none: Tooltip, HoverCard, Selector, ContextMenu and the like. ## Expected behavior and authority - `design:reduced-motion` (current): under reduced motion nothing moves, scales or springs (DR1); on a coarse pointer it cross-fades, opacity only, in 200 ms or less (DR2); on other pointers it changes at once (DR3); no theme value keeps motion (DR4). ## Smallest restoration One added media condition per placement in `layerAnimations.stylex.ts` (a fade keyframe, and the capped duration), and a real-engine contract with its own pr-a11y step. A UIKit-timed touch entrance for tooltips is an open owner question (D7), not built here. ## Scope - [x] One defect is restored. - [x] Separable contradictory or unsettled tagalongs were removed or split. - [x] No new public API, visual representation, default, or interaction model is hidden under the bug-fix label. - [x] Public text and artifacts contain no internal Meta context. ## Testing - `pnpm exec playwright test packages/core/src/Layer/__tests__/LayerReducedMotion.a11y.chromium.spec.ts --project chromium` after `pnpm storybook:build`: 8/8 (Tooltip and HoverCard: an opacity-only fade of 200 ms or less under a finger with Reduce Motion, also with a slow theme; unchanged without it; still instant under a mouse). The fade cases fail against main; the slow-theme case fails against #8214's head. - `pnpm vitest run .github/scripts/ci-test-routing.test.mjs .github/scripts/release-check-workflow.test.mjs`: 101 pass. - `pnpm vitest run packages/core/src/Layer packages/core/src/Tooltip packages/core/src/HoverCard packages/core/src/Popover packages/core/src/DropdownMenu packages/core/src/Selector packages/core/src/ContextMenu`: 983 pass. - iOS 26.2 simulator in Safari with Reduce Motion on, before and after, 60 fps recordings with a page log; the cap changes nothing at the default theme's 165 ms. - Core typecheck, eslint, prettier.
User impact
With Reduce Motion on, a Tooltip, HoverCard, menu or other layer that opens under a finger appeared in a single frame: the shared layer entrance dropped its whole animation, fade included. On a touch screen Reduce Motion replaces motion with a cross-fade, never a jump, so the layer should fade in where it lands.
Evidence
iOS 26.2 Safari with Reduce Motion on, a tap on the Tooltip's term (a grey dot drawn by the test page marks the finger); before (main), then after:
Problem and solution fit
layerAnimations(the entrance every layer applies throughlayer.render) setanimation-name: noneunderprefers-reduced-motion: reduce. That suits a mouse, where the theme decides. Under a coarse pointer it is a jump where iOS fades: a menu, a popover or a tip with Reduce Motion lands in place and fades in, with no travel and no scale.Under a coarse pointer with Reduce Motion the entrance now keeps its opacity ramp and drops only the slide and the scale. Under a mouse it still appears at once, and without Reduce Motion nothing changes. Components with their own touch presentation (DropdownMenu, Popover in the open stacks) override this entrance inline, so this only reaches layers that have none: Tooltip, HoverCard, Selector, ContextMenu and the like.
Expected behavior and authority
design:motionDR5 (current): a reduced-motion path communicates the same result without travel.Smallest restoration
One added media condition per placement in
layerAnimations.stylex.ts(a fade keyframe), and a real-engine contract with its own pr-a11y step, placed where none of the 130 open pull requests touchingci.ymladds a conflict. The fade keeps the theme's entrance timing; a UIKit-timed touch entrance for tooltips is an open owner question (D7, InfoTip as a TipKit popover tip), not built here.Scope
Testing
pnpm exec playwright test packages/core/src/Layer/__tests__/LayerReducedMotion.a11y.chromium.spec.ts --project chromiumafterpnpm storybook:build: 6/6 (Tooltip and HoverCard: fade under a finger with Reduce Motion, unchanged without it, still instant under a mouse); the two fade cases fail against main.pnpm vitest run .github/scripts/ci-test-routing.test.mjs .github/scripts/release-check-workflow.test.mjs: 101 pass.pnpm vitest run packages/core/src/Layer packages/core/src/Tooltip packages/core/src/HoverCard packages/core/src/Popover packages/core/src/DropdownMenu packages/core/src/Selector packages/core/src/ContextMenuplus the build-css graph, layers and data-token tests: 1001 pass.