From 890871ea3b86f9191b045d3b691a541a59725650 Mon Sep 17 00:00:00 2001 From: Christopher Chedeau Date: Mon, 2 Nov 2026 21:48:21 -0800 Subject: [PATCH 1/4] chore(core): UIDatePicker's wheel rules, measured on iOS 26.2 The date picker domain of the UIKit constants table, each rule sourced: the month/year wheels change the date, keeping the day; the AM/PM column follows the hour; a column that comes to rest past a bound turns back onto it; and a day the month lacks clamps to its last day. The date and time touch sheets follow these rules. Nothing is exported from the package entry point. --- .changeset/uikit-date-picker-values.md | 7 +++ packages/core/src/motion/MEASUREMENTS.md | 10 ++++ packages/core/src/motion/README.md | 1 + packages/core/src/motion/uikit/datePicker.ts | 60 ++++++++++++++++++++ packages/core/src/motion/uikit/index.ts | 7 ++- 5 files changed, 82 insertions(+), 3 deletions(-) create mode 100644 .changeset/uikit-date-picker-values.md create mode 100644 packages/core/src/motion/uikit/datePicker.ts diff --git a/.changeset/uikit-date-picker-values.md b/.changeset/uikit-date-picker-values.md new file mode 100644 index 0000000000000..09313746f4316 --- /dev/null +++ b/.changeset/uikit-date-picker-values.md @@ -0,0 +1,7 @@ +--- +'@astryxdesign/core': patch +--- + +[chore] Internal: the rules UIDatePicker adds to its wheels, measured on iOS 26.2, join the UIKit constants table: the month/year wheels changing the date, the AM/PM column following the hour, the turn back onto a bound, and the day clamped to the month. Each rule comes with its source. None is exported from the package entry point. + +@vjeux diff --git a/packages/core/src/motion/MEASUREMENTS.md b/packages/core/src/motion/MEASUREMENTS.md index bc49b27849b06..aaa6aafd51f35 100644 --- a/packages/core/src/motion/MEASUREMENTS.md +++ b/packages/core/src/motion/MEASUREMENTS.md @@ -268,6 +268,16 @@ match the probe. | M-SH21 | SheetProbe on iPad, iOS 27 `preferredPlacement` each case, RTL | present | placement geometry and motion | B, C | | M-SH22 | SheetProbe iOS 26 `preferredTransition = .zoom` from a bar button | present, dismiss | morph geometry per frame | A, C | +## Date picker (UIDatePicker) + +Rules the DateInput, DateTimeInput and TimeInput touch sheets follow, in their +own table (`uikit/datePicker.ts`). The wheels' physics are UIPickerView's: the +table only names them, and their values belong to the wheel's own table. + +Done (UIKit lab, iOS 26.2 simulator, Oct 23 2026; in the date picker's table): +the month/year wheels changing the date, the AM/PM column following the hour, +the turn back onto a bound, and the day clamped to the month. + ## Context menu (UIContextMenuInteraction) Values the context menu reads from its own table, which lands with the diff --git a/packages/core/src/motion/README.md b/packages/core/src/motion/README.md index b935f744a8cbb..fa0341f398e59 100644 --- a/packages/core/src/motion/README.md +++ b/packages/core/src/motion/README.md @@ -26,6 +26,7 @@ roles through `resolveMotion`. | `uikit/picker.ts` | UIPickerView: row pitch, height and rubber band, velocity floor, the coast's stop speed, snap at rest, tap spin, tick. | | `uikit/segmented.ts` | UISegmentedControl: the selection indicator's spring; under Reduce Motion the animator's cross-fade replaces it. | | `uikit/switch.ts` | UISwitch on iOS 26.2: geometry, the lift, settle, travel and tracking springs, the flip distance, the track colour. | +| `uikit/datePicker.ts` | UIDatePicker: the rules it adds to its wheels. | | `roles.ts` | The internal motion roles: the springs, durations and curves `spec:AST-094` FR4 names, registered by the shared tables, and a role's CSS form read back. | | `roleValue.ts` | A role's CSS form, written: a spring's duration and bounce, a duration in ms, a curve's `cubic-bezier()`. | | `resolveMotion.ts` | A role's value: its table value, as the per-role tables stay internal (`spec:AST-094` DEC-15). | diff --git a/packages/core/src/motion/uikit/datePicker.ts b/packages/core/src/motion/uikit/datePicker.ts new file mode 100644 index 0000000000000..ab482bb4cb4ce --- /dev/null +++ b/packages/core/src/motion/uikit/datePicker.ts @@ -0,0 +1,60 @@ +// Copyright (c) Meta Platforms, Inc. and affiliates. + +/** + * @file datePicker.ts + * @input Nothing; data only + * @output Exports `datePicker`: the rules UIDatePicker adds to its wheels. The + * month/year wheels change the date, the AM/PM column follows the hour, a + * column turns back onto a bound, and a day clamps to its month. + * @position The date picker domain of the UIKit constants table + * (motion/uikit), for the date and time touch sheets. Every leaf names its + * source, which uikit.test.ts checks. + * + * SYNC: When modified, update /packages/core/src/motion/MEASUREMENTS.md and + * /packages/core/src/motion/README.md. + */ + +export const datePicker = { + calendar: { + monthYearWheels: { + /** + * Turning the month or year wheel changes the picker's date, keeping + * the day, and fires `valueChanged`. + * @source measured: UIKit lab, iOS 26.2 simulator, `.date` `.inline`, + * October 15 → May 15 after the month wheel's fling, with a + * `valueChanged` carrying May 15, Oct 23 2026 + */ + changesDate: true, + }, + }, + wheels: { + /** + * In 12-hour time the AM/PM column follows the hour. Turning the hour from + * 11 to 12 turns the time from AM to PM, and back. From 12 to 1 it keeps + * the half of the day. + * @source measured: UIKit lab, iOS 26.2 simulator, `.time` `.wheels`, + * en_US: 11:58 AM, hour wheel +1 row → `valueChanged` 12:58 PM; then + * the minute wheel's +4 rows carried the hour from 12 to 1 → 1:02 PM, + * Oct 23 2026 + */ + meridiemFollowsHour: true, + /** + * A column that comes to rest past `minimumDate` or `maximumDate` turns + * back onto the bound. Its `valueChanged` already carries the bound. For + * example, with bounds 9:00–17:00, the hour of 10:00 dragged to 7 reads + * 9:00 at rest. The hour column then turns 64 pt, two rows, back to 9. + * @source measured: UIKit lab, iOS 26.2 simulator, `.time` `.wheels` + * with bounds 9:00–17:00 and `minuteInterval` 15, per-frame hour + * offsets after the rest (0.4, 2, 7.4, 16, 26.4, 37.7, 48.3, 56.7, + * 62, 64 pt at 33 ms steps) and the `valueChanged` time, Oct 23 2026 + */ + restPastBound: 'turnToBound', + /** + * In `.date` wheels a day the month does not have clamps to the month's + * last day (January 30, month +1 → February 28). + * @source measured: UIKit lab, iOS 26.2 simulator, `.date` `.wheels` + * with bounds Jun 1 2026 – Mar 15 2027, Oct 23 2026 + */ + dayClampsToMonth: true, + }, +} as const; diff --git a/packages/core/src/motion/uikit/index.ts b/packages/core/src/motion/uikit/index.ts index de0bee40f0ba0..f9bbff0054a1f 100644 --- a/packages/core/src/motion/uikit/index.ts +++ b/packages/core/src/motion/uikit/index.ts @@ -5,9 +5,9 @@ * @input The domain files of the UIKit constants table * @output Re-exports `springs` and one constant per spring (`SHEET_PRESENT_SPRING`, * …), `scroll`, `timing`, `navigation`, `picker`, `progressBar`, - * `segmentedControl`, the `slider` and switch (`switchControl`) domains, and - * the gesture thresholds (one `UIKIT_*` group per recognizer, their union - * `UIKIT_GESTURES`, and the haptic tables) + * `segmentedControl`, `datePicker`, `slider` and `switchControl`. It also + * re-exports the gesture thresholds: one `UIKIT_*` group per recognizer, + * their union `UIKIT_GESTURES`, and the haptic tables * @position Entry of the UIKit constants table (motion/uikit). Internal to * core: not exported from the package entry point * @@ -39,6 +39,7 @@ export { type GestureConstantUnit, type HapticFeedbackKind, } from './gestures'; +export {datePicker} from './datePicker'; export {navigation, registerNavigationTable} from './navigation'; export {picker} from './picker'; export {progressBar, registerProgressBarTable} from './progressBar'; From 69ac34d63b5fb983ec33dbe566a4719947fdc415 Mon Sep 17 00:00:00 2001 From: Christopher Chedeau Date: Mon, 2 Nov 2026 21:48:21 -0800 Subject: [PATCH 2/4] docs(DateInput): scope FR14's tap spin and FR18's mark to the touch wheel The mouse column turns by the browser's smooth scroll and marks each row as it scrolls, so FR14's tap-spin sentence and FR18 start with "Under a finger". The record's scope line lists FR17 and FR18. Two comments that restated the function below them are gone, and two changesets no longer present behavior that already shipped as new. --- .changeset/wheel-interaction-proofs.md | 2 +- .changeset/wheel-uikit-picker.md | 2 +- docs/specs/components/DateInput/DateInput.spec.md | 7 ++++--- packages/core/src/DateInput/PickerWheel.tsx | 2 -- packages/core/src/DateInput/Wheel.tsx | 1 - 5 files changed, 6 insertions(+), 8 deletions(-) diff --git a/.changeset/wheel-interaction-proofs.md b/.changeset/wheel-interaction-proofs.md index 2f9ad8ab99647..fbca1cccfa955 100644 --- a/.changeset/wheel-interaction-proofs.md +++ b/.changeset/wheel-interaction-proofs.md @@ -2,6 +2,6 @@ '@astryxdesign/core': patch --- -[chore] Internal: browser proofs for the touch picker wheel in Chromium and WebKit, which a finger gets on every engine. A flick coasts on UIKit's curve onto the row it projects and commits on arrival. A finger landing on a coasting wheel stops it, and it then eases onto the nearest row. A storyboard films a flick and its coast. In Chromium, the coast also keeps advancing every frame while the main thread is blocked, and does no Layout or Paint per frame. Under reduced motion, a flick still coasts as momentum with its rows flat, and a tap cross-fades onto its row, animating opacity alone for 200 ms or less. Opening the month and year wheels writes no element per year, and each year is drawn once. Under a mouse, a drag still scrolls today's column onto a row. +[chore] Internal: browser proofs for the touch picker wheel in Chromium and WebKit, which a finger gets on every engine. A flick coasts on UIKit's curve onto the row it projects and commits on arrival. A finger landing on a coasting wheel stops it, and it then eases onto the nearest row. A storyboard films a flick and its coast. In Chromium, the coast also keeps advancing every frame while the main thread is blocked, and does no Layout or Paint per frame. Under reduced motion, a flick still coasts as momentum with its rows flat, and a tap cross-fades onto its row, animating opacity alone for 200 ms or less. Opening the month and year wheels writes no element per year, and each year is drawn once. Under a mouse, a drag still scrolls the column onto a row. @vjeux diff --git a/.changeset/wheel-uikit-picker.md b/.changeset/wheel-uikit-picker.md index be37b67bb8378..296312ef12acf 100644 --- a/.changeset/wheel-uikit-picker.md +++ b/.changeset/wheel-uikit-picker.md @@ -2,6 +2,6 @@ '@astryxdesign/core': patch --- -[feat] Under a coarse primary pointer, as on a phone or a tablet, the picker wheels in the TimeInput, DateTimeInput and DateInput bottom sheets now move like UIPickerView. Their rows keep the 28 px pitch and lie flat, fading toward the edges of the column in every browser; the scrolling column under a mouse tilts and fades its rows only where the browser supports scroll-driven animations, and shows a plain list elsewhere. The row under the band is in semibold accent text on its plate while the wheel rests and while a finger drags it; no row is marked while the wheel coasts, snaps or turns. A flick coasts at UIKit's deceleration onto the row UIKit's projection picks. A finger landing on a moving wheel stops it. A tap on a row turns it onto the band, and a value changed from outside while the sheet is open, as when a bound clamps the time, turns the wheel onto its row instead of jumping there. Past the first or last row, a drag rubber-bands and the wheel bounces back. As on iOS, the value commits when the wheel comes to rest, and the coast keeps running while the page's main thread is busy. Arrow keys and clicks on a row still commit at once. Under reduced motion a flick still coasts, as scrolling does; a tap, a key or a slow release cross-fades onto its row. `presentation="popover"` keeps the previous surface; no value keeps the native-scroll column under a finger. Each row that reaches the band ticks with a short vibration where the browser supports it, as on Android; iOS Safari has no vibration. Under a fine primary pointer, as with a mouse on a desktop, the wheels keep the scrolling column. +[feat] Under a coarse primary pointer, as on a phone or a tablet, the picker wheels in the TimeInput, DateTimeInput and DateInput bottom sheets now move like UIPickerView. Their rows keep the 28 px pitch and lie flat, fading toward the edges of the column in every browser; the scrolling column under a mouse tilts and fades its rows only where the browser supports scroll-driven animations, and shows a plain list elsewhere. The row under the band is in semibold accent text on its plate while the wheel rests and while a finger drags it; no row is marked while the wheel coasts, snaps or turns. A flick coasts at UIKit's deceleration onto the row UIKit's projection picks. A finger landing on a moving wheel stops it. A tap on a row turns it onto the band, and a value changed from outside while the sheet is open, as when a bound clamps the time, turns the wheel onto its row. Past the first or last row, a drag rubber-bands and the wheel bounces back. As on iOS, the value commits when the wheel comes to rest, and the coast keeps running while the page's main thread is busy. Arrow keys and clicks on a row still commit at once. Under reduced motion a flick still coasts, as scrolling does; a tap, a key or a slow release cross-fades onto its row. `presentation="popover"` keeps the previous surface; no value keeps the native-scroll column under a finger. Each row that reaches the band ticks with a short vibration where the browser supports it, as on Android; iOS Safari has no vibration. Under a fine primary pointer, as with a mouse on a desktop, the wheels keep the scrolling column. @vjeux diff --git a/docs/specs/components/DateInput/DateInput.spec.md b/docs/specs/components/DateInput/DateInput.spec.md index 813dab250630d..5c43079eb1402 100644 --- a/docs/specs/components/DateInput/DateInput.spec.md +++ b/docs/specs/components/DateInput/DateInput.spec.md @@ -100,7 +100,8 @@ Consumer migration instructions belong in consumer docs and release notes. browser/OS date control. - The shared bottom-sheet picker wheel: how a wheel rests, commits, steps, turns, and looks wherever DateInput, TimeInput, or DateTimeInput renders it in a sheet - (FR13–FR15, DEC-1, DEC-2), and DateInput's own month and year wheels. + (FR13–FR15, FR17, FR18, DEC-1, DEC-2), and DateInput's own month and year + wheels. - The disabled-reason Tooltip composition: DateInput renders the reason Tooltip and links it from its focusable controls (`family:input-fields` representative matrix, "Member with `disabledMessage`"). @@ -155,11 +156,11 @@ Consumer migration instructions belong in consumer docs and release notes. | FR11 | Clear MUST emit `undefined` through the ordinary change pipeline. The pointer and bottom-sheet surfaces restore field focus using their shipped timing; the native surface MUST NOT reclaim focus because doing so reopens the OS picker. Implementation gap: when the field becomes disabled or busy while its clear control holds focus, the control unmounts and focus falls to the document body; `spec:AST-085/FR4` requires focus to stay or move to a destination this record names. | Current source and focused clear tests; `spec:AST-085/FR4` | | FR12 | No surface renders native `disabled`. A disabled field MUST stay focusable with `aria-disabled`, block typing, picking, clearing, and opening, describe its disabled reason when one is given, and take disabled paint. While busy (`isLoading` or a pending Action) the pointer and bottom-sheet surfaces expose `aria-disabled` and `aria-busy`, stay focusable, and block new commits; the native surface does the same for `isLoading`, and during a pending Action stays editable so the OS picker remains attached but rejects additional commits until the Action settles. A busy field that is not disabled keeps its ordinary paint and Spinner, shows no hover ring while commits are blocked, and keeps its keyboard focus ring. Caller handlers other than focus and blur MUST NOT run while the field is disabled (`spec:AST-085` FR2). Implementation gap: only a caller `onClick` is skipped while disabled; caller key, paste, and pointer handlers (such as `onKeyDown`, `onKeyUp`, `onPaste`, and `onPointerDown`) still run on every surface, the native field included. | `spec:AST-085/FR1–FR6`; `family:input-fields/FR4–FR6`; `family:buttons/FR4`; current source and focused tests | | FR13 | A bottom-sheet picker wheel that comes to rest on a disabled row (outside `min`/`max`) MUST continue to the nearest enabled row, a tie going toward the committed row, and select it there. A wheel resting on its committed row MUST NOT select or commit, even when that row is outside the bounds, so opening a bounded sheet never changes an untouched value: a committed value outside the bounds stays, and an empty field stays empty. This holds for every wheel in the DateInput, TimeInput, and DateTimeInput sheets. | DEC-1; UIDatePicker on the iOS 26.2 simulator; current source | -| FR14 | A shown bottom-sheet picker wheel whose value changes from outside, as when a bound clamps the time after another wheel turned, MUST turn onto the new row rather than jump to it. The turn plays the internal motion role `picker.tapSpin.durationMs`, UIKit's measured 300 ms, on `timing.curves.move`, as a tap on a row does. Under a finger, a tapped row commits its value when its turn ends; a keyboard or assistive-technology activation commits at once. | UIDatePicker on the iOS 26.2 simulator; current source | +| FR14 | A shown bottom-sheet picker wheel whose value changes from outside, as when a bound clamps the time after another wheel turned, MUST turn onto the new row rather than jump to it. Under a finger, the turn plays the internal motion role `picker.tapSpin.durationMs`, UIKit's measured 300 ms, on `timing.curves.move`, as a tap on a row does. Under a finger, a tapped row commits its value when its turn ends; a keyboard or assistive-technology activation commits at once. | UIDatePicker on the iOS 26.2 simulator; current source | | FR15 | Under a coarse primary pointer, a sheet wheel's pan takes a trackpad's two-finger scroll, as `spec:AST-094` FR43 allows a record to grant: the rows follow the fingers, coast on at the lift with the fingers' last speed, and rest on a row by FR13. A mouse-wheel notch turns the wheel one row, as an arrow key does, with no coast. A ctrl-scroll stays the page's zoom (`spec:AST-094` FR43). | `spec:AST-094` FR43; current source | | FR16 | Enter in the pointer field's text input commits typed text that has not been committed and is then held back, so a form never submits the value from before the commit. With no typed text pending, Enter is not prevented, so a form submits as it does from any text field, except on a disabled or busy field, where Enter does nothing (`spec:AST-085` FR2). | | FR17 | In a sheet wheel, Arrow, Page, Home and End move through the wheel's enabled values in time order, skipping disabled ones, and stop at the first or last enabled value; a key with no enabled value in its direction commits nothing, under every presentation. On a 12-hour hour wheel, time order follows the full time (12 PM comes before 1 PM), not the column's row order. | Owner decision (cixzhang, 2026-11-02); current source | -| FR18 | While a wheel coasts, snaps or turns, no row carries the selection band's accent mark; the mark returns on the row that comes to rest, as UIPickerView does (`spec:AST-094` FR3). | Owner decision (cixzhang, 2026-11-02); UIDatePicker on the iOS 26.2 simulator; current source | +| FR18 | Under a finger, while a wheel coasts, snaps or turns, no row carries the selection band's accent mark; the mark returns on the row that comes to rest, as UIPickerView does (`spec:AST-094` FR3). | Owner decision (cixzhang, 2026-11-02); UIDatePicker on the iOS 26.2 simulator; current source | | FR19 | The bottom-sheet calendar's weekday row MUST show the weekday names `component:Calendar` DD1 sets for the provider locale, in the order of the panes' day columns (`weekStartsOn`), so both pickers label a locale's columns alike. | ### Allowed variation diff --git a/packages/core/src/DateInput/PickerWheel.tsx b/packages/core/src/DateInput/PickerWheel.tsx index 8deee68ef95ce..9092ca2dc5749 100644 --- a/packages/core/src/DateInput/PickerWheel.tsx +++ b/packages/core/src/DateInput/PickerWheel.tsx @@ -586,8 +586,6 @@ export function PickerWheel({ isPointerKept: true, }); - // A key commits the row at `position` at once and turns the wheel there - // from `offset`. const turnToPosition = useCallback( (position: number, offset: number) => { const current = latestRef.current; diff --git a/packages/core/src/DateInput/Wheel.tsx b/packages/core/src/DateInput/Wheel.tsx index 29cb1e58485f0..b926189252454 100644 --- a/packages/core/src/DateInput/Wheel.tsx +++ b/packages/core/src/DateInput/Wheel.tsx @@ -493,7 +493,6 @@ export function ScrollWheel({ // there is no way to share — it takes every touch that lands on it. useOwnScrollGesture(scrollerRef, 'all', {isEnabled: isActive}); - // A key commits the row it moves to and scrolls the column there. const moveTo = useCallback( (row: number | null) => { const option = row == null ? undefined : options[row]; From 86819d64f67253981806180114c4c9708326ce2a Mon Sep 17 00:00:00 2001 From: Christopher Chedeau Date: Tue, 3 Nov 2026 12:16:49 -0800 Subject: [PATCH 3/4] docs(DateInput): the Compatibility row names what changes under a finger --- .../components/DateInput/DateInput.spec.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/specs/components/DateInput/DateInput.spec.md b/docs/specs/components/DateInput/DateInput.spec.md index 5c43079eb1402..239d2c5ed23ab 100644 --- a/docs/specs/components/DateInput/DateInput.spec.md +++ b/docs/specs/components/DateInput/DateInput.spec.md @@ -55,15 +55,15 @@ system_specs: ## Contract at a glance -| Area | Contract | -| ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Public contract | None. API shape and defaults are as shipped; every surface follows the input-field `changeAction`, disabled, and busy contracts. | -| Behavior | `presentation` selects the picker surface; an omitted value is `adaptive-native`; `presentation` wins over deprecated `nativePicker`; every committed path emits the same controlled ISO-date value and action feedback. | -| End-user impact | People type or pick one date using a surface suited to the primary pointer. Disabled and busy fields stay focusable; a busy field keeps its ordinary paint with a Spinner while new commits are blocked. | -| Builder impact | None. `changeAction` runs after `onChange` with optimistic busy feedback on every surface. | -| Compatibility/readiness | Records shipped behavior; no API, default, or DOM change. Caller handlers while disabled, fixed field height, the English-only typed parser, a clear control that drops focus, and month paging in the touch calendar are listed under Current-state impact. | -| Review checks | Reject a surface-mapping change, lost constraint, callback-order change, unlabeled field, native `disabled`, disabled paint on a busy-only field, unreachable disabled reason, changed close behavior, or a theming target placed on the wrong owner. | -| Governing rules | `spec:AST-043/FR1–FR5`; `spec:AST-085/FR1–FR6`; `spec:AST-086/FR3, FR4, FR6, FR8`; `family:input-fields/FR1–FR6, FR8–FR11`; `family:overlay-dismissal/FR1–FR7`, including its recorded BottomSheet adoption gap; `spec:AST-020/FR15`; `spec:AST-102/FR4`; the four architecture records linked above. | +| Area | Contract | +| ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Public contract | None. API shape and defaults are as shipped; every surface follows the input-field `changeAction`, disabled, and busy contracts. | +| Behavior | `presentation` selects the picker surface; an omitted value is `adaptive-native`; `presentation` wins over deprecated `nativePicker`; every committed path emits the same controlled ISO-date value and action feedback. | +| End-user impact | People type or pick one date using a surface suited to the primary pointer. Disabled and busy fields stay focusable; a busy field keeps its ordinary paint with a Spinner while new commits are blocked. | +| Builder impact | None. `changeAction` runs after `onChange` with optimistic busy feedback on every surface. | +| Compatibility/readiness | No API or default change. Under a finger, the sheet wheels' markup changes (one strip of flat rows, faded by a layer inside the column), and keys step through the values in time order (FR17). Caller handlers while disabled, fixed field height, the English-only typed parser, a clear control that drops focus, and month paging in the touch calendar are listed under Current-state impact. | +| Review checks | Reject a surface-mapping change, lost constraint, callback-order change, unlabeled field, native `disabled`, disabled paint on a busy-only field, unreachable disabled reason, changed close behavior, or a theming target placed on the wrong owner. | +| Governing rules | `spec:AST-043/FR1–FR5`; `spec:AST-085/FR1–FR6`; `spec:AST-086/FR3, FR4, FR6, FR8`; `family:input-fields/FR1–FR6, FR8–FR11`; `family:overlay-dismissal/FR1–FR7`, including its recorded BottomSheet adoption gap; `spec:AST-020/FR15`; `spec:AST-102/FR4`; the four architecture records linked above. | This table is a review projection; the body below is authoritative. From 0fcb39b5860033689b880fd941007cdf39e83587 Mon Sep 17 00:00:00 2001 From: Christopher Chedeau Date: Tue, 3 Nov 2026 15:40:34 -0800 Subject: [PATCH 4/4] docs(DateInput): the Compatibility class says what changes, as the row does The record's body is authoritative, and its Compatibility class still said no DOM change. It now names the sheet wheels' markup under a finger, and FR17's key order under every presentation, as the row does. --- .../components/DateInput/DateInput.spec.md | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/docs/specs/components/DateInput/DateInput.spec.md b/docs/specs/components/DateInput/DateInput.spec.md index 239d2c5ed23ab..f23b0555437b8 100644 --- a/docs/specs/components/DateInput/DateInput.spec.md +++ b/docs/specs/components/DateInput/DateInput.spec.md @@ -55,15 +55,15 @@ system_specs: ## Contract at a glance -| Area | Contract | -| ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Public contract | None. API shape and defaults are as shipped; every surface follows the input-field `changeAction`, disabled, and busy contracts. | -| Behavior | `presentation` selects the picker surface; an omitted value is `adaptive-native`; `presentation` wins over deprecated `nativePicker`; every committed path emits the same controlled ISO-date value and action feedback. | -| End-user impact | People type or pick one date using a surface suited to the primary pointer. Disabled and busy fields stay focusable; a busy field keeps its ordinary paint with a Spinner while new commits are blocked. | -| Builder impact | None. `changeAction` runs after `onChange` with optimistic busy feedback on every surface. | -| Compatibility/readiness | No API or default change. Under a finger, the sheet wheels' markup changes (one strip of flat rows, faded by a layer inside the column), and keys step through the values in time order (FR17). Caller handlers while disabled, fixed field height, the English-only typed parser, a clear control that drops focus, and month paging in the touch calendar are listed under Current-state impact. | -| Review checks | Reject a surface-mapping change, lost constraint, callback-order change, unlabeled field, native `disabled`, disabled paint on a busy-only field, unreachable disabled reason, changed close behavior, or a theming target placed on the wrong owner. | -| Governing rules | `spec:AST-043/FR1–FR5`; `spec:AST-085/FR1–FR6`; `spec:AST-086/FR3, FR4, FR6, FR8`; `family:input-fields/FR1–FR6, FR8–FR11`; `family:overlay-dismissal/FR1–FR7`, including its recorded BottomSheet adoption gap; `spec:AST-020/FR15`; `spec:AST-102/FR4`; the four architecture records linked above. | +| Area | Contract | +| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| Public contract | None. API shape and defaults are as shipped; every surface follows the input-field `changeAction`, disabled, and busy contracts. | +| Behavior | `presentation` selects the picker surface; an omitted value is `adaptive-native`; `presentation` wins over deprecated `nativePicker`; every committed path emits the same controlled ISO-date value and action feedback. | +| End-user impact | People type or pick one date using a surface suited to the primary pointer. Disabled and busy fields stay focusable; a busy field keeps its ordinary paint with a Spinner while new commits are blocked. | +| Builder impact | None. `changeAction` runs after `onChange` with optimistic busy feedback on every surface. | +| Compatibility/readiness | No API or default change. Under a finger, the sheet wheels' markup changes (one strip of flat rows, faded by a layer inside the column). Under every presentation, keys step through the values in time order (FR17). Caller handlers while disabled, fixed field height, the English-only typed parser, a clear control that drops focus, and month paging in the touch calendar are listed under Current-state impact. | +| Review checks | Reject a surface-mapping change, lost constraint, callback-order change, unlabeled field, native `disabled`, disabled paint on a busy-only field, unreachable disabled reason, changed close behavior, or a theming target placed on the wrong owner. | +| Governing rules | `spec:AST-043/FR1–FR5`; `spec:AST-085/FR1–FR6`; `spec:AST-086/FR3, FR4, FR6, FR8`; `family:input-fields/FR1–FR6, FR8–FR11`; `family:overlay-dismissal/FR1–FR7`, including its recorded BottomSheet adoption gap; `spec:AST-020/FR15`; `spec:AST-102/FR4`; the four architecture records linked above. | This table is a review projection; the body below is authoritative. @@ -76,8 +76,9 @@ browser/OS date picker selected by the public presentation contract. ## Compatibility and migration - Released default preserved: `yes` -- Compatibility class: records shipped behavior; no public prop, default, - export, or DOM change +- Compatibility class: no public prop, default, or export change; under a + finger the sheet wheels' markup changes, and under every presentation keys + step through the values in time order (FR17) - Controlled/uncontrolled behavior: unchanged; DateInput remains controlled by `value`, with `undefined` representing no selected date - Migration decision: `spec:AST-043/DEC-1` and `spec:AST-043/DEC-2` own the