Skip to content

fix(motion): a carried pending animation runs in step with its original - #10983

Merged
cixzhang merged 1 commit into
meta:mainfrom
vjeux:uikit/motion-carry-pending-start
Nov 10, 2026
Merged

cixzhang merged 1 commit into
meta:mainfrom
vjeux:uikit/motion-carry-pending-start

Conversation

@vjeux

@vjeux vjeux commented Nov 10, 2026

Copy link
Copy Markdown
Contributor

User impact

On a busy page, a transient copy can start a carried animation after its original. The SideNav rail's leaving copy started the toggle's press fade 333 ms late.

Problem and solution fit

  • Why the defect occurs: a copy carries its element's animations by start time. An animation still waiting for its first frame has none yet, so the copy started on a frame of its own.
  • Why it harms the task: out of step, a carried animation visibly restarts in the copy.
  • Why that matters: a copy and its element must read as one surface while they cross-fade.

The copy now takes the start its original resolves to.

Expected behavior and authority

  • carryAnimations.ts carries a running animation by its start time. A pending one now follows the same rule.
  • The rail's reduced-motion press proof states the effect: "the old state's copy shows the toggle's press fading in step with the toggle (design:reduced-motion DR2)".

Smallest restoration

  • carryAnimations.ts: a running animation saved while its play was pending takes its original's start time, now or once that play starts.
  • Both callers get it: the transient copy (motion/transientCopy.ts) and the layer runtime's re-placement (Layer/preserveAnimations.ts).
  • The press proof also reads each fade's start as the fade turns ready, and keeps a frame trace. A copy fade that never started fails as frame starvation only when the trace shows no frame while the copy held it. Otherwise it fails plainly.
  • There is no public API or visual change.

Evidence

  • Reproduction before the change: under a 2-CPU load with 4 workers, main failed the press proof in 4 of 10 runs.
  • With the proof reading starts on ready but without the carry fix, a copy's press fade started 333 ms after the toggle's.
  • Result after the change: under the same load, 22 of 24 press runs pass over two rounds. The other 2 fail by name, as frame starvation. In each, the page drew one frame while the copy held the fade, after a gap of 113 or 343 ms.
  • Representative unchanged path: the layer runtime's re-placement proof (preserveAnimations) passes at one worker in Chromium and WebKit. So do layerExitCopy, LayerCloseLayout, PopoverTouchMotion and the other SideNav proofs.

Scope

  • One defect is restored.
  • Separable contradictory or unsettled tagalongs were removed or split.
  • No new public API, visual representation, default, or interaction model is hidden under the bug-fix label.
  • Public text and artifacts contain no internal Meta context.

Testing

This head, dc59ea5:

  • pnpm -F @astryxdesign/core typecheck, pnpm check:knowledge and pnpm check:changesets pass. ESLint and Prettier pass on the changed files.
  • Vitest: the motion, SideNav, Layer and AppShell suites, 2,051 passed.
  • Interaction specs at one worker: SideNavRailMotion, SideNavTextSize, layerExitCopy, preserveAnimations, LayerCloseLayout and PopoverTouchMotion. Chromium: 42 passed. WebKit with CI's profile: 15 passed and 27 skipped; the SideNav proofs run in Chromium only.

A transient copy carries its element's animations by start time. One
saved while its play was still pending had no start time yet, so the
copy took only its current time and started on a frame of its own. On
a loaded machine, the SideNav rail's leaving copy started the toggle's
press fade 333 ms after the toggle's. The copy now takes the start its
original resolves to, at once or once the original's play starts. Both
callers get it: the transient copy and the layer runtime's
re-placement.

The rail's reduced-motion press proof read the copy's start only in
frame callbacks, which on a busy page ran after the copy was gone. It
also reads the start as the fade turns ready, and it keeps a frame
trace. A copy fade that never started fails as frame starvation only
when the trace shows no frame while the copy held it; otherwise it
fails plainly.
@vercel

vercel Bot commented Nov 10, 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 Nov 10, 2026 6:43am UTC

Request Review

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

astracat-bot Bot commented Nov 10, 2026 •

Copy link
Copy Markdown
Contributor

AI review status for this pull request.

Review status Updated
🟢 Reviewed (for maintainers only) Nov 10, 2026, 7:04 AM UTC

@github-actions

github-actions Bot commented Nov 10, 2026 •

Copy link
Copy Markdown
Contributor

PR Analysis Report

🔎 Preview · dc59ea5

Storybook · Sandbox · built from this PR's head

Modified components

Component Size LoC Visual · A11y · RTL
InfoTip 47.0 kB (+76 B) · 5.3 kB CSS (0) — — · — · —
DateTimeInput 209.1 kB (+69 B) · 18.7 kB CSS (0) — — · — · —
Tokenizer 91.9 kB (+69 B) · 18.8 kB CSS (0) — — · — · —
112 more components
Component Size LoC Visual · A11y · RTL
NumberInput 72.2 kB (+66 B) · 11.4 kB CSS (0) — — · — · —
Toast 98.5 kB (+66 B) · 10.3 kB CSS (0) — — · — · —
Avatar 43.7 kB (+64 B) · 5.6 kB CSS (0) — — · — · —
TopNavMenu 75.0 kB (+62 B) · 11.2 kB CSS (0) — — · — · —
AvatarGroup 46.4 kB (+61 B) · 5.7 kB CSS (0) — — · — · —
CodeBlock 75.8 kB (+61 B) · 11.3 kB CSS (0) — — · — · —
Stepper 62.3 kB (+61 B) · 10.1 kB CSS (0) — — · — · —
TopNavHeading 75.6 kB (+61 B) · 11.2 kB CSS (0) — — · — · —
TabList 44.3 kB (+60 B) · 6.5 kB CSS (0) — — · — · —
ContextMenu 109.3 kB (+59 B) · 17.4 kB CSS (0) — — · — · —
SideNav 114.9 kB (+59 B) · 11.4 kB CSS (0) — ✓ · ✓ · ✓
SideNavHeading 76.0 kB (+59 B) · 11.3 kB CSS (0) — ✓ · ✓ · ✓
Thumbnail 65.0 kB (+59 B) · 11.6 kB CSS (0) — — · — · —
AppShell 91.8 kB (+58 B) · 13.5 kB CSS (0) — — · — · —
Popover 91.3 kB (+58 B) · 14.2 kB CSS (0) — — · — · —
Slider 109.6 kB (+58 B) · 8.8 kB CSS (0) — — · — · —
Toolbar 44.3 kB (+58 B) · 8.1 kB CSS (0) — — · — · —
DrawerHeader 63.7 kB (+57 B) · 12.9 kB CSS (0) — — · — · —
Link 53.5 kB (+57 B) · 7.0 kB CSS (0) — — · — · —
MultiSelector 95.4 kB (+57 B) · 16.8 kB CSS (0) — — · — · —
Tooltip 29.9 kB (+57 B) · 4.4 kB CSS (0) — — · — · —
Button 60.7 kB (+56 B) · 10.0 kB CSS (0) — — · — · —
DialogHeader 63.5 kB (+56 B) · 11.5 kB CSS (0) — — · — · —
FileInput 69.6 kB (+56 B) · 10.9 kB CSS (0) — — · — · —
PowerSearch 289.1 kB (+56 B) · 24.2 kB CSS (0) — — · — · —
Selector 100.7 kB (+56 B) · 17.4 kB CSS (0) — — · — · —
SideNavCollapseButton 61.5 kB (+56 B) · 10.0 kB CSS (0) — ✓ · ✓ · ✓
ChatLayoutScrollButton 61.2 kB (+55 B) · 10.2 kB CSS (0) — — · — · —
ComplexSelector 71.4 kB (+55 B) · 11.8 kB CSS (0) — — · — · —
DateInput 187.4 kB (+55 B) · 17.0 kB CSS (0) — — · — · —
SegmentedControl 96.6 kB (+55 B) · 6.9 kB CSS (0) — — · — · —
TableCell 110.5 kB (+55 B) · 17.9 kB CSS (0) — — · — · —
TableHeaderCell 110.1 kB (+55 B) · 17.7 kB CSS (0) — — · — · —
ToggleButton 87.1 kB (+55 B) · 10.1 kB CSS (0) — — · — · —
Calendar 71.7 kB (+54 B) · 10.6 kB CSS (0) — — · — · —
ChatLayout 83.3 kB (+54 B) · 10.5 kB CSS (0) — — · — · —
Pagination 110.7 kB (+54 B) · 17.8 kB CSS (0) — — · — · —
StatusDot 31.6 kB (+54 B) · 5.3 kB CSS (0) — — · — · —
TextInput 68.2 kB (+54 B) · 11.2 kB CSS (0) — — · — · —
TopNav 72.6 kB (+54 B) · 10.9 kB CSS (0) — — · — · —
Banner 62.6 kB (+53 B) · 10.5 kB CSS (0) — — · — · —
ChatSendButton 61.1 kB (+53 B) · 10.0 kB CSS (0) — — · — · —
Lightbox 122.9 kB (+53 B) · 10.6 kB CSS (0) — — · — · —
RadioList 56.8 kB (+53 B) · 7.3 kB CSS (0) — — · — · —
RadioListItem 67.9 kB (+53 B) · 10.1 kB CSS (0) — — · — · —
TabMenu 72.2 kB (+53 B) · 11.1 kB CSS (0) — — · — · —
ChatDictationButton 61.2 kB (+52 B) · 10.0 kB CSS (0) — — · — · —
DropdownMenu 97.5 kB (+52 B) · 17.6 kB CSS (0) — — · — · —
Field 55.4 kB (+52 B) · 7.2 kB CSS (0) — — · — · —
IconButton 60.7 kB (+52 B) · 10.0 kB CSS (0) — — · — · —
Markdown 188.7 kB (+52 B) · 20.6 kB CSS (0) — — · — · —
MobileNav 71.0 kB (+52 B) · 10.6 kB CSS (0) — — · — · —
MobileNavToggle 61.0 kB (+52 B) · 10.0 kB CSS (0) — — · — · —
MoreMenu 97.8 kB (+52 B) · 17.6 kB CSS (0) — — · — · —
ChatComposer 78.8 kB (+51 B) · 12.0 kB CSS (0) — — · — · —
InputGroup 57.1 kB (+51 B) · 7.8 kB CSS (0) — — · — · —
TimeInput 162.8 kB (+51 B) · 16.0 kB CSS (0) — — · — · —
Typeahead 80.1 kB (+51 B) · 12.4 kB CSS (0) — — · — · —
BreadcrumbItem 85.0 kB (+50 B) · 12.1 kB CSS (0) — — · — · —
FieldLabel 51.5 kB (+50 B) · 5.7 kB CSS (0) — — · — · —
HoverCard 28.2 kB (+49 B) · 4.4 kB CSS (0) — — · — · —
CheckboxInput 64.8 kB (+48 B) · 9.2 kB CSS (0) — — · — · —
Table 126.0 kB (+48 B) · 18.3 kB CSS (0) — — · — · —
TableSelectionToolbar 70.9 kB (+48 B) · 13.2 kB CSS (0) — — · — · —
TextArea 62.8 kB (+48 B) · 9.3 kB CSS (0) — — · — · —
Carousel 70.3 kB (+47 B) · 10.9 kB CSS (0) — — · — · —
CheckboxListItem 71.7 kB (+47 B) · 11.4 kB CSS (0) — — · — · —
AlertDialog 105.8 kB (+46 B) · 14.6 kB CSS (0) — — · — · —
AvatarGroupOverflow 49.4 kB (+46 B) · 5.7 kB CSS (0) — — · — · —
BaseTypeahead 71.5 kB (+46 B) · 10.9 kB CSS (0) — — · — · —
ListItem 70.8 kB (+46 B) · 11.4 kB CSS (0) — — · — · —
CheckboxList 68.9 kB (+45 B) · 7.7 kB CSS (0) — — · — · —
DateRangeInput 93.3 kB (+45 B) · 12.5 kB CSS (0) — — · — · —
TopNavMegaMenu 70.1 kB (+45 B) · 11.8 kB CSS (0) — — · — · —
ChatComposerInput 76.5 kB (+44 B) · 11.3 kB CSS (0) — — · — · —
ChatComposerTokenElement 76.5 kB (+44 B) · 11.3 kB CSS (0) — — · — · —
DropdownMenuSubMenu 70.6 kB (+43 B) · 9.7 kB CSS (0) — — · — · —
Switch 104.2 kB (+43 B) · 9.9 kB CSS (0) — — · — · —
SideNavItem 70.9 kB (+42 B) · 11.1 kB CSS (0) — ✓ · ✓ · ✓
ChatMessageList 29.4 kB (0) · 7.0 kB CSS (0) — — · — · —
ChatToolCalls 33.3 kB (0) · 8.3 kB CSS (0) — — · — · —
CommandPalette 73.5 kB (0) · 12.7 kB CSS (0) — — · — · —
CommandPaletteInput 33.0 kB (0) · 7.5 kB CSS (0) — — · — · —
Heading 13.7 kB (0) · 5.4 kB CSS (0) — — · — · —
ProgressBar 11.7 kB (0) · 6.3 kB CSS (0) — — · — · —
SideNavSection 6.9 kB (0) · 3.7 kB CSS (0) — ✓ · ✓ · ✓
Timer 15.3 kB (0) · 6.0 kB CSS (0) — — · — · —
Timestamp 34.3 kB (0) · 10.2 kB CSS (0) — — · — · —
Spinner 26.0 kB (−1 B) · 6.2 kB CSS (0) — — · — · —
Text 13.2 kB (−1 B) · 5.4 kB CSS (0) — — · — · —
charts/Chart 205.0 kB (new) · 494 B CSS (new) — — · — · —
charts/ChartLegend 53.0 kB (new) · 224 B CSS (new) — — · — · —
charts/ChartTooltip 182.3 kB (new) · 423 B CSS (new) — — · — · —
lab/ChatEmojiPicker 106.5 kB (new) · 773 B CSS (new) — — · — · —
lab/ChatReactionBar 107.6 kB (new) · 1.1 kB CSS (new) — — · — · —
lab/CommentComposer 152.6 kB (new) · 1.9 kB CSS (new) — — · — · —
lab/CommentPin 57.6 kB (new) · 1.2 kB CSS (new) — — · — · —
lab/Comments 352.6 kB (new) · 2.5 kB CSS (new) — — · — · —
lab/CommentThread 345.7 kB (new) · 2.4 kB CSS (new) — — · — · —
lab/DialogHeroHeader 83.7 kB (new) · 372 B CSS (new) — — · — · —
lab/DiffViewer 87.8 kB (new) · 2.1 kB CSS (new) — — · — · —
lab/EmojiPicker 87.4 kB (new) · 751 B CSS (new) — — · — · —
lab/LineClamp 15.3 kB (new) · 168 B CSS (new) — — · — · —
lab/ListInput 88.7 kB (new) · 922 B CSS (new) — — · — · —
lab/Schedule 106.6 kB (new) · 3.9 kB CSS (new) — — · — · —
lab/SelectableCardGroup 64.9 kB (new) · 162 B CSS (new) — — · — · —
lab/TourStep 97.6 kB (new) · 590 B CSS (new) — — · — · —
lab/TransferList 102.6 kB (new) · 1.2 kB CSS (new) — — · — · —
lab/TransferListSelector 110.1 kB (new) · 1.4 kB CSS (new) — — · — · —
richtext/RichTextEditor 298.6 kB (new) · 2.6 kB CSS (new) — — · — · —
richtext/RichTextEditorToolbar 279.1 kB (new) · 454 B CSS (new) — — · — · —
richtext/RichTextView 276.8 kB (new) · 2.3 kB CSS (new) — — · — · —

Package size: measured; main has no measurement of the merge base to compare yet.

API changes: none in this pull request

Committed API report behind this build: cli 29 rows, core 36 rows; run pnpm api:report.

Touched components checked; add ci:run-visual, ci:run-a11y or ci:run-rtl to run the full suites.


Generated by PR Enrichment workflow | View full report

@cixzhang

Copy link
Copy Markdown
Contributor

/approve-spec dc59ea5

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

Approved pending CI: CI has not run on this head yet. The pull request is a draft from a fork, so once it is ready for review, a maintainer adds the ci:run label so CI runs.

The fix holds up: the copy now takes its original's start, and the changed press proof fails on main under a slowed CPU (the copy's fade started about 420 ms late) but never fails that way on this head.

  • Optional: the first sentence of followStart's doc comment (packages/core/src/motion/carryAnimations.ts:172) restates the code below it. Keeping only the pending-start quirk would match the repository's comment guidance.

[Automated review]

Comment on lines +172 to +174
* Gives `next` the start time `original` runs on, now or, while its play is
* still pending, once that play starts. A pending play has no start time
* yet: it resolves on the frame that first draws the animation.

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.

Optional: the first sentence restates the two branches below. Keeping only the quirk is enough, for example: "A pending play has no start time. Its start resolves on the frame that first draws the animation." The comment in restore() can then keep only its reason, that the two run in step.

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.

Kept for this head, which the spec owner has stamped. The trim to the pending-start quirk goes in the next change to this file.

@cixzhang cixzhang added the ci:run A maintainer lets this outside contributor's pull request run CI (AST-030 FR17) label Nov 10, 2026
@vjeux
vjeux marked this pull request as ready for review November 10, 2026 20:12
@cixzhang
cixzhang merged commit 894a2a0 into meta:main Nov 10, 2026
127 checks passed

This branch was successfully deployed

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

Labels

ci:run A maintainer lets this outside contributor's pull request run CI (AST-030 FR17) 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.

2 participants