Repository navigation
fix(core): a press's release fades on the theme's exit motion tokens - #9890
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
AI review status for this pull request.
|
abc005a to
9864140
Compare
|
/approve-spec 9864140 |
There was a problem hiding this comment.
Requesting changes: moving the press release onto the theme's exit motion tokens has owner-approved spec direction for this head, but four things need fixing first.
pressConsts.releaseMs, exported from@astryxdesign/core/utils/interactionOverlay.stylexsince 0.7.6, is renamed toreducedMotionReleaseMsin a patch with no compatibility alias or migration, so keep the released member and take the Reduce Motion bound from the existing internalreducedMotionFadeConsts.duration(or ship the rename as[breaking]with a migration).scripts/build-css.test.mjsstill readsreleaseMs:and the deletedutils/rowPressLayer.stylex.ts, so 2 of its 14 tests fail on this head while all pass on the base; these are the two source-build failures the description puts down to a missing install.- The Changeset and the new
spec:AST-094FR5 sentence say a mouse or pen press on a desktop releases at once, but a pen press on a fine-pointer device is painted by the press controller and fades (now on the exit tokens), the mouse wording contradicts FR11 and DEC-21, and the release already followedtempobefore this change, so those sentences need correcting. defaultTokenMsinutils/pressGesture.tsre-implements thevar()resolution thatresolveTokenReferencesalready provides, andreducedMotionReleaseMsrestatesreducedMotionFadeConsts.duration, so call the shared ones instead.
The rest of the change, the stylesheet arms and the controller reading --duration-exit, looks right, and the unit suites pass.
[Automated review]
| * The longest a finger's release may fade under Reduce Motion, in ms: the | ||
| * Reduce Motion cross-fade's bound (`timing.reducedMotionCrossFade`). | ||
| */ | ||
| reducedMotionReleaseMs: 200, |
There was a problem hiding this comment.
pressConsts is public on @astryxdesign/core/utils/interactionOverlay.stylex (api-report.md lists const pressConsts: { releaseMs: number }, shipped since 0.7.6), so renaming releaseMs here breaks a consumer that reads it, in a patch release. Keep releaseMs as released and take the Reduce Motion bound from the internal reducedMotionFadeConsts.duration (motion/reducedMotion.stylex.ts) in the stylesheets and timing.reducedMotionCrossFadeMs in the controller, or ship the rename as [breaking] with a migration.
| @@ -1,27 +0,0 @@ | |||
| // Copyright (c) Meta Platforms, Inc. and affiliates. | |||
There was a problem hiding this comment.
scripts/build-css.test.mjs still opens this file (readLayerConsts) and looks for releaseMs: in interactionOverlay.stylex.ts (readReleaseMs), so 2 of its 14 tests fail on this head and pass on the base. Point both at the token declarations (var(--duration-exit), min(var(--duration-exit),.2s), var(--ease-exit)), as interactionOverlay.cell.test.ts now does.
| '@astryxdesign/core': patch | ||
| --- | ||
|
|
||
| [fix] A press's release now fades on the theme's exit motion tokens, `--duration-exit` and `--ease-exit`, instead of a fixed 200 ms on a curve of its own. This covers every release that fades today: a finger's press, and a mouse, pen or key press on a phone or tablet, on buttons, cards, switches, checkboxes, list and tree rows, menu items and action groups. With the default theme the release is 175 ms and holds its colour a little longer before it goes. A theme that changes `--duration-exit`, `--ease-exit` or its `tempo` now changes the release too. Under Reduce Motion a finger's release still fades for no more than 200 ms. A mouse or pen press on a desktop still releases at once, as before. |
There was a problem hiding this comment.
On a fine-pointer device the press controller paints a pen press and its release fades (200 ms on cubic-bezier(0, 0, 0.2, 1) before, the exit tokens now), so 'A mouse or pen press on a desktop still releases at once' is wrong for a pen. 'or its tempo now changes the release too' also describes what 0.7.7 already did. Suggested: 'A mouse press on a desktop still releases at once; a pen's release there fades on the same tokens.' and 'A theme that changes --duration-exit or --ease-exit now changes the release; tempo still does.'
| decision (2026-11-03); a press that has no release fade (a mouse or pen on | ||
| a fine-pointer device, on the press overlay) releases at once. |
There was a problem hiding this comment.
A pen on a fine-pointer device does have a release fade: the controller paints every press but a mouse's, and the overlay's fading arm is not gated to pointer: coarse. For a mouse, this sentence contradicts FR11's fine-pointer profile ('through the same press controller and release fade as a finger's, in place of CSS :active') and DEC-21, which stay as they are. Drop 'or pen', and reconcile the mouse case with FR11 and DEC-21.
| function defaultTokenMs(token: keyof typeof durationDefaults): number { | ||
| const value: string = durationDefaults[token]; | ||
| const reference = /^var\((--[\w-]+)\)$/u.exec(value); | ||
| return reference == null | ||
| ? Number.parseFloat(value) | ||
| : defaultTokenMs(reference[1] as keyof typeof durationDefaults); | ||
| } |
There was a problem hiding this comment.
This re-implements the var() chase that resolveTokenReferences (theme/tokenReferences.ts) already does over this token map (themeMotion.ts uses it), without its fallback and cycle handling. export const PRESS_FADE_MS = Number.parseFloat(resolveTokenReferences(durationDefaults)['--duration-exit']); keeps one path.
| * The release's curve: the theme's exit easing. The list row's layer (privateAttributes/press.stylex.ts) and Switch's press layers | ||
| * use the same token; interactionOverlay.cell.test.ts fails if they differ. |
There was a problem hiding this comment.
Non-blocking: interactionOverlay.cell.test.ts never renders Switch; Switch.test.tsx holds Switch's curve to this one. Suggested: 'The list row's layer and Switch's press layers use the same token. interactionOverlay.cell.test.ts and Switch.test.tsx hold them to it.'
2aa1f8b to
3df0724
Compare
|
/approve-spec 3df0724 |
There was a problem hiding this comment.
Requesting changes: moving every press release onto the theme's exit motion tokens has owner-approved spec direction for this head, pressConsts.releaseMs keeps its released shape and scripts/build-css.test.mjs passes, but two things remain.
- Miss:
durationMsinutils/pressFeedback.tsre-implements the CSS time parsing thatparseMotionRoleValue('duration', …)inmotion/roles.tsalready provides (SegmentedControl reads theme durations with it), so call the shared parser for both--duration-exitandreducedMotionFadeConsts.durationand delete the copy. - The description's User impact says today's release is "a fixed 200 ms" that "a theme's motion settings do not reach" (the Changeset also says "a fixed 200 ms"), but on the base a theme's
tempoalready scales it, as the description's own Unchanged bullet says, and the commit message says a desktop pen press releases at once while it fades, so reword them, for example "Today: the pressed colour fades over 200 ms, scaled by the theme'stempo, on a curve of its own; the theme's duration and easing tokens do not reach it."
Smaller and non-blocking: the now-unused timing import in interactionOverlay.cell.test.ts can go, and the comment above RELEASE_EASE should name Switch.test.tsx as the test that holds Switch's curve.
[Automated review]
| /** A CSS time in ms (`'175ms'` → 175, `'0.2s'` → 200); NaN for anything else. */ | ||
| function durationMs(value: string): number { | ||
| const match = /^(-?[\d.]+)(ms|s)$/u.exec(value.trim()); | ||
| if (match == null) { | ||
| return Number.NaN; | ||
| } | ||
| const amount = Number(match[1]); | ||
| return match[2] === 's' ? amount * 1000 : amount; | ||
| } |
There was a problem hiding this comment.
Miss: this re-implements parseMotionRoleValue('duration', …) from motion/roles.ts, which SegmentedControl already uses to read a theme duration off computed style, and it already differs (it rejects an exponent the shared parser takes). Suggested:
const exit =
parseMotionRoleValue('duration', getComputedStyle(element).getPropertyValue('--duration-exit')) ??
PRESS_FADE_MS;and the same call for reducedMotionFadeConsts.duration, then delete durationMs.
| * The release's curve: the theme's exit easing. The list row's layer (privateAttributes/press.stylex.ts) and Switch's press layers | ||
| * use the same token; interactionOverlay.cell.test.ts fails if they differ. |
There was a problem hiding this comment.
Non-blocking: interactionOverlay.cell.test.ts never renders Switch; Switch.test.tsx holds Switch's curve. Suggested: "The list row's layer and Switch's press layers use the same token. interactionOverlay.cell.test.ts and Switch.test.tsx hold them to it."
3df0724 to
d92c7ef
Compare
|
/approve-spec d92c7ef |
There was a problem hiding this comment.
Approved pending CI: moving every press release onto the theme's exit motion tokens has owner-approved spec direction for this head, and both earlier blocking points are fixed (the controller now uses the shared parseMotionRoleValue, and the description, Changeset and commit message describe today's release correctly). Build and test CI has not run on this head yet.
Non-blocking, for this or a follow-up:
- The headers of
utils/interactionOverlay.stylex.tsandprivateAttributes/press.stylex.tsstill name the deletedutils/rowPressLayer.stylex.tsand the old clocks, andPressTracker'sreleaseMscomment still promises a 200 ms fade. spec:AST-094FR5 now says every press release fades on the exit tokens on every pointer, while a mouse release on ClickableCard, SelectableCard and Thumbnail still uses--duration-fastand--ease-standard; anImplementation gap:line would keep the record accurate until those follow.
[Automated review]
| A press shows at once. Every press release that fades does so on the | ||
| theme's exit motion tokens (`--duration-exit`, `--ease-exit`), on every | ||
| device and pointer, by the owner's decision (2026-11-03); under Reduce | ||
| Motion, `design:reduced-motion` DR2 applies. |
There was a problem hiding this comment.
Non-blocking: this says every press release fades on the exit tokens on every device and pointer, but a mouse release on ClickableCard, SelectableCard and Thumbnail still transitions on --duration-fast and --ease-standard (for example ClickableCard.tsx lines 124-130). Until those follow, an Implementation gap: line here would keep the record accurate.
1b6a265 to
3b74202
Compare
Every press release that fades now plays `--duration-exit` and `--ease-exit` instead of 200 ms, scaled by the theme's tempo, on a curve of its own: the shared overlay's touch release, the controller's release clock (which action groups read too), Switch's press layers and the list row layer. A desktop pen press, which the controller fades, moves to the tokens too. Under Reduce Motion a finger's release is no longer than 200 ms, as before. A mouse press on a desktop is unchanged.
d92c7ef to
82dd709
Compare
|
/approve-spec 82dd709 |
There was a problem hiding this comment.
Approved pending CI: build and test CI is still running on this head. Moving every press release that fades onto --duration-exit and --ease-exit has owner-approved spec direction for this head, and the stylesheet arms, the controller's clock and the action groups all read that one token. In Chromium the touch row and Button release now runs 175 ms on the exit curve, holding 0.74 of its colour at 67 ms instead of 0.31.
Non-blocking, for this or a follow-up:
- The headers of
utils/interactionOverlay.stylex.tsandprivateAttributes/press.stylex.tsstill name the deletedutils/rowPressLayer.stylex.tsand the old clocks.gestures/PressTracker.tsstill says every surface releases on the 200 ms deselect clock. spec:AST-094FR5 now says every release that fades plays the exit tokens on every pointer. ClickableCard, SelectableCard and Thumbnail still release a desktop mouse press on--duration-fastand--ease-standard, so anImplementation gap:line would keep the record accurate until they follow.
[Automated review]
| * The touch press's measured release clock, kept as a reference value. Press | ||
| * releases now play the theme's `--duration-exit`. |
There was a problem hiding this comment.
Miss, non-blocking: "now" narrates the change. Suggested:
* The touch press's measured release clock, a reference value. A press
* release plays the theme's `--duration-exit`.| A press shows at once. Every press release that fades does so on the | ||
| theme's exit motion tokens (`--duration-exit`, `--ease-exit`), on every | ||
| device and pointer, by the owner's decision (2026-11-03); under Reduce | ||
| Motion, `design:reduced-motion` DR2 applies. |
There was a problem hiding this comment.
Non-blocking: a desktop mouse release on ClickableCard, SelectableCard and Thumbnail still transitions on --duration-fast and --ease-standard (ClickableCard.tsx lines 125-131). Until those follow, an Implementation gap: line here would keep "on every device and pointer" accurate.
PR Analysis Report🔎 Preview ·
|
| Component | Size | LoC | Visual · A11y · RTL |
|---|---|---|---|
| CommandPalette | 🔴 56.2 kB (+2.3 kB) · 12.3 kB CSS (−16 B) | — | — · — · — |
| lab/ChatEmojiPicker | 🔴 68.6 kB (+2.2 kB) · 773 B CSS (0) | — | — · — · — |
| lab/ChatReactionBar | 🔴 69.2 kB (+2.2 kB) · 1.1 kB CSS (0) | — | — · — · — |
184 more components
| Component | Size | LoC | Visual · A11y · RTL |
|---|---|---|---|
| ContextMenuItem | 🔴 23.8 kB (+2.2 kB) · 6.0 kB CSS (−52 B) | — | — · — · — |
| DateRangeInput | 🔴 74.0 kB (+2.2 kB) · 11.2 kB CSS (−32 B) | — | — · — · — |
| MultiSelector | 🔴 75.9 kB (+2.2 kB) · 15.3 kB CSS (−24 B) | — | — · — · — |
| lab/TourStep | 🔴 60.7 kB (+2.2 kB) · 590 B CSS (0) | — | — · — · — |
| Tokenizer | 🔴 72.8 kB (+2.2 kB) · 17.1 kB CSS (−29 B) | — | — · — · — |
| Popover | 🔴 54.8 kB (+2.2 kB) · 12.7 kB CSS (−26 B) | — | — · — · — |
| lab/CommentComposer | 🔴 107.5 kB (+2.1 kB) · 1.9 kB CSS (0) | — | — · — · — |
| Carousel | 🔴 52.4 kB (+2.1 kB) · 9.5 kB CSS (−31 B) | — | — · — · — |
| lab/Schedule | 🔴 63.3 kB (+2.1 kB) · 3.9 kB CSS (0) | — | — · — · — |
| richtext/RichTextEditorToolbar | 🔴 231.3 kB (+2.1 kB) · 454 B CSS (0) | — | — · — · — |
| Selector | 🔴 81.9 kB (+2.1 kB) · 16.1 kB CSS (−51 B) | — | — · — · — |
| lab/TransferListSelector | 🔴 86.0 kB (+2.1 kB) · 1.9 kB CSS (0) | — | — · — · — |
| Table | 🔴 87.5 kB (+2.1 kB) · 16.6 kB CSS (−55 B) | — | — · — · — |
| Pagination | 🔴 90.7 kB (+2.1 kB) · 16.4 kB CSS (−51 B) | — | — · — · — |
| lab/TransferList | 🔴 78.3 kB (+2.0 kB) · 1.7 kB CSS (0) | — | — · — · — |
| TableHeaderCell | 78.8 kB (+2.0 kB) · 16.1 kB CSS (−54 B) | — | — · — · — |
| TableCell | 79.2 kB (+2.0 kB) · 16.3 kB CSS (−55 B) | — | — · — · — |
| ContextMenu | 77.9 kB (+1.9 kB) · 15.9 kB CSS (−49 B) | — | — · — · — |
| DropdownMenu | 76.6 kB (+1.9 kB) · 16.0 kB CSS (−54 B) | — | — · — · — |
| MoreMenu | 76.9 kB (+1.9 kB) · 16.0 kB CSS (−54 B) | — | — · — · — |
| lab/ListInput | 84.1 kB (+1.7 kB) · 1.6 kB CSS (0) | — | — · — · — |
| Markdown | 148.3 kB (+1.6 kB) · 18.8 kB CSS (−56 B) | — | — · — · — |
| ClickableCard | 16.5 kB (+939 B) · 6.9 kB CSS (−38 B) | — | — · — · — |
| SelectableCard | 16.6 kB (+920 B) · 7.2 kB CSS (−38 B) | — | — · — · — |
| CommandPaletteItem | 11.7 kB (+904 B) · 3.6 kB CSS (−28 B) | — | — · — · — |
| SegmentedControlItem | 12.1 kB (+899 B) · 4.0 kB CSS (−33 B) | — | — · — · — |
| Tab | 13.0 kB (+891 B) · 4.2 kB CSS (−31 B) | — | — · — · — |
| TopNavItem | 12.4 kB (+888 B) · 4.0 kB CSS (−34 B) | — | — · — · — |
| DropdownMenuRadioItem | 20.7 kB (+906 B) · 6.3 kB CSS (−53 B) | — | — · — · — |
| DropdownMenuCheckboxItem | 20.7 kB (+904 B) · 6.3 kB CSS (−53 B) | — | — · — · — |
| Token | 27.2 kB (+878 B) · 4.7 kB CSS (−30 B) | — | — · — · — |
| TopNavMegaMenuItem | 12.5 kB (+873 B) · 4.2 kB CSS (−35 B) | — | — · — · — |
| SelectorOption | 18.9 kB (+881 B) · 5.7 kB CSS (−51 B) | — | — · — · — |
| DropdownMenuItem | 19.9 kB (+878 B) · 5.9 kB CSS (−54 B) | — | — · — · — |
| Outline | 23.5 kB (+857 B) · 4.1 kB CSS (−33 B) | — | — · — · — |
| richtext/RichTextEditor | 279.7 kB (+819 B) · 2.6 kB CSS (0) | — | — · — · — |
| Item | 15.5 kB (+865 B) · 5.2 kB CSS (−55 B) | — | — · — · — |
| NumberInput | 56.5 kB (+821 B) · 10.1 kB CSS (−30 B) | — | — · — · — |
| SideNav | 74.9 kB (+818 B) · 10.1 kB CSS (−33 B) | — | — · — · — |
| lab/EmojiPicker | 72.1 kB (+780 B) · 722 B CSS (0) | — | — · — · — |
| CheckboxInput | 50.0 kB (+813 B) · 8.7 kB CSS (−34 B) | — | — · — · — |
| lab/SelectableCardGroup | 51.7 kB (+774 B) · 162 B CSS (0) | — | — · — · — |
| lab/DiffViewer | 72.7 kB (+770 B) · 2.1 kB CSS (0) | — | — · — · — |
| InputGroup | 43.9 kB (+797 B) · 7.3 kB CSS (−30 B) | — | — · — · — |
| ChatComposerInput | 60.8 kB (+797 B) · 10.0 kB CSS (−31 B) | — | — · — · — |
| Step | 29.6 kB (+797 B) · 5.6 kB CSS (−31 B) | — | — · — · — |
| ChatComposerTokenElement | 60.8 kB (+796 B) · 10.0 kB CSS (−31 B) | — | — · — · — |
| Link | 44.1 kB (+794 B) · 6.8 kB CSS (−30 B) | — | — · — · — |
| TopNav | 57.2 kB (+790 B) · 9.6 kB CSS (−32 B) | — | — · — · — |
| Slider | 92.1 kB (+790 B) · 8.3 kB CSS (−36 B) | — | — · — · — |
| Stepper | 48.2 kB (+780 B) · 8.8 kB CSS (−31 B) | — | — · — · — |
| TextInput | 54.3 kB (+781 B) · 9.9 kB CSS (−32 B) | — | — · — · — |
| ChatComposer | 62.8 kB (+779 B) · 10.5 kB CSS (−31 B) | — | — · — · — |
| FileInput | 55.1 kB (+775 B) · 9.6 kB CSS (−32 B) | — | — · — · — |
| CheckboxList | 45.4 kB (+765 B) · 7.5 kB CSS (−29 B) | — | — · — · — |
| ChatLayout | 49.7 kB (+765 B) · 9.2 kB CSS (−32 B) | — | — · — · — |
| Switch · Storybook | 89.7 kB (+775 B) · 9.5 kB CSS (−47 B) | 1,717 (0) | — · — · — |
| richtext/RichTextView | 260.1 kB (+726 B) · 2.3 kB CSS (0) | — | — · — · — |
| TextArea | 49.0 kB (+755 B) · 8.8 kB CSS (−32 B) | — | — · — · — |
| CodeBlock | 60.5 kB (+749 B) · 10.0 kB CSS (−32 B) | — | — · — · — |
| MobileNav | 55.7 kB (+747 B) · 9.3 kB CSS (−33 B) | — | — · — · — |
| Lightbox | 53.8 kB (+747 B) · 9.1 kB CSS (−34 B) | — | — · — · — |
| ComplexSelector | 57.0 kB (+743 B) · 10.4 kB CSS (−31 B) | — | — · — · — |
| SideNavItem | 56.1 kB (+745 B) · 9.8 kB CSS (−33 B) | — | — · — · — |
| Collapsible | 24.6 kB (+737 B) · 4.5 kB CSS (−29 B) | — | — · — · — |
| BaseTypeahead | 56.7 kB (+749 B) · 9.6 kB CSS (−43 B) | — | — · — · — |
| Typeahead | 64.5 kB (+736 B) · 11.2 kB CSS (−32 B) | — | — · — · — |
| AlertDialog | 87.8 kB (+725 B) · 13.1 kB CSS (−23 B) | — | — · — · — |
| SideNavCollapseButton | 47.4 kB (+734 B) · 8.7 kB CSS (−32 B) | — | — · — · — |
| ToggleButton | 47.4 kB (+730 B) · 8.8 kB CSS (−31 B) | — | — · — · — |
| Banner | 48.5 kB (+730 B) · 9.2 kB CSS (−32 B) | — | — · — · — |
| lab/CommentPin | 48.3 kB (+690 B) · 1.2 kB CSS (0) | — | — · — · — |
| ChatLayoutScrollButton | 47.1 kB (+715 B) · 8.9 kB CSS (−31 B) | — | — · — · — |
| Thumbnail | 50.9 kB (+715 B) · 10.2 kB CSS (−32 B) | — | — · — · — |
| Calendar | 55.7 kB (+715 B) · 9.3 kB CSS (−33 B) | — | — · — · — |
| DialogHeader | 49.4 kB (+711 B) · 10.2 kB CSS (−30 B) | — | — · — · — |
| SideNavHeading | 61.0 kB (+714 B) · 10.0 kB CSS (−33 B) | — | — · — · — |
| MobileNavToggle | 46.9 kB (+711 B) · 8.7 kB CSS (−31 B) | — | — · — · — |
| DrawerHeader | 49.6 kB (+708 B) · 11.6 kB CSS (−30 B) | — | — · — · — |
| ChatDictationButton | 47.1 kB (+707 B) · 8.8 kB CSS (−31 B) | — | — · — · — |
| ChatSendButton | 47.0 kB (+707 B) · 8.7 kB CSS (−31 B) | — | — · — · — |
| TopNavMegaMenu | 55.6 kB (+691 B) · 10.6 kB CSS (−20 B) | — | — · — · — |
| AvatarGroupOverflow | 40.1 kB (+700 B) · 5.4 kB CSS (−31 B) | — | — · — · — |
| Button · Storybook | 46.5 kB (+690 B) · 8.7 kB CSS (−31 B) | 628 (0) | — · — · — |
| TopNavMenu | 60.4 kB (+690 B) · 9.9 kB CSS (−33 B) | — | — · — · — |
| IconButton | 46.6 kB (+684 B) · 8.7 kB CSS (−31 B) | — | — · — · — |
| TopNavHeading | 60.4 kB (+684 B) · 9.8 kB CSS (−31 B) | — | — · — · — |
| TableSelectionToolbar | 56.1 kB (+683 B) · 11.8 kB CSS (−32 B) | — | — · — · — |
| TimeInput | 139.1 kB (+674 B) · 14.5 kB CSS (−24 B) | — | — · — · — |
| TreeList | 40.8 kB (+699 B) · 5.3 kB CSS (−52 B) | — | — · — · — |
| TabMenu | 57.7 kB (+675 B) · 9.8 kB CSS (−30 B) | — | — · — · — |
| DateInput | 152.8 kB (+643 B) · 15.5 kB CSS (−24 B) | — | — · — · — |
| BreadcrumbItem | 70.6 kB (+660 B) · 11.2 kB CSS (−58 B) | — | — · — · — |
| AppShell | 74.4 kB (+590 B) · 12.2 kB CSS (−33 B) | — | — · — · — |
| PowerSearch | 245.8 kB (+595 B) · 22.2 kB CSS (−41 B) | — | — · — · — |
| DropdownMenuSubMenu | 59.0 kB (+546 B) · 9.2 kB CSS (−57 B) | — | — · — · — |
| RadioListItem | 53.0 kB (+530 B) · 9.3 kB CSS (−62 B) | — | — · — · — |
| DateTimeInput | 174.8 kB (+487 B) · 17.2 kB CSS (−24 B) | — | — · — · — |
| CheckboxListItem | 55.2 kB (+515 B) · 10.3 kB CSS (−57 B) | — | — · — · — |
| ListItem | 54.5 kB (+510 B) · 10.3 kB CSS (−57 B) | — | — · — · — |
| SegmentedControl | 83.6 kB (+477 B) · 6.7 kB CSS (−31 B) | — | — · — · — |
| Overlay | 21.5 kB (+363 B) · 3.9 kB CSS (−35 B) | — | — · — · — |
| LayerProvider | 64.1 kB (+256 B) · 9.4 kB CSS (−33 B) | — | — · — · — |
| Toast | 60.3 kB (+198 B) · 9.0 kB CSS (−31 B) | — | — · — · — |
| lab/ChatTypingIndicator | 25.0 kB (+162 B) · 612 B CSS (0) | — | — · — · — |
| lab/SVGIcon | 7.8 kB (+159 B) · 764 B CSS (0) | — | — · — · — |
| charts/ChartAxis | 6.9 kB (+157 B) · 112 B CSS (0) | — | — · — · — |
| lab/LogStream | 8.1 kB (+155 B) · 1.6 kB CSS (0) | — | — · — · — |
| lab/ChartDotGLInteractive | 8.5 kB (+154 B) · 20 B CSS (0) | — | — · — · — |
| lab/ChatReasoning | 7.1 kB (+154 B) · 967 B CSS (0) | — | — · — · — |
| lab/CircularProgress | 8.6 kB (+154 B) · 1.1 kB CSS (0) | — | — · — · — |
| lab/ChartDotGL | 7.5 kB (+153 B) · 20 B CSS (0) | — | — · — · — |
| lab/Stat | 4.3 kB (+153 B) · 646 B CSS (0) | — | — · — · — |
| lab/ChartStreamGL | 7.6 kB (+151 B) · 20 B CSS (0) | — | — · — · — |
| lab/ChartHeatmapGL | 8.6 kB (+150 B) · 20 B CSS (0) | — | — · — · — |
| lab/ChatUnreadDivider | 3.7 kB (+148 B) · 348 B CSS (0) | — | — · — · — |
| lab/ThreeDScatterGL | 7.8 kB (+148 B) · 20 B CSS (0) | — | — · — · — |
| BottomSheetSwitcher | 17.4 kB (+174 B) · 3.4 kB CSS (−28 B) | — | — · — · — |
| CommandPaletteList | 16.1 kB (+163 B) · 3.4 kB CSS (−27 B) | — | — · — · — |
| ChatSystemMessage | 7.0 kB (+160 B) · 3.8 kB CSS (−26 B) | — | — · — · — |
| MetadataListItem | 6.7 kB (+161 B) · 3.5 kB CSS (−27 B) | — | — · — · — |
| NavHeadingMenu | 15.5 kB (+162 B) · 3.5 kB CSS (−28 B) | — | — · — · — |
| AvatarStatusDot | 7.3 kB (+161 B) · 3.5 kB CSS (−28 B) | — | — · — · — |
| ButtonGroup | 9.1 kB (+161 B) · 3.4 kB CSS (−28 B) | — | — · — · — |
| Toolbar | 35.0 kB (+166 B) · 7.8 kB CSS (−33 B) | — | — · — · — |
| FormLayout | 6.5 kB (+160 B) · 3.5 kB CSS (−28 B) | — | — · — · — |
| CommandPaletteEmpty | 6.4 kB (+158 B) · 3.5 kB CSS (−27 B) | — | — · — · — |
| ToggleButtonGroup | 6.6 kB (+159 B) · 3.4 kB CSS (−28 B) | — | — · — · — |
| TypeaheadItem | 6.4 kB (+162 B) · 3.5 kB CSS (−31 B) | — | — · — · — |
| Grid | 4.6 kB (+158 B) · 3.8 kB CSS (−28 B) | — | — · — · — |
| Section | 7.7 kB (+156 B) · 6.3 kB CSS (−26 B) | — | — · — · — |
| TableRow | 6.7 kB (+160 B) · 3.9 kB CSS (−30 B) | — | — · — · — |
| Card | 6.7 kB (+155 B) · 6.3 kB CSS (−26 B) | — | — · — · — |
| LayoutPanel | 7.8 kB (+157 B) · 4.9 kB CSS (−28 B) | — | — · — · — |
| TableFooter | 3.5 kB (+157 B) · 3.3 kB CSS (−28 B) | — | — · — · — |
| TopNavMegaMenuFeaturedCard | 7.1 kB (+157 B) · 3.6 kB CSS (−28 B) | — | — · — · — |
| Center | 4.4 kB (+157 B) · 4.6 kB CSS (−29 B) | — | — · — · — |
| Code | 3.7 kB (+153 B) · 3.5 kB CSS (−25 B) | — | — · — · — |
| GridSpan | 3.6 kB (+157 B) · 3.3 kB CSS (−29 B) | — | — · — · — |
| OverflowList | 8.8 kB (+159 B) · 3.6 kB CSS (−31 B) | — | — · — · — |
| TableBody | 3.5 kB (+156 B) · 3.3 kB CSS (−28 B) | — | — · — · — |
| TableHeader | 3.5 kB (+156 B) · 3.3 kB CSS (−28 B) | — | — · — · — |
| ChatMessageBubble | 7.2 kB (+151 B) · 3.7 kB CSS (−24 B) | — | — · — · — |
| ChatMessageMetadata | 19.9 kB (+151 B) · 4.1 kB CSS (−24 B) | — | — · — · — |
| InputGroupText | 6.6 kB (+155 B) · 3.8 kB CSS (−28 B) | — | — · — · — |
| MetadataList | 16.9 kB (+152 B) · 3.7 kB CSS (−25 B) | — | — · — · — |
| SideNavSection | 6.8 kB (+157 B) · 3.7 kB CSS (−30 B) | — | — · — · — |
| Skeleton | 4.0 kB (+153 B) · 3.7 kB CSS (−26 B) | — | — · — · — |
| CommandPaletteFooter | 17.2 kB (+154 B) · 3.7 kB CSS (−28 B) | — | — · — · — |
| NavIcon | 3.7 kB (+154 B) · 3.4 kB CSS (−28 B) | — | — · — · — |
| CommandPaletteGroup | 3.7 kB (+150 B) · 3.4 kB CSS (−25 B) | — | — · — · — |
| DropdownMenuGroup | 6.3 kB (+153 B) · 3.4 kB CSS (−28 B) | — | — · — · — |
| DropdownMenuRadioGroup | 6.3 kB (+155 B) · 3.3 kB CSS (−30 B) | — | — · — · — |
| ResizeHandle | 19.7 kB (+155 B) · 4.3 kB CSS (−30 B) | — | — · — · — |
| AspectRatio | 3.9 kB (+151 B) · 3.6 kB CSS (−28 B) | — | — · — · — |
| CollapsibleGroup | 6.6 kB (+153 B) · 3.3 kB CSS (−30 B) | — | — · — · — |
| ChatComposerDrawer | 17.7 kB (+156 B) · 4.7 kB CSS (−34 B) | — | — · — · — |
| TabList | 35.6 kB (+151 B) · 6.3 kB CSS (−29 B) | — | — · — · — |
| Badge | 4.2 kB (+151 B) · 4.0 kB CSS (−30 B) | — | — · — · — |
| Blockquote | 3.7 kB (+148 B) · 3.5 kB CSS (−27 B) | — | — · — · — |
| ChatTokenizedText | 7.2 kB (+151 B) · 4.0 kB CSS (−30 B) | — | — · — · — |
| Citation | 20.2 kB (+149 B) · 4.4 kB CSS (−29 B) | — | — · — · — |
| ChatMessage | 16.8 kB (+149 B) · 3.7 kB CSS (−30 B) | — | — · — · — |
| Breadcrumbs | 16.1 kB (+145 B) · 3.4 kB CSS (−31 B) | — | — · — · — |
| Kbd | 16.8 kB (+145 B) · 3.6 kB CSS (−31 B) | — | — · — · — |
| Timestamp | 21.4 kB (+115 B) · 8.9 kB CSS (−30 B) | — | — · — · — |
| charts/Chart | 160.9 kB (+60 B) · 494 B CSS (0) | — | — · — · — |
| lab/CodeEditor | 14.1 kB (+36 B) · 743 B CSS (0) | — | — · — · — |
| ProgressBar | 10.9 kB (+41 B) · 6.2 kB CSS (−20 B) | — | — · — · — |
| ChatMessageList | 22.9 kB (+45 B) · 6.8 kB CSS (−30 B) | — | — · — · — |
| CommandPaletteInput | 26.9 kB (+41 B) · 7.4 kB CSS (−29 B) | — | — · — · — |
| ChatToolCalls | 26.0 kB (+37 B) · 8.2 kB CSS (−31 B) | — | — · — · — |
| Timer | 9.6 kB (+33 B) · 5.8 kB CSS (−29 B) | — | — · — · — |
| BottomSheet | 55.1 kB (0) · 7.5 kB CSS (0) | — | — · — · — |
| List | 5.4 kB (0) · 435 B CSS (0) | — | — · — · — |
| StatusDot | 22.9 kB (+1 B) · 5.2 kB CSS (−30 B) | — | — · — · — |
| Avatar | 34.8 kB (−10 B) · 5.4 kB CSS (−30 B) | — | — · — · — |
| Drawer | 40.5 kB (0) · 8.2 kB CSS (−45 B) | — | — · — · — |
| AvatarGroup | 37.5 kB (−32 B) · 5.5 kB CSS (−29 B) | — | — · — · — |
| lab/Comments | 265.5 kB (−84 B) · 2.5 kB CSS (0) | — | — · — · — |
| lab/CommentThread | 260.2 kB (−132 B) · 2.4 kB CSS (0) | — | — · — · — |
| FieldLabel | 37.8 kB (−304 B) · 4.8 kB CSS (−595 B) | — | — · — · — |
| Field | 41.7 kB (−340 B) · 6.3 kB CSS (−590 B) | — | — · — · — |
| RadioList | 43.1 kB (−389 B) · 6.4 kB CSS (−590 B) | — | — · — · — |
Package size: @astryxdesign/core 6.43 MB packed (−1.4 kB); no new dependencies.
API changes: none in this pull request
Committed API report behind this build: cli 8 rows, core 2 rows; run pnpm api:report.
Wide change (shared styles: packages/core/src/theme/themeMotion.ts, packages/core/src/utils/interactionOverlay.stylex.ts, packages/core/src/utils/rowPressLayer.stylex.ts): add ci:run-visual, ci:run-a11y or ci:run-rtl to run them.
Generated by PR Enrichment workflow | View full report
…t curve After #9890 the row's touch release plays the theme's exit tokens, whose curve leaves fastest at its end. The video's 40 ms frames rarely land in the last few milliseconds before Linux WebKit's end-of-animation hand-back, so the "within 1 of rest" check failed most runs. The case now sets a 400 ms exit on the row, reads the clock and curve from the page, and judges rest as the curve's value one recording frame before the end.
…t curve After #9890 the row's touch release plays the theme's exit tokens, whose curve leaves fastest at its end. The video's 40 ms frames rarely land in the last few milliseconds before Linux WebKit's end-of-animation hand-back, so the "within 1 of rest" check failed most runs. The case now sets a 400 ms exit on the row, reads the clock and curve from the page, and judges rest as the curve's value one recording frame before the end.
…t curve After #9890 the row's touch release plays the theme's exit tokens, whose curve leaves fastest at its end. The video's 40 ms frames rarely land in the last few milliseconds before Linux WebKit's end-of-animation hand-back, so the "within 1 of rest" check failed most runs. The case now sets a 400 ms exit on the row, reads the clock and curve from the page, and judges rest as the curve's value one recording frame before the end.
…t curve (#10011) * test(core): the WebKit row release video proof judges rest on the exit curve After #9890 the row's touch release plays the theme's exit tokens, whose curve leaves fastest at its end. The video's 40 ms frames rarely land in the last few milliseconds before Linux WebKit's end-of-animation hand-back, so the "within 1 of rest" check failed most runs. The case now sets a 400 ms exit on the row, reads the clock and curve from the page, and judges rest as the curve's value one recording frame before the end. * Merge branch 'main' into fix/list-row-webkit-video-exit-curve * Merge branch 'main' into fix/list-row-webkit-video-exit-curve * Merge branch 'main' into fix/list-row-webkit-video-exit-curve
User impact
This affects people who press a control on a phone or tablet: buttons, cards, switches, checkboxes, list and tree rows, tabs, navigation items and action groups, every surface that shows the shared pressed colour.
tempo, on a curve of its own. Most of the colour is gone in about 40 ms, so the fade is hard to see. The theme's duration and easing tokens do not reach it.--duration-exitand--ease-exit. With the default theme that is 175 ms on an accelerating curve. The colour stays a little longer, then goes. A theme that changes those tokens, or itstempo, changes the release too.tempostill scales the release, now through--duration-exit.The owner's words, and the code for each
The owner decided (2026-11-03): "my expectation with these kinds of press fade motion is we're using the appropriate motion tokens to do the job however that timing is supposed to be". Specs & Decisions recorded the tokens as
--duration-exitand--ease-exit, on every pointer, for every release that fades today, and no new fades.--duration-exitutils/interactionOverlay.stylex.ts:PRESS_RELEASE_DURATION = durationVars['--duration-exit']. The same token inprivateAttributes/press.stylex.ts(list row layer) andSwitch/Switch.tsx(Switch's press layers).--ease-exitRELEASE_EASE = easeVars['--ease-exit']in the same three files.utils/pressFeedback.ts:pressFadeMsreads the computed--duration-exiton the pressed element. Without a readable value it uses the token's default (PRESS_FADE_MS, 175 ms). Action groups (hooks/actionGroupTracker.ts) read the same function.min(var(--duration-exit), 200ms)in the stylesheet, and the same bound in the controller. The 200 ms is the shared Reduce Motion fade,reducedMotionFadeConsts.duration.:active) is unchanged, so a desktop mouse press is as on main.What changes, by component
Every release that fades: a finger, mouse, pen or key press on a phone or tablet, and a pen press on a desktop (the press controller paints it there). With the default theme the release goes from 200 ms on
cubic-bezier(0, 0, 0.2, 1)to 175 ms oncubic-bezier(0.3, 0, 0.6, 0.6).--duration-exit,--ease-exit--duration-exit,--ease-exit--duration-exit,--ease-exit--duration-exit--duration-exit,--ease-exit:active)Components that animate their own desktop press in CSS are out of scope here. ClickableCard, SelectableCard and Thumbnail move in their own pull request, and the rest follow in the next one.
Records
spec:AST-094FR5: "A press shows at once. Every press release that fades does so on the theme's exit motion tokens (--duration-exit,--ease-exit), on every device and pointer, by the owner's decision (2026-11-03); under Reduce Motion,design:reduced-motionDR2 applies."component:ListFR11 andcomponent:ButtonFR15: the release fades on the theme's exit motion tokens (--duration-exit,--ease-exit).spec:AST-108: Button FR15's release followstempothrough--duration-exit, which references a tempo-scaled duration.timing.press.highlightOutMsandhighlightOutCurvestay in the motion table, marked "reference only, not played".pressConsts.releaseMs, exported from@astryxdesign/core/utils/interactionOverlay.stylex, stays exported with its value, documented as the measured reference value; press releases now play--duration-exit.Writing rubric results
Compatibility
A press's release timing changes on every touch-first device, for a pen press on a desktop, and with any theme that sets
--duration-exitor--ease-exit. Every press release that fades does so on the theme's exit motion tokens, on every device, by the owner's decision (2026-11-03). A mouse press on a desktop is unchanged: its pixels match main. No API change:pressConsts.releaseMsstays exported as a reference value.Verification
Red first: 18 new or changed unit cases fail with the old stylesheet and controller, and pass here. They cover:
--duration-exit, in ms and in s;var(--duration-exit),min(var(--duration-exit), 200ms)andvar(--ease-exit).Suites: the press, overlay, Switch, List, Item, TreeList, action-group and motion-table unit suites pass (2,091 tests), and so does the shipped stylesheet test (
scripts/build-css.test.mjs, 14 of 14). Lint (strict) on the changed files and the core type check are clean.check:knowledgepasses.review:prereports no findings.Since the phone check at 9864140: no behaviour change. The Reduce Motion bound now reads the shared
reducedMotionFadeConsts.duration(the same 200 ms),pressConsts.releaseMsis kept as a reference value, the default clock uses the sharedresolveTokenReferences, the controller parses the token with the sharedparseMotionRoleValue('duration', …), the stylesheet test reads the new clocks, and the changeset, commit message and FR5 wording are corrected.Real Chromium (phone viewport): the ButtonPress, ListRowPress, ListRowPressPaths and Switch interaction specs pass: 66 passed, 1 skipped (a WebKit-only case). The specs now read the release clock and curve from the page's
--duration-exitand--ease-exit. Three of them fail against main's Storybook: the row's release curve, the button's release clock, and Switch following a theme's--duration-exitwith the 200 ms Reduce Motion bound.Phone, before and after (real Chromium, release seeked frame by frame; pressed-colour strength):
The Storybook theme sets
--duration-fast, which--duration-exituses, to 125 ms, so the release there is 125 ms. With core's default tokens it is 175 ms.Desktop, before and after: a mouse hover, held press and release on a List row and a Button: 0 different pixels against main in all six frames.
Card 39 pass carry-over
Cindy passed card 39 at 9864140. Since then the head took review fixes with no behaviour change (the controller now parses the token with the shared
parseMotionRoleValue), and it is now rebased ontomainafter #9758, #9762 and #9765 merged. The range-diff of this pull request's own change, from d92c7ef to this head, is two changeset lines: this changeset credits its author, and the list-row changeset no longer says the row's release keeps "the same clock and curve" (this pull request changes them).Real Chromium, phone touch emulation, the same row ("Sent") and Button, the release seeked to the same frames. The values are pressed-colour strength, the same for the row and the Button. Each side is built from its exact head's source with the same Storybook stories.
Every frame matches. Desktop mouse at this head against
main(with #9765): 0 different pixels in hover, held press, and the frame after the release, for the row and the Button, in four runs.Desktop pen press (fine pointer; the press controller paints it),
mainagainst this head:mainThe pen's release is an existing fade, retimed onto the exit tokens by FR5's every-device, every-pointer decision.
Scope