Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .changeset/list-row-press-compositor.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@
'@astryxdesign/core': patch
---

[fix] A list row's touch press release keeps fading while the page runs script, once the release has started. `ListItem`, `Item` and `TreeList` rows used to freeze mid-fade until the main thread was free, and every frame of the fade repainted the row; the release now runs on the compositor, with the same color, clock and curve as before. Script that runs in the tap's own handlers, before the next frame, still holds the row pressed until it ends. Under a finger, while a row is pressed and through its release, the press paints on the row's `::before` and the row is its own stacking context; at rest nothing of it applies, so a row's own `::before` decoration and its children's stacking are as they were. During a press the layer is the row's `::before`, and any `::before` property a caller sets, from `xstyle` (which `Item` composes after the layer) or from a stylesheet, wins over the layer's: a decoration with its own size and background jumps to the row's corner, and the row shows no pressed fill until the release ends. Decoration that should stay through a press belongs on `::after` or a child element. With a mouse or trackpad the row is unchanged: no layer and no new stacking context.
[fix] A list row's touch press release keeps fading while the page runs script, once the release has started. `ListItem`, `Item` and `TreeList` rows used to freeze mid-fade until the main thread was free, and every frame of the fade repainted the row; the release now runs on the compositor, with the same color as before. Script that runs in the tap's own handlers, before the next frame, still holds the row pressed until it ends. Under a finger, while a row is pressed and through its release, the press paints on the row's `::before` and the row is its own stacking context; at rest nothing of it applies, so a row's own `::before` decoration and its children's stacking are as they were. During a press the layer is the row's `::before`, and any `::before` property a caller sets, from `xstyle` (which `Item` composes after the layer) or from a stylesheet, wins over the layer's: a decoration with its own size and background jumps to the row's corner, and the row shows no pressed fill until the release ends. Decoration that should stay through a press belongs on `::after` or a child element. With a mouse or trackpad the row is unchanged: no layer and no new stacking context.

@vjeux
7 changes: 7 additions & 0 deletions .changeset/press-release-exit-tokens.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@astryxdesign/core': patch
---

[fix] A press's release now fades on the theme's exit motion tokens, `--duration-exit` and `--ease-exit`, instead of 200 ms, scaled by the theme's `tempo`, on a curve of its own. This covers every release that fades: a finger, mouse, pen or key press on a phone or tablet, and a pen press on a desktop, on buttons, cards, switches, checkboxes, list and tree rows, tabs, menu items and action groups. With the default tokens the release is 175 ms and holds its colour a little longer before it goes. A theme that changes `--duration-exit` or `--ease-exit` now changes the release too; a theme's `tempo` still does, through `--duration-exit`. Under Reduce Motion a finger's release still fades for no more than 200 ms. A mouse press on a desktop is unchanged.

@cixzhang
4 changes: 4 additions & 0 deletions docs/specs/AST-094-touch-presentations/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,10 @@ a measured, sourced difference exists.
and MUST keep advancing at display rate through a main-thread block. It MUST
NOT animate layout properties, and MUST NOT step a frame from script when the
compositor can run the curve.
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.
Comment on lines +232 to +235

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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.

Comment on lines +232 to +235

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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.

- **FR6 — Motion is interruptible.** A finger MUST be able to catch any
animation mid-flight at the value on screen. That value and its velocity
MUST come from the curve, not from a layout read. A new target MUST keep the
Expand Down
2 changes: 1 addition & 1 deletion docs/specs/AST-108-theme-motion/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ defineTheme({
`spec:AST-012` FR4 (`reducedMotion` stays root-owned),
`architecture:theme-application` INV1 (`'always'` reaches nested
providers), `architecture:theme-authoring-contract` (the internal motion resolver),
`component:Button` FR15 (its release fade follows `tempo`), the draft `design:motion` DR3, and `spec:AST-090` are amended to
`component:Button` FR15 (its release fade follows `tempo` through `--duration-exit`, which references a tempo-scaled duration), the draft `design:motion` DR3, and `spec:AST-090` are amended to
match.

## Verification
Expand Down
2 changes: 1 addition & 1 deletion docs/specs/components/Button/Button.spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ Consumer migration instructions belong in consumer docs and release notes.
| FR12 | With `tooltip`, the tooltip attaches to the Button element itself (no wrapper element), opens above it, and its content is added to `aria-describedby` after any caller value, unless its trimmed text equals the Button's accessible name as its props decide it (the derived or caller `aria-label`, or `label` when `icon` and `endContent` render nothing or a decorative Icon), as Avatar AR5 does, so a tooltip that repeats the name is not read twice. When the name comes from elsewhere (`aria-labelledby`, or other content in `icon` or `endContent`), the tooltip still describes the Button. IconButton inherits this. | Current source; real-browser accessibility test |
| FR13 | A standalone Button paints the shadow token for its `elevation`. Inside a ButtonGroup it paints no shadow and reflects `data-elevation="none"`, whatever `elevation` says. | `family:buttons` FR8–FR10; current source and tests |
| FR14 | The root carries `astryx-button` and reflects the effective `data-variant`, `data-size`, and `data-elevation`. | `family:buttons` FR10; `architecture:component-theming-surface`; tests |
| FR15 | Hover paints the shared interaction overlay on hover-capable pointers, and a press paints the shared pressed overlay through the press controller: at once under a mouse or a pen (`spec:AST-094` FR11's fine-pointer profile) and on FR11's control profile under a finger, fading out over 200 ms on release at `tempo` 1, scaled by the theme's `tempo` (`spec:AST-108` FR4). Without a reduced-motion preference, a standalone Button also scales to 0.98 on a mouse press, or while the touch press reads on; under reduced motion it MUST NOT scale (`design:reduced-motion` DR1). All of this paint follows the element's exposed disabled state: a Button that is natively disabled or exposes `aria-disabled="true"` (busy-only, disabled with a tooltip, or a caller's own `aria-disabled`) shows no overlay and no press scale, even when it stays operable. Inside a ButtonGroup the press scale does not apply. Button's hover and press transitions are fast, and instant under reduced motion. Implementation gap: the 0.98 press scale also applies under reduced motion; only its transition becomes instant. | Current source and tests; `spec:AST-094` FR11 and DEC-21 for the mouse and pen press |
| FR15 | Hover paints the shared interaction overlay on hover-capable pointers, and a press paints the shared pressed overlay through the press controller: at once under a mouse or a pen (`spec:AST-094` FR11's fine-pointer profile) and on FR11's control profile under a finger, fading out on release on the theme's exit motion tokens (`--duration-exit`, `--ease-exit`; `spec:AST-094` FR5). Without a reduced-motion preference, a standalone Button also scales to 0.98 on a mouse press, or while the touch press reads on; under reduced motion it MUST NOT scale (`design:reduced-motion` DR1). All of this paint follows the element's exposed disabled state: a Button that is natively disabled or exposes `aria-disabled="true"` (busy-only, disabled with a tooltip, or a caller's own `aria-disabled`) shows no overlay and no press scale, even when it stays operable. Inside a ButtonGroup the press scale does not apply. Button's hover and press transitions are fast, and instant under reduced motion. Implementation gap: the 0.98 press scale also applies under reduced motion; only its transition becomes instant. | Current source and tests; `spec:AST-094` FR11 and DEC-21 for the mouse and pen press |
| FR16 | The ghost variant carries the edge-compensation marker so an enclosing container can align it flush with its edge; other variants do not. | Current source; edge-compensation tests |
| FR17 | `ref`, `className`, `style`, `xstyle`, and other DOM props reach the root element. A caller's own `aria-busy` and `aria-disabled` are kept beside Button's own (either one sets the attribute); a caller `aria-disabled` changes exposure and paint only and does not make the Button inert. The tooltip trigger shares the caller's `ref`, which stays attached across rerenders. | Current source and tests |

Expand Down
Loading
Loading