Skip to content

fix(DateInput, DateTimeInput): the calendar's toggle and selection follow UIDatePicker's timing - #8206

Draft
vjeux wants to merge 2160 commits into
meta:mainfrom
vjeux:uikit/c22-calendar-motion
Draft

vjeux wants to merge 2160 commits into
meta:mainfrom
vjeux:uikit/c22-calendar-motion

Conversation

@vjeux

@vjeux vjeux commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

Depends on #8200. Review only this PR's change: vjeux/astryx@693fafc...c4d5c04

User impact

Under a finger, the month/year toggle runs off UIDatePicker's clock: DateInput fades linearly with a 180° flip, DateTimeInput swaps at once, a tapped day eases in. Now, from #8178's values: a 0.25 s ease-in-ease-out cross-fade, a chevron along the reading direction turning a quarter, a day selecting at once; hidden wheels laid out ahead. A mouse keeps today's toggle.

Before and after

Recorded on the iOS 26.2 simulator and the Android emulator (Chromium 158) from these heads; Overlays: JS clock, Reduce Motion, finger. No real device was available to the test rig. Before: #8200.

Path before this PR
Title tap and back
… on Android
RTL
Reduce Motion
DateTimeInput title tap and back
… on Android

The fade on iOS. Mobile Safari clocks a transition from the style change, so the commit flips no inherited property on the panels: their inert swaps when the fade ends; aria-hidden and focus move with the toggle; a shield takes touches meanwhile, and the header's and footer's few controls leave with the toggle. Opening progress at the first frame:

Opening before this PR
DateInput, 3 takes 87–100 % 9–23 %
DateInput, RTL 22 %
Reduce Motion (200 ms) 100 % 14 %
DateTimeInput, 3 takes 100 % 12–52 %

On Android (emulator, one take each) the opening drops one frame before (85 ms), none here. Commit script (earlier head): 24.5 ms at 4× CPU (was 27.5).

Visual authority

  • Existing component/module DD row reused or amended.
  • Durable component-local intent added to the optional Design decisions table by a team design owner.
  • Correction already settled by current family, design, theme, or objective accessibility authority.
  • New shared or cross-component direction settled in docs/design/.

None is settled; the timing is UIKit's, played through internal motion roles timing.stateChangeMs and timing.curves.move (spec:AST-094 FR1, FR4). Pending owner review (cixzhang): the chevron glyph.

State matrix

  • Both sheets, opening and closing, under a finger.
  • RTL: the chevron is mirrored, ending down.
  • Reduced Motion (design:reduced-motion): under a finger, a 200 ms cross-fade and chevron dip; with a mouse, at once.
  • Size (INV6, analyze-pr.js), import gzip, base → head: DateInput 148.8 → 150.2 KB, DateTimeInput 161.6 → 163.0 KB, PowerSearch 215.6 → 217.0 KB; component CSS +332 to +444 B.

Scope

  • Appearance is the primary intent.
  • Behavior, API, interaction model, and unrelated layout changes were removed or split.
  • The evidence shows the changed pixels actively running, not only source or computed styles.
  • Public text and artifacts contain no internal Meta context.

Testing

  • The toggle's 0.25 s reads timing.stateChangeMs through its variable, beside timing.curves.move; DateInputTouch.test.tsx holds the table's fade equal to the role and checks both variables.
  • New useChevronDip.test.tsx: a second change within the dip cancels the running one and starts from the glyph's current opacity, and the unmount cancels it; all failed before. The dip plays on the policy's fixed curve, so its middle stays on the turn's delay.
  • SHEET_SETTLE_MS is a constant pinned to springs.sheetPresent by useWheelsWarmUp.test.tsx, so the pickers ship none of the springs table.
  • New in both sheets' jsdom tests and MonthYearToggle.interaction.browser.spec.ts: nothing outside the panels takes a tap while the toggle fades. With the fades stretched to 2 s, a real tap mid-fade on Done closed the DateInput sheet through Save beneath it (Chromium and WebKit), and a tap on DateTimeInput's next-month arrow reached it (WebKit); both now reach nothing.
  • MonthYearToggle.interaction.browser.spec.ts, Chromium and WebKit, twice over in the top-head gate. Each test opens the sheet and waits for the hidden wheels' warm-up and its end before its first tap, so the only animation on the panel is the one the test starts (a CI run tapped during the warm-up's own transition, and the busy-thread block began before the click was handled); the busy-thread case, which WebKit skips, failed 4 of 10 in Chromium pinned to two cores at 4 workers (the compositor itself presented a frame only every 48 to 95 ms there), a load-sensitive proof still to be fixed at its cause. The spec checks:
    • the wheels' fade and the chevron's quarter turn as the page plays them, read from the watched transitions: 250 ms on ease-in-ease-out (cubic-bezier(0.42, 0, 0.58, 1), which WebKit reports as ease-in-out), opacity 0 → 1 and rotate(-90deg) → rotate(0deg), DateTimeInput's too;
    • in RTL the chevron points left and turns to point down;
    • a tapped day runs no animation; a second tap mid-fade turns the fade around without a jump (a test stylesheet stretches the toggle's fades to 2 s, the test waits page-side for opacity above 0.3 so the tap lands mid-fade on any machine, and it reads the turnaround from a log of every frame from before the first tap until the wheels rest; the fall is bounded per millisecond);
    • Reduce Motion: the wheels cross-fade in 200 ms or less, opacity only, and the chevron dips, its turn applying at the dip's middle (this failed before the dip's ref moved to the title button: a registry Icon does not forward one);
    • with a mouse: today's toggle, and under Reduce Motion it changes at once.
  • DateInputTouch.test.tsx: the fades' and the chevron's definitions under the touch key and the two reduced-motion keys, spelled as the resolver's constants.
  • New in both sheets' spec: the toggle's commit changes only the panels' classes and aria-hidden (computed pointer-events stays auto); a touch one frame into the fade meets the shield; the panels swap inert when the outgoing panel's exit has ended (faded to 0, or hidden at the cover's end), at least the fade's 250 ms after the commit on the document timeline, the wheels fully faded in. It failed on the previous head (inert at opacity 1, one frame after the commit). As a diagnostic (Chromium, 4× CPU), the commit restyles 36–41 elements instead of 574 in DateInput, and 75 instead of 1,023 in DateTimeInput.
  • A WebKit race in two of these proofs is fixed: on a loaded runner they failed 3 times in 208 runs, a hold reaching the 0.25 s fade after it had ended, and a frame count running short. Red-first under 140 ms frames, the previous proofs failed 6/6 in WebKit and 3/6 in Chromium; these pass 6/6 in both. The second-tap proof read its peak from a log started after that wait, which could begin once the turnaround had begun (1 failure in 12 under load, peak 0.2997); with the log's first frame 200 ms late, the previous proof fails 3/3 in both engines and this one passes 3/3. MonthYearToggle 78/78 (6 skipped) over 6 runs in WebKit and 42/42 over 3 runs in Chromium.
  • New render spies in DateInputTouch.test.tsx: the toggle re-renders no Button or IconButton and the wheels once (as they go live); it failed before (6 button renders). Under a finger the calendar stays visible beneath the opaque wheels (aria-hidden and inert keep it out of reach), so closing flips no inherited visibility; the inert swap follows the fade's end event.
  • New useFadeInert.test.tsx: inert lands on the exit's transitionend or transitioncancel (not another property's), at once when no exit runs, and with a mouse in the toggle's own commit (one commit per toggle); focus inside the outgoing panel moves to the incoming one, which goes live for it; focus outside stays.
  • Android trace (browser-wide Chrome trace on the emulator over the opening toggle, 3 takes each; frames are Viz draws): the longest gap after the click is 71–72 ms in this PR (225 ms in one take that compiled Dawn pipelines on first use) and 73–106 ms before. In every gap the renderer's main thread runs at most 10 ms, while the GPU process rasters the newly shown tiles (56–208 ms on its main thread) and draws the fading group's render pass (up to 74 ms).
  • The iOS table above: grid-area brightness of every recorded frame of each simulator take.
  • Top of the stack (gated on test(TimeInput, DateTimeInput): a stepped minute wheel loops like the full list under a finger #8398's head 5bb9440, which its head 1e4fcad changes only by a comment: fix(core): one click guard for the menu press model and the gesture host #8360 @ 045f666, feat(Wheel): an owner's rest rule and turns from script for the picker wheel #8527 1d7ee3e with feat(Wheel): a short looping list repeats down the column under a finger #8366 5292d5e, fix(DateInput, DateTimeInput): the month scroller's steps follow design:reduced-motion #8496 cdbe481, chore(core): a pager that pages like a UIKit paging scroll view #8146 bb3b76b): pnpm build then the root pnpm typecheck; CI=true pnpm lint; the full core suite at 16 workers, 15,518 of 15,520 (the two Selector and MultiSelector bottom-sheet cases are test(Selector, MultiSelector): wait for the on-demand sheet module before the first open #8642's load red); the node project (its known reds, and touch-support-bundle, which test(core): a touch component's import carries its gesture code, and nothing else does #8671 replaces: this head passes test(core): a touch component's import carries its gesture code, and nothing else does #8671's version, 8 of 8); interaction specs (MonthYearToggle, TimeWheels, MonthYearWheels, MonthScroller, MonthPager, WheelActivation) twice over: Chromium 93 of 94 (feat(Wheel): an owner's rest rule and turns from script for the picker wheel #8527's WheelActivation load red), WebKit 84 plus 10 skips.
  • Typecheck; ESLint and Prettier on touched files; check:changesets.
  • CI's scripts/touch-support-bundle.test.mjs "is absent from the root entry" fails for every touch component under main's current test; test(core): a touch component's import carries its gesture code, and nothing else does #8671 replaces it with the per-component check, which lists DateInput, DateTimeInput, PowerSearch and TimeInput.

Stack (merge in this order): … → #8178 → #8179 → #8146 → #8496 → #8200 → this PR (the full order is in #8156).

@vercel

vercel Bot commented Oct 24, 2026 •

Copy link
Copy Markdown

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

Project Deployment Actions Updated
astryx Error Error Oct 30, 2026 12:45am UTC

Request Review

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

astracat-bot Bot commented Oct 24, 2026 •

Copy link
Copy Markdown

AI review status for this pull request.

Review status Updated
❓ Needs maintainer input (for maintainers only) Oct 30, 2026, 1:09 AM UTC

@github-actions

github-actions Bot commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

PR Analysis Report

📚 Storybook Preview

View Storybook for this PR

🧪 Sandbox Preview

View Sandbox for this PR

Modified Components

DateInput (@astryxdesign/core) · View in Storybook
Metric Before After Delta
Entry File (ESM) 266B 266B 0B
Lines of Code 3532 6774 -
Complexity Very High Very High (607) -
DateTimeInput (@astryxdesign/core) · View in Storybook
Metric Before After Delta
Entry File (ESM) 286B 286B 0B
Lines of Code 3609 3766 -
Complexity Very High Very High (566) -
TimeInput (@astryxdesign/core) · View in Storybook
Metric Before After Delta
Entry File (ESM) 411B 411B 0B
Lines of Code 1245 1728 -
Complexity Very High Very High (204) -

Component Size

Component Import before Import after Delta CSS before CSS after Delta
core/AlertDialog 56.9KB 57.2KB +294B 12.4KB 12.4KB 0B
core/AppShell 57.9KB 58.1KB +242B 11.6KB 11.6KB 0B
core/AvatarGroupOverflow 39.9KB 40.2KB +303B 6.4KB 6.4KB 0B
core/Banner 47.8KB 48.1KB +289B 9.9KB 9.9KB 0B
core/BaseTypeahead 54.3KB 54.6KB +251B 9.9KB 9.9KB 0B
core/BottomSheet 34.0KB 33.9KB -9B 7.9KB 7.9KB -22B
core/BottomSheetSwitcher 14.8KB 14.8KB 0B 3.3KB 3.3KB 0B
core/BreadcrumbItem 66.4KB 66.6KB +297B 11.0KB 11.0KB 0B
core/Button 43.3KB 43.5KB +273B 9.2KB 9.2KB 0B
core/Calendar 54.9KB 55.2KB +290B 10.0KB 10.0KB 0B
core/Carousel 50.2KB 50.5KB +316B 10.2KB 10.2KB 0B
core/ChatComposer 60.6KB 60.9KB +270B 10.8KB 10.8KB 0B
core/ChatComposerInput 55.7KB 56.0KB +264B 10.1KB 10.1KB 0B
core/ChatComposerTokenElement 55.7KB 56.0KB +264B 10.1KB 10.1KB 0B
core/ChatDictationButton 46.5KB 46.8KB +264B 9.5KB 9.5KB 0B
core/ChatLayout 48.8KB 49.1KB +273B 9.9KB 9.9KB 0B
core/ChatLayoutScrollButton 46.4KB 46.6KB +255B 9.6KB 9.6KB 0B
core/ChatSendButton 45.8KB 46.1KB +265B 9.2KB 9.2KB 0B
core/CheckboxInput 49.9KB 50.2KB +273B 9.3KB 9.3KB 0B
core/CheckboxListItem 54.0KB 54.3KB +244B 10.5KB 10.5KB 0B
core/ClickableCard 12.8KB 13.1KB +289B 6.9KB 6.9KB 0B
core/CodeBlock 60.5KB 60.8KB +278B 10.7KB 10.7KB 0B
core/Collapsible 11.7KB 12.0KB +291B 4.2KB 4.2KB 0B
core/CommandPalette 50.9KB 51.2KB +281B 11.8KB 11.8KB 0B
core/CommandPaletteItem 8.4KB 8.7KB +280B 3.5KB 3.5KB 0B
core/ComplexSelector 55.8KB 56.1KB +318B 10.6KB 10.6KB 0B
core/ContextMenu 77.7KB 78.0KB +275B 14.6KB 14.6KB -13B
core/ContextMenuItem 16.6KB 16.8KB +280B 5.2KB 5.2KB 0B
core/DateInput 99.9KB 146.3KB +46.4KB ⚠️ 14.8KB 15.1KB +355B
core/DateRangeInput 69.4KB 69.6KB +266B 11.2KB 11.2KB 0B
core/DateTimeInput 110.1KB 159.0KB +48.9KB ⚠️ 15.2KB 15.7KB +444B
core/DialogHeader 48.8KB 49.1KB +280B 9.8KB 9.8KB 0B
core/DrawerHeader 48.9KB 49.2KB +249B 11.2KB 11.2KB 0B
core/DropdownMenu 72.7KB 73.0KB +277B 14.7KB 14.7KB -12B
core/DropdownMenuCheckboxItem 16.1KB 16.4KB +284B 5.6KB 5.6KB 0B
core/DropdownMenuItem 15.4KB 15.7KB +287B 5.2KB 5.2KB 0B
core/DropdownMenuRadioItem 16.2KB 16.5KB +275B 5.6KB 5.6KB 0B
core/DropdownMenuSubMenu 58.1KB 58.3KB +270B 9.5KB 9.5KB 0B
core/FileInput 54.1KB 54.4KB +262B 10.3KB 10.3KB 0B
core/IconButton 43.3KB 43.6KB +273B 9.2KB 9.2KB 0B
core/Item 11.4KB 11.6KB +273B 4.7KB 4.7KB 0B
core/LayerProvider 63.3KB 63.6KB +288B 10.1KB 10.1KB 0B
core/Lightbox 51.2KB 51.4KB +249B 9.8KB 9.8KB 0B
core/Link 43.5KB 43.8KB +288B 7.6KB 7.6KB 0B
core/ListItem 53.5KB 53.7KB +268B 10.5KB 10.5KB 0B
core/Markdown 144.5KB 144.7KB +247B 17.4KB 17.3KB -14B
core/MobileNav 51.0KB 51.3KB +264B 10.1KB 10.1KB 0B
core/MobileNavToggle 46.2KB 46.5KB +259B 9.4KB 9.4KB 0B
core/MoreMenu 72.9KB 73.2KB +278B 14.7KB 14.7KB -12B
core/MultiSelector 76.2KB 76.4KB +270B 14.6KB 14.6KB -11B
core/NumberInput 55.7KB 56.0KB +278B 10.7KB 10.7KB 0B
core/Outline 28.6KB 28.9KB +290B 4.0KB 4.0KB 0B
core/Pagination 87.7KB 88.0KB +263B 15.3KB 15.3KB -17B
core/Popover 49.8KB 50.1KB +238B 12.1KB 12.1KB -9B
core/PowerSearch 155.1KB 203.4KB +48.3KB ⚠️ 18.6KB 19.0KB +384B
core/RadioListItem 52.1KB 52.4KB +286B 9.7KB 9.7KB 0B
core/SegmentedControlItem 8.6KB 8.9KB +285B 3.9KB 3.9KB 0B
core/SelectableCard 12.8KB 13.1KB +294B 7.1KB 7.1KB 0B
core/Selector 79.1KB 79.4KB +267B 15.0KB 15.0KB -14B
core/SelectorOption 14.5KB 14.8KB +251B 5.1KB 5.1KB 0B
core/SideNav 58.0KB 58.3KB +272B 10.6KB 10.6KB 0B
core/SideNavCollapseButton 46.6KB 46.9KB +242B 9.5KB 9.5KB 0B
core/SideNavHeading 54.7KB 55.0KB +248B 10.1KB 10.1KB 0B
core/SideNavItem 54.5KB 54.8KB +266B 10.1KB 10.1KB 0B
core/Step 32.4KB 32.7KB +279B 5.3KB 5.3KB 0B
core/Stepper 47.4KB 47.7KB +265B 9.6KB 9.6KB 0B
core/Switch 48.4KB 48.7KB +285B 9.3KB 9.3KB 0B
core/Tab 9.7KB 10.0KB +294B 4.1KB 4.1KB 0B
core/TabMenu 51.6KB 51.9KB +276B 10.0KB 10.0KB 0B
core/Table 84.0KB 84.2KB +253B 15.1KB 15.1KB -12B
core/TableCell 78.8KB 79.1KB +267B 14.8KB 14.8KB -13B
core/TableHeaderCell 78.6KB 78.8KB +272B 14.8KB 14.8KB -13B
core/TableSelectionToolbar 52.4KB 52.6KB +278B 11.2KB 11.2KB 0B
core/TextInput 53.5KB 53.8KB +281B 10.5KB 10.5KB 0B
core/Thumbnail 50.7KB 50.9KB +274B 11.0KB 11.0KB 0B
core/TimeInput 86.8KB 116.5KB +29.7KB ⚠️ 13.8KB 13.8KB +19B
core/Timestamp 27.5KB 27.5KB -1B 9.5KB 9.5KB 0B
core/Toast 59.8KB 60.1KB +258B 9.7KB 9.7KB 0B
core/ToggleButton 44.1KB 44.4KB +243B 9.3KB 9.3KB 0B
core/Token 29.6KB 29.9KB +289B 4.5KB 4.5KB 0B
core/Tokenizer 64.8KB 65.1KB +306B 11.7KB 11.7KB 0B
core/TopNav 51.9KB 52.1KB +277B 10.4KB 10.4KB 0B
core/TopNavHeading 54.2KB 54.4KB +231B 9.9KB 9.9KB 0B
core/TopNavItem 9.0KB 9.3KB +283B 3.8KB 3.8KB 0B
core/TopNavMegaMenu 54.2KB 54.4KB +268B 10.8KB 10.8KB 0B
core/TopNavMegaMenuItem 9.1KB 9.4KB +281B 4.1KB 4.1KB 0B
core/TopNavMenu 53.6KB 53.8KB +258B 10.1KB 10.1KB 0B
core/TreeList 32.5KB 32.8KB +274B 4.7KB 4.7KB 0B
core/Typeahead 61.5KB 61.7KB +268B 11.3KB 11.3KB 0B

Sizes are gzip. Import counts the JavaScript the import loads eagerly; CSS counts the package stylesheet rules referenced by the modules it can load from its own package, lazily loaded ones included.

⚠️ Touch-only size budget

These components grew past the touch-only budget (2 KB gzip JavaScript or 1 KB gzip CSS per component). If the growth runs only on a coarse pointer, load its JavaScript lazily so the import does not carry it, or justify the cost in the pull request description; CSS stays in the package stylesheet, so justify it.

Component JavaScript CSS
core/DateInput +46.4KB ⚠️ +355B
core/DateTimeInput +48.9KB ⚠️ +444B
core/PowerSearch +48.3KB ⚠️ +384B
core/TimeInput +29.7KB ⚠️ +19B

Bundle Size Summary

Gzip figures measure each package's entry file, not the code an import pulls in.

Package Entry (ESM) Entry gzip before Entry gzip after Delta
@astryxdesign/core 4.9KB 1.2KB 1.2KB 0B
Package stylesheet Gzip before Gzip after Delta
@astryxdesign/core 34.6KB 34.9KB +337B

Accessibility Audit

Status: No accessibility violations detected.

Visual Regression

55 of 502 shot(s) changed.

A change here is a question, not a failure: check whether the after is the
picture you intended. Record that review on the PR. Baseline maintenance is an
explicit dispatch of CI; this report never rewrites the baseline or adds a release gate.

component story theme mode pixels
Selector Indicator Space Bottom Sheet Narrow End RTL neutral light 753,933
Selector Indicator Space Bottom Sheet Wide Start neutral light 740,072
RichTextEditor Markdown parity: side by side probe dark 558,840
RichTextEditor Markdown parity: side by side probe light 540,756
Chat Full AI Chat probe light 230,823
Chat Full AI Chat probe dark 229,805
AppShell With Mobile Nav probe light 217,010
AppShell With Mobile Nav probe dark 216,960
Collapsible Dividers probe dark 66,445
Collapsible Dividers probe light 66,445
AppShell Playground neutral dark 65,198
AppShell Playground neutral light 63,788
Chat Default neutral dark 36,304
Chat Default neutral light 35,767
Chat Empty State probe light 23,531
Chat Empty State probe dark 22,622
ChatAutoScroll Scroll Behavior Comparison neutral dark 15,378
ChatAutoScroll Scroll Behavior Comparison neutral light 14,107
Chat With Attachments probe light 13,406
Chat With Attachments probe dark 11,232

and 35 more.


Generated by PR Enrichment workflow | Storybook | Sandbox | View full report

@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.

Approval waits on an engineering owner's decision. The calendar toggle's timing moves from the theme's motion tokens to UIKit constants under draft AST-094 (FR1, FR4), which would narrow the current theme-tokens architecture (INV3) for touch presentations, and the reading-direction chevron that turns down has no governing spec yet. An engineering owner records that decision by commenting /approve-spec <full head SHA> on this pull request once the records say what is approved; the author may do so as an engineering owner. Whatever is decided, these need changing:

  • Land the stacked predecessors first, so this pull request's compare holds its own 371-line commit instead of 36,310 lines.
  • Make the lint check pass: ESLint runs out of memory on this head but passes on main.
  • Add real-browser evidence for DateTimeInput's new cross-fade and chevron turn and for the RTL chevron (AST-013 FR10), since the browser spec covers only the DateInput story in LTR.
  • Tag .changeset/calendar-toggle-uidatepicker-timing.md [feat] rather than [fix] and name the old chevron, because it changes visual direction (AST-017 FR3).

[Automated review]

'@astryxdesign/core': patch
---

[fix] The calendar in the DateInput and DateTimeInput bottom sheets now follows the iOS date picker's timing:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This entry describes an intentional change of visual direction (a new chevron glyph and new motion timing) that no current visual authority covers, so AST-017 FR3 calls for [feat] rather than [fix]. FR3 also asks the entry to name the old appearance: a down chevron that flipped up.

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.

See the newer thread: the body records the visual direction as proposed, pending owner review.

const ease = cubicBezier(...timing.curves.easeInOut);

async function openCalendar(page: Page) {
await openStory(page, storybook(), 'core-dateinput--bottom-sheet-calendar');

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Only the DateInput story is opened here, in LTR. DateTimeInput's new cross-fade and chevron turn, and the mirrored chevron in RTL, are paint changes too (AST-013 FR10). Running the same samples against a DateTimeInput presentation: 'bottom-sheet' story and with the RTL direction global would cover them.

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 in 0fbbb07: the spec now covers DateTimeInput's cross-fade curve and the mirrored chevron in RTL.

@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.

Approval waits on an engineering owner's decision: the calendar toggle's timing moves from the theme's motion tokens to UIKit constants under draft AST-094 (FR1, FR4), which would narrow the current theme-tokens architecture (INV3) for touch presentations, and the reading-direction chevron that turns down has no governing spec yet. An engineering owner records that decision by commenting /approve-spec <full head SHA> on this pull request once the records say what is approved; the author may do so as an engineering owner. Whatever is decided, these still need changing:

  • Land the stacked predecessors first, so this pull request's compare holds its own 371-line commit instead of 36,158 lines.
  • Tag .changeset/calendar-toggle-uidatepicker-timing.md [feat] rather than [fix] and name the old chevron, because it changes visual direction (AST-017 FR3).
  • AST-094 FR7 cites AST-090, which only #8003 proposes, so land that first or qualify the sentence.

[Automated review]

follows.
- **FR7 — Exits animate as UIKit's do.** A touch presentation MUST play the
exit its UIKit counterpart plays. **Proposed; pending owner review:** this
departs from the draft `spec:AST-090` FR1 (exits off by default) and FR10

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

spec:AST-090 is not in the repository at this head or on main; it is proposed only in #8003. If this lands first, FR7 states a departure from a record readers cannot open. Landing after #8003, or qualifying the sentence until then, would keep the citation honest.

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.

At 0fbbb07 this branch no longer edits docs/specs/AST-094.

@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.

Approval waits on an engineering owner's decision: the calendar toggle's timing moves from the theme's motion tokens to UIKit constants under draft AST-094 (FR1, FR4), which would narrow the current theme-tokens architecture (INV3) for touch presentations, while the reading-direction chevron and keeping the full 250 ms fade and 90° turn under Reduce Motion (draft FR8) have no governing spec yet. An engineering owner records that decision by commenting /approve-spec <full head SHA> on this pull request once the records say what is approved; the author may do so as an engineering owner. Whatever is decided, these still need changing:

  • Land the stacked predecessors first, so this pull request's compare holds its own 671-line commit instead of 36,863 lines.
  • Tag .changeset/calendar-toggle-uidatepicker-timing.md [feat] rather than [fix] and name the old chevron and the old 0.01 s Reduce Motion timing, because it changes visual direction (AST-017 FR3).
  • AST-094 FR7 cites AST-090, which only #8003 proposes, so land that first or qualify the sentence.

[Automated review]


[fix] The calendar in the DateInput and DateTimeInput bottom sheets now follows the iOS date picker's timing:

- The month/year toggle cross-fades in 0.25 s on iOS's ease-in-out curve. It used to be a theme-speed linear fade in DateInput and instant in DateTimeInput. With Reduce Motion on, the fade and the chevron's turn keep their timing, since neither travels.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

People with Reduce Motion on go from a toggle that finished in 0.01 s to a 250 ms fade and a 90° chevron turn, so "keep their timing" reads as no change. Naming the old 0.01 s timing here, and the old down chevron below, would let release-note readers see what changes for them (AST-017 FR3).

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 in 0fbbb07 per design:reduced-motion. Under a finger the toggle is a 200 ms opacity cross-fade and the chevron dips instead of turning; with a mouse it changes at once.

@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.

Approval waits on an engineering owner's decision: the calendar toggle's timing moves from the theme's motion tokens to UIKit constants under draft AST-094 (FR1, FR4), which would narrow the current theme-tokens architecture (INV3) for touch presentations, while the reading-direction chevron and keeping the full 250 ms fade and 90° turn under Reduce Motion (draft FR8) have no governing spec yet. An engineering owner records that decision by commenting /approve-spec <full head SHA> on this pull request once the records say what is approved; the author may do so as an engineering owner. Whatever is decided, these still need changing:

  • Land the stacked predecessors first, so this pull request's compare holds its own 1,001 lines instead of 37,193.
  • Attach an iOS simulator recording of the CSS fade jumping after a busy commit, or keep the fades as CSS transitions, because moving them into an Effect needs that Mobile Safari evidence (component-style-authoring INV2, AST-013 FR10).
  • Tag .changeset/calendar-toggle-uidatepicker-timing.md [feat] rather than [fix] and name the old chevron and the old 0.01 s Reduce Motion timing (AST-017 FR3).
  • AST-094 FR7 cites AST-090, which only #8003 proposes, so land that first or qualify the sentence.

[Automated review]

): (element: HTMLElement | null) => void {
const {ref, animator} = useAnimator({initial: {opacity: isShown ? 1 : 0}});
const shownRef = useRef(isShown);
useIsomorphicLayoutEffect(() => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This Effect now owns the toggle’s opacity instead of a CSS transition. architecture:component-style-authoring INV2 keeps a transition CSS can derive out of Effects unless CSS cannot derive it, and the only reason given is a Mobile Safari jump after a busy commit, which spec:AST-013 FR10 says needs evidence from that browser. The busy-commit proof in MonthYearToggle.interaction.browser.spec.ts passes with a CSS fade in Chromium and Playwright WebKit by its own comment. An iOS simulator recording of the previous CSS fade jumping would settle it; otherwise the CSS transitions can stay.

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: the toggle is back on CSS transitions in 0fbbb07, and useToggleFade is gone. Measured on the iOS simulator with the main thread blocked as the toggle commits, two takes each. The CSS fade's first frame showed opacity 0.85 and 0.81, then about 170 ms of fade. The animator's showed 0.93 and then about 190 ms in one take, and jumped in one frame in the other. For this head, the body's iOS table gives how far the fade has run at the first presented frame.

'@astryxdesign/core': patch
---

[fix] The calendar in the DateInput and DateTimeInput bottom sheets now follows the iOS date picker's timing:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The new chevron glyph and turn, the fixed timing and the Reduce Motion behaviour change visual direction with no current visual authority behind them, so spec:AST-017 FR3 calls for [feat]. FR3 also asks the entry to name the old appearance: a down chevron that flipped up, and a toggle that finished in 0.01 s with Reduce Motion on.

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 body's Visual authority section records it: none is settled, and the chevron glyph is proposed, pending owner review (cixzhang).

follows.
- **FR7 — Exits animate as UIKit's do.** A touch presentation MUST play the
exit its UIKit counterpart plays. **Proposed; pending owner review:** this
departs from the draft `spec:AST-090` FR1 (exits off by default) and FR10

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

spec:AST-090 is not in the repository at this head or on main; only #8003 proposes it. Landing after #8003, or qualifying the sentence until then, keeps the citation readable.

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.

At 0fbbb07 this branch no longer edits docs/specs/AST-094; that record is #8316's.

vjeux added 20 commits October 29, 2026 13:37
The touch column's options lie under its drawn rows, so the proof taps the
PM row's place on the screen, where the column's tap recognizer takes it,
rather than asking for a tap on the option element.
…o their own changesets

In the same release meta#8179's time columns loop, so "no shipped column loops
yet" would contradict it; the columns that loop say so themselves.
…heirs) into uikit/c21-wheel-rest-and-spin
… while the toggle fades

The header's arrows and Reset, and DateInput's footer actions, sit outside the shield that covers the panels during the month/year cross-fade, and their `inert` followed the panels' (deferred to the fade's end). During the opening fade a tap on Done fell through the still-inert Done to Save beneath it and closed the DateInput sheet, and DateTimeInput's fading arrows and Reset still paged the month or cleared the pick. These few controls now leave with the toggle and arrive with the fade's end; only the panels' own `inert` waits for the fade, since flipping it restyles a panel's whole subtree.
… the glyph is

Under Reduce Motion on a touch screen, a second title tap within the 200 ms dip restarted it from opacity 1, flashing the chevron back to opaque. The new dip now starts from the running dip's presented opacity, read before it is cancelled (a style read only in that case).
…llows the toggle during render

`useFadeInert` mirrored the toggle into state from a layout effect when the swap is not deferred, costing a second commit per toggle (architecture:react-component-runtime INV1, INV3). `settled` now follows `isWheelOpen` during render in that case; the effect keeps only the branch where the committed panel shows no exit can run (no Web Animations), with a reason naming that read.
…fect synchronizes with

The effect waits on the browser's clock and idle time (a timer for the sheet's present motion, then an idle callback), and its cleanup cools the wheels for the next opening (architecture:react-component-runtime INV3).
…ged, and where it was measured

The panels' `inert` swaps when the fade ends (not a frame later), the commit's restyle is described without claiming it changes no inherited style, the old chevron and the 0.01 s fade are DateInput's, and the iPhone and Android claims are narrowed to the iOS simulator measurement.
…s own spin

meta#8527's `spinTo` no longer takes a duration or a curve, so a column the time wheels move by themselves turns on the wheel's own spin: an animated `setContentOffset`, 0.3 s on Core Animation's ease-in-ease-out (`picker.tapSpin`, the role `timing.curves.move`), resolved under the column when the turn starts (spec:AST-094 FR4). TimeWheels no longer plays the literal `datePicker.wheels.columnTurn`, and the browser proof samples the AM/PM column's turn against that spin.
…ay round its seam

Under min 13:00 and max 23:00 the hours 0 to 12 are disabled; one ArrowDown from 23 crosses the seam onto 0, and the nearest enabled hour around the loop is 23 itself, so nothing moves or commits. It rested on 13 before meta#8527's seam-aware rest (f4386c5).
@vjeux
vjeux marked this pull request as draft October 29, 2026 22:33
vjeux added a commit to vjeux/astryx that referenced this pull request Oct 30, 2026

@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.

Approval waits on an engineering owner's decision: no current record covers the title chevron that rests along the reading direction and turns a quarter turn down (spec:AST-094 leaves glyphs out of its scope), and the draft DateInput and DateTimeInput requirements this compare carries from its predecessors (component:DateInput FR13 with DEC-1, PR2 and AR8; component:DateTimeInput FR8) are unapproved. An engineering owner can provide or identify the governing spec, or record approval by commenting /approve-spec <full head SHA> on this pull request; the author may do so as an engineering owner. The toggle's 250 ms timing, the tapped day and Reduce Motion already follow current spec:AST-094 FR1, FR4 and FR8. Separately, these need changes before approval:

  • Land the predecessors first: compared with main, this pull request carries about 49,000 authored lines of the open stack.
  • The touch minute wheel now steps by increment, which current component:TimeInput defines as the typed field's arrow-key step; amend that row in its own pull request, or keep the sheet listing every minute.
  • Add real-device recordings for the iOS Safari and Android Chrome claims, as spec:AST-094's platform support asks.
  • In the changeset, name DateTimeInput's old chevron (a down chevron that never moved) and narrow “nothing in the header or the footer takes a tap during the fade”, since a second tap on the title turns the fade around.

[Automated review]


[feat] Under a finger, the calendar in the DateInput and DateTimeInput bottom sheets now follows the iOS date picker's timing:

- The month/year toggle cross-fades in 0.25 s on iOS's ease-in-out curve. It was DateInput's quick linear fade and DateTimeInput's instant swap. Once the sheet has settled, the hidden month/year wheels are laid out at opacity 0, and the panels swap `inert` when the fade ends, so the toggle's commit restyles little and, on the iOS 26.2 simulator, the fade is on screen from near its start. As in UIKit, nothing in the header or the footer takes a tap during the fade.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The title sits in the header, and a second tap on it mid-fade turns the fade around (the pull request's own browser proof does exactly that), so “nothing in the header or the footer takes a tap during the fade” overstates it. Naming what goes inert, the month arrows and Reset in the header and Save and Done in the footer, would match the code.

[feat] Under a finger, the calendar in the DateInput and DateTimeInput bottom sheets now follows the iOS date picker's timing:

- The month/year toggle cross-fades in 0.25 s on iOS's ease-in-out curve. It was DateInput's quick linear fade and DateTimeInput's instant swap. Once the sheet has settled, the hidden month/year wheels are laid out at opacity 0, and the panels swap `inert` when the fade ends, so the toggle's commit restyles little and, on the iOS 26.2 simulator, the fade is on screen from near its start. As in UIKit, nothing in the header or the footer takes a tap during the fade.
- The title's chevron points along the reading direction and turns a quarter turn to point down while the wheels show. In DateInput it was a down chevron that flipped up.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This names DateInput's old chevron but not DateTimeInput's, which pointed down and never moved. spec:AST-017 FR3 asks a visual-direction entry to name the old and new appearance; adding “In DateTimeInput it was a down chevron that stayed still” would cover both sheets.

time={parsedWheelTime}
hourFormat={hourFormat}
hasSeconds={hasSeconds}
minuteInterval={minuteIntervalOf(increment)}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The sheet's minute wheel now steps by increment, but the current component:TimeInput record's Increment row still reads “Arrow-key step.” with availability “Typed surface”. Amending that row (and DateTimeInput's timeIncrement docs) in the pull request that introduces this, or keeping the sheet listing every minute, would keep code and record in step.

vjeux added 3 commits October 29, 2026 17:34
…names what it synchronizes

The un-keyed layout effect that lands a far jump synchronizes the pager's position with the DOM: it can move only once the render that mounts the target's pane has committed it, and moves before paint (architecture:react-component-runtime INV3).
vjeux added a commit to vjeux/astryx that referenced this pull request Oct 30, 2026

@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.

Approval waits on an engineering owner's decision: no current record covers the title chevron that rests along the reading direction and turns a quarter turn down, and the draft DateInput and DateTimeInput requirements this compare carries from its predecessors (component:DateInput FR13 with DEC-1, PR2 and AR8; component:DateTimeInput FR8) are unapproved. An engineering owner can identify the governing spec, or record approval by commenting /approve-spec <full head SHA> on this pull request; the author may do so as an engineering owner. The toggle's 250 ms timing, the tapped day and Reduce Motion already follow current spec:AST-094 FR1, FR4 and FR8. These still need changes before approval:

  • Land the predecessors first: against main this pull request carries about 49,000 authored lines of the open stack.
  • The touch minute wheel now steps by increment, which current component:TimeInput defines as the typed field's arrow-key step; amend that row in its own pull request, or keep the sheet listing every minute.
  • Add real-device recordings for the iOS Safari and Android Chrome claims, as spec:AST-094's platform support asks.
  • In the changeset, name DateTimeInput's old chevron (a down chevron that never moved) and narrow “nothing in the header or the footer takes a tap during the fade”, since a second tap on the title turns the fade around.

[Automated review]


[feat] Under a finger, the calendar in the DateInput and DateTimeInput bottom sheets now follows the iOS date picker's timing:

- The month/year toggle cross-fades in 0.25 s on iOS's ease-in-out curve. It was DateInput's quick linear fade and DateTimeInput's instant swap. Once the sheet has settled, the hidden month/year wheels are laid out at opacity 0, and the panels swap `inert` when the fade ends, so the toggle's commit restyles little and, on the iOS 26.2 simulator, the fade is on screen from near its start. As in UIKit, nothing in the header or the footer takes a tap during the fade.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The title sits in the header, and a second tap on it mid-fade turns the fade around (MonthYearToggle.interaction.browser.spec.ts:250-319 proves exactly that), so “nothing in the header or the footer takes a tap during the fade” is not what ships. Say that the header's month arrows and Reset and the footer's Save and Done take no tap during the fade.

[feat] Under a finger, the calendar in the DateInput and DateTimeInput bottom sheets now follows the iOS date picker's timing:

- The month/year toggle cross-fades in 0.25 s on iOS's ease-in-out curve. It was DateInput's quick linear fade and DateTimeInput's instant swap. Once the sheet has settled, the hidden month/year wheels are laid out at opacity 0, and the panels swap `inert` when the fade ends, so the toggle's commit restyles little and, on the iOS 26.2 simulator, the fade is on screen from near its start. As in UIKit, nothing in the header or the footer takes a tap during the fade.
- The title's chevron points along the reading direction and turns a quarter turn to point down while the wheels show. In DateInput it was a down chevron that flipped up.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This names DateInput's old chevron but not DateTimeInput's, which pointed down and never moved. spec:AST-017 FR3 asks a visual-direction entry to name the old and new appearance, so add DateTimeInput's.

time={parsedWheelTime}
hourFormat={hourFormat}
hasSeconds={hasSeconds}
minuteInterval={minuteIntervalOf(increment)}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The sheet's minute wheel now steps by increment, but current component:TimeInput's Increment row still reads “Arrow-key step.” with availability “Typed surface”, and the record is unchanged here; TouchDateTimeField.tsx:1439 does the same with timeIncrement. Amend the row (and DateTimeInput's timeIncrement docs) in the pull request that introduces this step, or keep the sheet listing every minute.

This branch had an error being deployed

1 failed deployment
Preview — c4d5c041 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. needs:design-review Affects visuals — Design should review needs:spec-owner-review Current knowledge records await owner approval

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants