Repository navigation
fix(Layer): the touch cross-fade uses the motion foundation's Reduce Motion values - #8355
Conversation
…Motion values Under Reduce Motion the layer entrance now takes the values and media queries the motion foundation's policy uses: under a coarse pointer an opacity-only fade of 200 ms on easeInOut that no theme value lengthens (design:reduced-motion DR2, DR4), under any other pointer no animation (DR3). The queries are spelled as that policy spells them (literal keys, which the StyleX build orders as written), and the values become its shared constants once those are on main. The Chromium contract checks the curve too: ease-in-out, which fails on the theme-timed fade on main. Its mouse case asserts that its context is a fine pointer before it checks anything.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
AI review status for this pull request.
|
There was a problem hiding this comment.
Thanks: the layer's Reduce Motion touch fade works as described, an opacity-only 200 ms fade under a finger, with mouse and no-preference entrances unchanged. Approval waits on one owner decision: design:reduced-motion DR2 only bounds this fade at 200 ms and names no curve, so a fixed 200 ms on easeInOut whatever the theme (the policy proposed in #8056) needs an engineering owner to provide or identify the governing spec. An engineering owner records that decision by commenting /approve-spec f23d41c0cf2f88ef38b81e60f5bd0992d191fa62 on this pull request (the author may do so if they are an engineering owner), or by stating the curve and duration in DR2.
- If the curve stays, let the contract's
ease-in-outcheck also acceptcubic-bezier(0.42, 0, 0.58, 1): Chromium reports the curve as written, and only the production Storybook build turns it intoease-in-out.
[Automated review]
| animationTimingFunction: { | ||
| default: easeVars['--ease-standard'], | ||
| '@media (prefers-reduced-motion: reduce) and (pointer: coarse)': | ||
| 'cubic-bezier(0.42, 0, 0.58, 1)', |
There was a problem hiding this comment.
This curve is the open question for an owner: design:reduced-motion DR2 bounds the touch cross-fade at 200 ms, opacity only, and names no curve, while main's layer fade uses the theme's --ease-standard. Whether core's reduced-motion touch cross-fade is a fixed 200 ms on easeInOut whatever the theme (the policy proposed in #8056) needs an engineering owner's decision.
| // The motion foundation's cross-fade: Core Animation's easeInOut, | ||
| // cubic-bezier(0.42, 0, 0.58, 1), which the engine names ease-in-out. | ||
| expect(entrance.easing).toBe('ease-in-out'); |
There was a problem hiding this comment.
Chromium reports this value as cubic-bezier(0.42, 0, 0.58, 1) (computed on a Storybook dev server, and the core package's dist/astryx.css ships it that way); it reads ease-in-out here only because the production Storybook build rewrites it. If the curve stays, accept both spellings and say the build, not the engine, renames it. The same applies at line 153.
PR Analysis Report🔎 Preview ·
|
| Component | Size | LoC | Visual · A11y · RTL |
|---|---|---|---|
| DateInput | 103.3 kB (+41 B) · 15.3 kB CSS (+46 B) | — | — · — · — |
| TableHeaderCell | 75.9 kB (+46 B) · 15.6 kB CSS (+39 B) | — | — · — · — |
| TableCell | 76.4 kB (+45 B) · 15.8 kB CSS (+39 B) | — | — · — · — |
110 more components
| Component | Size | LoC | Visual · A11y · RTL |
|---|---|---|---|
| CheckboxInput | 48.8 kB (+49 B) · 9.7 kB CSS (+33 B) | — | — · — · — |
| DateTimeInput | 158.8 kB (+39 B) · 17.1 kB CSS (+40 B) | — | — · — · — |
| Table | 84.5 kB (+39 B) · 16.0 kB CSS (+40 B) | — | — · — · — |
| CheckboxList | 43.4 kB (+44 B) · 8.6 kB CSS (+33 B) | — | — · — · — |
| Markdown | 145.2 kB (+45 B) · 18.3 kB CSS (+32 B) | — | — · — · — |
| ContextMenu | 75.2 kB (+40 B) · 15.3 kB CSS (+36 B) | — | — · — · — |
| TextArea | 47.3 kB (+45 B) · 9.9 kB CSS (+30 B) | — | — · — · — |
| MoreMenu | 73.5 kB (+36 B) · 15.5 kB CSS (+38 B) | — | — · — · — |
| Slider | 88.9 kB (+43 B) · 9.4 kB CSS (+31 B) | — | — · — · — |
| PowerSearch | 184.6 kB (+35 B) · 20.6 kB CSS (+38 B) | — | — · — · — |
| Selector | 77.2 kB (+36 B) · 15.7 kB CSS (+36 B) | — | — · — · — |
| TimeInput | 90.1 kB (+35 B) · 14.3 kB CSS (+37 B) | — | — · — · — |
| RadioList | 43.0 kB (+38 B) · 8.4 kB CSS (+33 B) | — | — · — · — |
| Tokenizer | 70.3 kB (+27 B) · 17.1 kB CSS (+44 B) | — | — · — · — |
| ComplexSelector | 55.6 kB (+40 B) · 11.1 kB CSS (+30 B) | — | — · — · — |
| DropdownMenu | 73.2 kB (+32 B) · 15.5 kB CSS (+38 B) | — | — · — · — |
| AppShell | 68.6 kB (+39 B) · 12.2 kB CSS (+30 B) | — | — · — · — |
| IconButton | 42.4 kB (+41 B) · 9.5 kB CSS (+28 B) | — | — · — · — |
| ListItem | 52.8 kB (+37 B) · 11.0 kB CSS (+32 B) | — | — · — · — |
| Pagination | 86.2 kB (+33 B) · 16.0 kB CSS (+36 B) | — | — · — · — |
| ToggleButton | 43.3 kB (+40 B) · 9.6 kB CSS (+29 B) | — | — · — · — |
| BaseTypeahead | 55.2 kB (+37 B) · 10.3 kB CSS (+31 B) | — | — · — · — |
| CheckboxListItem | 53.5 kB (+36 B) · 11.0 kB CSS (+32 B) | — | — · — · — |
| DialogHeader | 48.5 kB (+38 B) · 10.2 kB CSS (+30 B) | — | — · — · — |
| DrawerHeader | 48.5 kB (+36 B) · 11.6 kB CSS (+32 B) | — | — · — · — |
| FileInput | 53.7 kB (+37 B) · 10.7 kB CSS (+31 B) | — | — · — · — |
| Lightbox | 50.5 kB (+39 B) · 10.2 kB CSS (+29 B) | — | — · — · — |
| MobileNav | 50.3 kB (+37 B) · 10.4 kB CSS (+31 B) | — | — · — · — |
| MultiSelector | 72.8 kB (+29 B) · 15.2 kB CSS (+39 B) | — | — · — · — |
| NumberInput | 55.0 kB (+38 B) · 11.2 kB CSS (+30 B) | — | — · — · — |
| ChatComposer | 61.1 kB (+38 B) · 11.2 kB CSS (+29 B) | — | — · — · — |
| SideNav | 66.1 kB (+36 B) · 10.9 kB CSS (+31 B) | — | — · — · — |
| Thumbnail | 49.6 kB (+35 B) · 11.3 kB CSS (+32 B) | — | — · — · — |
| Button | 42.4 kB (+38 B) · 9.5 kB CSS (+28 B) | — | — · — · — |
| ChatDictationButton | 46.0 kB (+36 B) · 9.8 kB CSS (+30 B) | — | — · — · — |
| CodeBlock | 59.1 kB (+36 B) · 11.1 kB CSS (+30 B) | — | — · — · — |
| MobileNavToggle | 45.8 kB (+36 B) · 9.8 kB CSS (+30 B) | — | — · — · — |
| SideNavItem | 54.8 kB (+37 B) · 10.5 kB CSS (+29 B) | — | — · — · — |
| Switch | 87.1 kB (+38 B) · 10.6 kB CSS (+28 B) | — | — · — · — |
| TextInput | 52.9 kB (+35 B) · 11.0 kB CSS (+31 B) | — | — · — · — |
| ChatLayout | 48.5 kB (+36 B) · 10.3 kB CSS (+29 B) | — | — · — · — |
| TabMenu | 55.5 kB (+36 B) · 10.4 kB CSS (+29 B) | — | — · — · — |
| TopNavMegaMenu | 54.4 kB (+35 B) · 11.2 kB CSS (+30 B) | — | — · — · — |
| TopNavMenu | 58.4 kB (+35 B) · 10.5 kB CSS (+30 B) | — | — · — · — |
| Typeahead | 62.9 kB (+34 B) · 11.8 kB CSS (+31 B) | — | — · — · — |
| SideNavHeading | 58.9 kB (+35 B) · 10.6 kB CSS (+29 B) | — | — · — · — |
| TopNav | 51.9 kB (+34 B) · 10.7 kB CSS (+30 B) | — | — · — · — |
| TopNavHeading | 58.3 kB (+33 B) · 10.4 kB CSS (+31 B) | — | — · — · — |
| Banner | 47.3 kB (+31 B) · 10.3 kB CSS (+32 B) | — | — · — · — |
| Carousel | 50.8 kB (+33 B) · 10.6 kB CSS (+30 B) | — | — · — · — |
| ChatComposerInput | 56.2 kB (+36 B) · 10.4 kB CSS (+27 B) | — | — · — · — |
| ChatComposerTokenElement | 56.2 kB (+36 B) · 10.4 kB CSS (+27 B) | — | — · — · — |
| RadioListItem | 51.3 kB (+30 B) · 10.1 kB CSS (+33 B) | — | — · — · — |
| AvatarGroupOverflow | 39.0 kB (+39 B) · 6.6 kB CSS (+23 B) | — | — · — · — |
| DateRangeInput | 69.9 kB (+31 B) · 11.9 kB CSS (+31 B) | — | — · — · — |
| Field | 41.5 kB (+31 B) · 8.3 kB CSS (+31 B) | — | — · — · — |
| Popover | 50.1 kB (+33 B) · 12.4 kB CSS (+29 B) | — | — · — · — |
| SideNavCollapseButton | 46.2 kB (+32 B) · 9.8 kB CSS (+30 B) | — | — · — · — |
| Stepper | 47.0 kB (+31 B) · 9.9 kB CSS (+30 B) | — | — · — · — |
| TableSelectionToolbar | 52.3 kB (+31 B) · 11.5 kB CSS (+30 B) | — | — · — · — |
| BreadcrumbItem | 67.3 kB (+32 B) · 11.5 kB CSS (+28 B) | — | — · — · — |
| FieldLabel | 36.1 kB (+39 B) · 5.9 kB CSS (+21 B) | — | — · — · — |
| AlertDialog | 56.5 kB (+23 B) · 12.8 kB CSS (+36 B) | — | — · — · — |
| InputGroup | 42.1 kB (+27 B) · 8.4 kB CSS (+32 B) | — | — · — · — |
| Toast | 59.5 kB (+30 B) · 10.1 kB CSS (+29 B) | — | — · — · — |
| AvatarGroup | 37.7 kB (+36 B) · 6.6 kB CSS (+22 B) | — | — · — · — |
| HoverCard | 18.6 kB (+39 B) · 5.0 kB CSS (+19 B) | — | — · — · — |
| Calendar | 54.3 kB (+28 B) · 10.4 kB CSS (+29 B) | — | — · — · — |
| ChatLayoutScrollButton | 45.9 kB (+27 B) · 9.9 kB CSS (+30 B) | — | — · — · — |
| Drawer | 36.9 kB (+35 B) · 8.2 kB CSS (+22 B) | — | — · — · — |
| LayerProvider | 62.8 kB (+29 B) · 10.5 kB CSS (+28 B) | — | — · — · — |
| StatusDot | 23.2 kB (+38 B) · 6.3 kB CSS (+19 B) | — | — · — · — |
| InfoTip | 37.8 kB (+35 B) · 6.2 kB CSS (+21 B) | — | — · — · — |
| Link | 42.9 kB (+34 B) · 8.0 kB CSS (+22 B) | — | — · — · — |
| Avatar | 35.1 kB (+30 B) · 6.5 kB CSS (+23 B) | — | — · — · — |
| Tooltip | 21.0 kB (+32 B) · 5.0 kB CSS (+19 B) | — | — · — · — |
| ChatSendButton | 45.2 kB (+19 B) · 9.5 kB CSS (+29 B) | — | — · — · — |
| lab/ChatEmojiPicker | 65.2 kB (+45 B) · 773 B CSS (0) | — | — · — · — |
| lab/ChatReactionBar | 65.7 kB (+42 B) · 1.1 kB CSS (0) | — | — · — · — |
| DropdownMenuSubMenu | 56.0 kB (+22 B) · 9.9 kB CSS (+19 B) | — | — · — · — |
| lab/CommentPin | 47.2 kB (+41 B) · 1.2 kB CSS (0) | — | — · — · — |
| lab/MobileTokenizer | 101.2 kB (+41 B) · 880 B CSS (0) | — | — · — · — |
| CommandPalette | 48.9 kB (0) · 12.2 kB CSS (+40 B) | — | — · — · — |
| lab/TransferList | 74.4 kB (+40 B) · 1.7 kB CSS (0) | — | — · — · — |
| lab/DiffViewer | 70.8 kB (+38 B) · 2.1 kB CSS (0) | — | — · — · — |
| lab/TransferListSelector | 80.9 kB (+38 B) · 1.9 kB CSS (0) | — | — · — · — |
| richtext/RichTextEditorToolbar | 222.3 kB (+37 B) · 454 B CSS (0) | — | — · — · — |
| lab/ListInput | 81.6 kB (+36 B) · 1.6 kB CSS (0) | — | — · — · — |
| lab/MobilePowerSearch | 214.0 kB (+36 B) · 1.2 kB CSS (0) | — | — · — · — |
| charts/ChartLegend | 52.4 kB (+34 B) · 20 B CSS (0) | — | — · — · — |
| lab/EmojiPicker | 70.3 kB (+34 B) · 722 B CSS (0) | — | — · — · — |
| richtext/RichTextEditor | 277.8 kB (+34 B) · 2.6 kB CSS (0) | — | — · — · — |
| SegmentedControl | 81.9 kB (+11 B) · 7.8 kB CSS (+22 B) | — | — · — · — |
| charts/Chart | 62.9 kB (+32 B) · 20 B CSS (0) | — | — · — · — |
| charts/ChartTooltip | 52.4 kB (+32 B) · 20 B CSS (0) | — | — · — · — |
| lab/TourStep | 55.5 kB (+31 B) · 590 B CSS (0) | — | — · — · — |
| Timestamp | 21.3 kB (0) · 9.9 kB CSS (+30 B) | — | — · — · — |
| lab/Schedule | 61.4 kB (+30 B) · 3.9 kB CSS (0) | — | — · — · — |
| lab/CommentThread | 227.3 kB (+26 B) · 2.4 kB CSS (0) | — | — · — · — |
| richtext/RichTextView | 258.3 kB (+26 B) · 2.3 kB CSS (0) | — | — · — · — |
| lab/CommentComposer | 102.5 kB (+24 B) · 1.9 kB CSS (0) | — | — · — · — |
| Timer | 9.6 kB (+1 B) · 7.0 kB CSS (+22 B) | — | — · — · — |
| Spinner | 18.5 kB (0) · 6.8 kB CSS (+21 B) | — | — · — · — |
| CommandPaletteInput | 26.8 kB (+1 B) · 8.5 kB CSS (+19 B) | — | — · — · — |
| Heading | 7.9 kB (−1 B) · 6.0 kB CSS (+21 B) | — | — · — · — |
| Text | 7.4 kB (−1 B) · 6.0 kB CSS (+21 B) | — | — · — · — |
| ChatToolCalls | 25.8 kB (0) · 9.3 kB CSS (+19 B) | — | — · — · — |
| lab/Comments | 232.6 kB (+19 B) · 2.5 kB CSS (0) | — | — · — · — |
| ChatMessageList | 22.8 kB (−2 B) · 7.9 kB CSS (+19 B) | — | — · — · — |
| ProgressBar | 10.8 kB (+1 B) · 7.3 kB CSS (+1 B) | — | — · — · — |
Package size: @astryxdesign/core 6.09 MB packed (+206 B); no new dependencies.
Wide change (shared styles: packages/core/src/Layer/layerAnimations.stylex.ts): add ci:run-visual, ci:run-a11y or ci:run-rtl to run them.
Generated by PR Enrichment workflow | View full report
…accepts either easeInOut spelling reducedMotionFadeConsts is on main now, so the layer's Reduce Motion fade takes its duration and curve from it rather than repeating the literals. CodeBunny on this PR: Chromium reports the curve as written, so the contract accepts cubic-bezier(0.42, 0, 0.58, 1) as well as ease-in-out.
|
/approve-spec 2f89fc0 |
There was a problem hiding this comment.
The Reduce Motion fade works as described: under a finger, layers now fade in over 200 ms on easeInOut, opacity only, and under a mouse they still appear at once. Approval waits on one engineering-owner decision.
- Owner decision needed: the published
layerAnimationstype changes, since each placement'sanimationTimingFunctionwidens fromStyleXClassNameFor<"animationTimingFunction", "cubic-bezier(0.24, 1, 0.4, 1)">toStyleXClassNameFor<"animationTimingFunction", string>, and no current record decides that API change, so an engineering owner confirms or rejects it with a comment on this pull request. - Optional: the comment above
animationBase(layerAnimations.stylex.ts, lines 94-95) repeats what the styles and the exported JSDoc already say; keeping only the reason and the StyleX quirk is enough.
[Automated review]
| // Under Reduce Motion a coarse pointer gets the shared opacity-only fade, | ||
| // which no theme value lengthens, and any other pointer gets no animation. |
There was a problem hiding this comment.
Non-blocking: this sentence repeats what the styles below and the exported JSDoc already say. Keeping only the reason and the quirk is enough:
// No theme value lengthens the Reduce Motion fade, so its values are constants.
// The media keys stay literal: StyleX gives an imported at-rule key a later
// priority layer (see reducedMotionKeys).…er fade comment The fade's easing now comes from a StyleX constant, so each placement's animationTimingFunction class type is StyleXClassNameFor<..., string>. The core API report records that. The comment above animationBase keeps only the reason and the StyleX quirk.
…ions keeps its published type Specs: a widened published type needs the owner, and a patch must not widen it. The Reduce Motion fade keeps main's animationTimingFunction (the theme ease) on the class and sets reducedMotionFadeConsts.easing in the fade keyframe's first frame, which overrides it for that interval. The core API report is back to main's. The contract reads a curve set in the first keyframe and leaves it out of the animated properties.
|
/approve-spec b749028 |
There was a problem hiding this comment.
No changes needed: with Reduce Motion under a finger, layers now fade in over a fixed 200 ms on easeInOut, opacity only, and under a mouse they still appear at once.
- The Chromium contract passes on this head and fails on the base build only on the curve, so it pins the decided fade.
- The published
layerAnimationstype is unchanged now that the curve sits in the fade's first keyframe. - Optional: an inline suggestion offers plainer wording for the new comment above
fadeIn.
[Automated review]
| // The fade's curve rides in its first keyframe, so the published class type | ||
| // of `animationTimingFunction` stays the theme ease's. |
There was a problem hiding this comment.
Optional (non-blocking): this keeps a real reason, but "rides in" is an idiom and "the theme ease's" is hard to parse. A plainer form:
// The fade sets its curve in its first keyframe. This keeps the published
// class type of `animationTimingFunction` unchanged.
Follows #8322 (merged), as its review agreed: the layer's touch cross-fade takes the motion foundation's own Reduce Motion values.
User impact
With Reduce Motion on, a layer that opens under a finger fades in over the theme's fast duration and curve (#8322), capped at 200 ms. The motion foundation's policy, which every other touch presentation follows, fades over exactly 200 ms on easeInOut. A layer and a touch presentation opened side by side fade on different clocks.
Evidence
iOS 26.2 Safari with Reduce Motion on, a tap on the Tooltip's term (a grey dot marks the finger); after this change:
Problem and solution fit
layerAnimations.stylex.tstimed the cross-fade with the theme's fast duration and easing. The animator's Reduce Motion policy (design:reduced-motion) uses a fixed 200 ms easeInOut under a coarse pointer and no animation under any other pointer. The layer now uses the same values and spells its media queries as that policy does:(prefers-reduced-motion: reduce) and (pointer: coarse)and(prefers-reduced-motion: reduce) and (not (pointer: coarse)). The keys stay literal, since the StyleX build orders an imported at-rule key differently; the values become the foundation's shared constants once those are on main.Expected behavior and authority
design:reduced-motion(current): on a coarse pointer an opacity-only cross-fade in 200 ms or less that no theme value lengthens (DR2, DR4); on other pointers an instant change (DR3).Smallest restoration
Two media conditions and their values in
layerAnimations.stylex.ts; the contract checks the easing, and its mouse case asserts a fine pointer in its context first.Scope
Testing
pnpm exec playwright test packages/core/src/Layer/__tests__/LayerReducedMotion.a11y.chromium.spec.ts --project chromiumafterpnpm storybook:build: 8/8. The four cross-fade cases fail on main's build (theme-timed curve).pnpm vitest run packages/core/src/Layer: 210 pass.