Repository navigation
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
AI review status for this pull request.
|
PR Analysis Report📚 Storybook Preview🧪 Sandbox PreviewModified ComponentsButton (@astryxdesign/core) · View in Storybook
CheckboxInput (@astryxdesign/core) · View in Storybook
ClickableCard (@astryxdesign/core) · View in Storybook
Item (@astryxdesign/core) · View in Storybook
SelectableCard (@astryxdesign/core) · View in Storybook
Thumbnail (@astryxdesign/core) · View in Storybook
TreeList (@astryxdesign/core) · View in Storybook
Component Size
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.
|
| Component | JavaScript | CSS |
|---|---|---|
| core/AlertDialog | +3.2KB |
+805B |
| core/AppShell | +2.9KB |
+811B |
| core/AvatarGroupOverflow | +2.6KB |
+234B |
| core/Banner | +3.2KB |
+807B |
| core/BaseTypeahead | +3.1KB |
+816B |
| core/BreadcrumbItem | +3.5KB |
+1.0KB |
| core/Button | +3.2KB |
+806B |
| core/Calendar | +3.2KB |
+801B |
| core/Carousel | +3.2KB |
+805B |
| core/ChatComposer | +3.2KB |
+805B |
| core/ChatComposerInput | +3.2KB |
+808B |
| core/ChatComposerTokenElement | +3.2KB |
+808B |
| core/ChatDictationButton | +3.2KB |
+805B |
| core/ChatLayout | +3.2KB |
+803B |
| core/ChatLayoutScrollButton | +3.2KB |
+805B |
| core/ChatSendButton | +3.2KB |
+804B |
| core/CheckboxInput | +2.5KB |
+468B |
| core/CheckboxListItem | +3.5KB |
+977B |
| core/ClickableCard | +3.0KB |
+813B |
| core/CodeBlock | +3.1KB |
+819B |
| core/Collapsible | +2.5KB |
+260B |
| core/CommandPalette | +2.7KB |
+236B |
| core/CommandPaletteItem | +2.5KB |
+264B |
| core/ComplexSelector | +3.2KB |
+799B |
| core/ContextMenu | +3.8KB |
+1.4KB |
| core/ContextMenuItem | +3.1KB |
+805B |
| core/DateInput | +3.3KB |
+807B |
| core/DateRangeInput | +3.3KB |
+794B |
| core/DateTimeInput | +3.3KB |
+818B |
| core/DialogHeader | +3.2KB |
+807B |
| core/DrawerHeader | +3.2KB |
+806B |
| core/DropdownMenu | +3.5KB |
+1.4KB |
| core/DropdownMenuCheckboxItem | +3.0KB |
+637B |
| core/DropdownMenuItem | +3.0KB |
+657B |
| core/DropdownMenuRadioItem | +3.0KB |
+637B |
| core/DropdownMenuSubMenu | +3.0KB |
+631B |
| core/FileInput | +3.2KB |
+795B |
| core/IconButton | +3.2KB |
+806B |
| core/Item | +3.3KB |
+659B |
| core/LayerProvider | +3.1KB |
+812B |
| core/Lightbox | +3.2KB |
+808B |
| core/Link | +2.4KB |
+237B |
| core/ListItem | +3.3KB |
+977B |
| core/Markdown | +3.7KB |
+1.4KB |
| core/MobileNav | +3.1KB |
+809B |
| core/MobileNavToggle | +3.2KB |
+803B |
| core/MoreMenu | +3.4KB |
+1.4KB |
| core/MultiSelector | +3.3KB |
+1015B |
| core/NumberInput | +3.2KB |
+805B |
| core/Outline | +2.5KB |
+261B |
| core/Pagination | +3.3KB |
+1.0KB |
| core/Popover | +2.9KB |
+797B |
| core/PowerSearch | +3.4KB |
+1.0KB |
| core/RadioListItem | +3.2KB |
+803B |
| core/SegmentedControlItem | +2.6KB |
+257B |
| core/SelectableCard | +3.0KB |
+813B |
| core/Selector | +3.4KB |
+1.0KB |
| core/SelectorOption | +3.2KB |
+656B |
| core/SideNav | +3.2KB |
+809B |
| core/SideNavCollapseButton | +3.2KB |
+802B |
| core/SideNavHeading | +3.1KB |
+809B |
| core/SideNavItem | +3.2KB |
+812B |
| core/Step | +2.5KB |
+252B |
| core/Stepper | +3.2KB |
+803B |
| core/Switch | +2.6KB |
+234B |
| core/Tab | +2.6KB |
+253B |
| core/TabMenu | +3.2KB |
+801B |
| core/Table | +3.7KB |
+1.4KB |
| core/TableCell | +3.7KB |
+1.4KB |
| core/TableHeaderCell | +3.8KB |
+1.4KB |
| core/TableSelectionToolbar | +3.2KB |
+812B |
| core/TextInput | +3.2KB |
+808B |
| core/Thumbnail | +3.2KB |
+800B |
| core/TimeInput | +3.2KB |
+799B |
| core/Toast | +3.2KB |
+806B |
| core/ToggleButton | +3.1KB |
+806B |
| core/Token | +2.5KB |
+262B |
| core/Tokenizer | +2.9KB |
+804B |
| core/TopNav | +3.3KB |
+819B |
| core/TopNavHeading | +3.1KB |
+816B |
| core/TopNavItem | +2.6KB |
+261B |
| core/TopNavMegaMenu | +3.1KB |
+810B |
| core/TopNavMegaMenuItem | +2.6KB |
+261B |
| core/TopNavMenu | +3.1KB |
+812B |
| core/TreeList | +3.1KB |
+657B |
| core/Typeahead | +3.2KB |
+800B |
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 | 36.0KB | +1.4KB |
Accessibility Audit
Status: 8 accessibility violation(s) found — 8 serious.
Button pattern - 1 issue(s)
- 🟠 serious: Ensure the contrast between foreground and background colors meets WCAG 2 AA minimum contrast ratio thresholds
- Rule:
color-contrast· Affects 1/10 stories · Learn more - WCAG: 1.4.3 (Level AA)
- Rule:
Checkbox pattern - 1 issue(s)
- 🟠 serious: Ensure the contrast between foreground and background colors meets WCAG 2 AA minimum contrast ratio thresholds
- Rule:
color-contrast· Affects 2/28 stories · Learn more - WCAG: 1.4.3 (Level AA)
- Rule:
CheckboxList - 1 issue(s)
- 🟠 serious: Ensure the contrast between foreground and background colors meets WCAG 2 AA minimum contrast ratio thresholds
- Rule:
color-contrast· Affects 3/23 stories · Learn more - WCAG: 1.4.3 (Level AA)
- Rule:
ClickableCard - 1 issue(s)
- 🟠 serious: Ensure the contrast between foreground and background colors meets WCAG 2 AA minimum contrast ratio thresholds
- Rule:
color-contrast· Affects 1/5 stories · Learn more - WCAG: 1.4.3 (Level AA)
- Rule:
Radio group pattern - 1 issue(s)
- 🟠 serious: Ensure the contrast between foreground and background colors meets WCAG 2 AA minimum contrast ratio thresholds
- Rule:
color-contrast· Affects 3/11 stories · Learn more - WCAG: 1.4.3 (Level AA)
- Rule:
RadioList - 1 issue(s)
- 🟠 serious: Ensure the contrast between foreground and background colors meets WCAG 2 AA minimum contrast ratio thresholds
- Rule:
color-contrast· Affects 3/16 stories · Learn more - WCAG: 1.4.3 (Level AA)
- Rule:
SelectableCard - 1 issue(s)
- 🟠 serious: Ensure the contrast between foreground and background colors meets WCAG 2 AA minimum contrast ratio thresholds
- Rule:
color-contrast· Affects 1/4 stories · Learn more - WCAG: 1.4.3 (Level AA)
- Rule:
Thumbnail - 1 issue(s)
- 🟠 serious: Ensure the contrast between foreground and background colors meets WCAG 2 AA minimum contrast ratio thresholds
- Rule:
color-contrast· Affects 1/12 stories · Learn more - WCAG: 1.4.3 (Level AA)
- Rule:
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
There was a problem hiding this comment.
Approval waits on an owner decision about draft spec:AST-094. Its FR1–FR23 and DEC-1/DEC-2 are new requirements, it says it amends the current design:user-states DR1/DR3, family:buttons, architecture:theme-tokens and spec:AST-025 FR14 and extends architecture:interaction-modality, and holdSelection, divider hiding and the ./utils/pressLayer.stylex subpath still need a governing spec identified. An engineering owner records that decision by commenting /approve-spec <full head SHA> on this pull request (the author may do so as an engineering owner), and the amended current records need updating for the intended changes. Independently of that decision:
- Land the stacked predecessors first: this diff carries 30,836 authored lines across 36 commits for an 861-line fix.
- Correct
.changeset/gesture-press-tracking.mdand.changeset/gesture-host.md, which say no component uses code that everyusePressFeedbacksurface now runs. - Name the external system in the
useGestureRecognizer.tsEffect that callssetEnabled, which can run the consumer'sonGesture.
[Automated review]
| '@astryxdesign/core': patch | ||
| --- | ||
|
|
||
| [chore] Internal: UIControl press tracking with control and cell profiles, the shared press paint, delegated press tracking and the gesture hooks. Not exported from the package entry point; no component uses them yet. |
There was a problem hiding this comment.
At this head usePressFeedback installs usePressAttribute, which builds on the gesture host and PressTracker (gestures/delegatedPress.ts:135, :177), so every Astryx pressable uses this code; "no component uses them yet" no longer holds.
| '@astryxdesign/core': patch | ||
| --- | ||
|
|
||
| [chore] Internal: the gesture system's DOM host (claims, click suppression, the browser's takeover, the pointer behind a contact). Not exported from the package entry point; no component uses it yet. |
There was a problem hiding this comment.
Every usePressFeedback surface now mounts the gesture host through the delegated press tracking, so "no component uses it yet" misstates the release.
| }, [ref, recognizer, manageStyle]); | ||
|
|
||
| useIsomorphicLayoutEffect(() => { | ||
| recognizer.setEnabled(isEnabled); |
There was a problem hiding this comment.
setEnabled(false) on an active recognizer cancels it and runs the consumer's onGesture from inside this layout Effect (GestureRecognizer.ts:548-552, :582-585). Per architecture:react-component-runtime INV3, name the recognizer as the external system this Effect synchronizes, or cancel from the code path that disables it.
| @@ -118,3 +118,15 @@ export {isImeKeyEvent} from './ime'; | |||
| // that paints and composes one of these on it; the controller, its attributes | |||
| // and its clocks are internals. | |||
| export {interactionOverlayStyles} from './interactionOverlay.stylex'; | |||
|
|
|||
| // A list row kept selected while a navigation covers it, deselected as the | |||
| // navigation pops (UIKit's push-from-a-row). Exported for @astryxdesign/lab's | |||
There was a problem hiding this comment.
No package in the repository calls holdSelection at this head, so the comment's "which calls it" describes a caller that does not exist yet.
There was a problem hiding this comment.
Approval waits on an engineering owner's decision on draft spec:AST-094 (FR1-FR23, DEC-1, DEC-2), which the press profiles, gesture host, adaptive boundary and new public exports here rest on; an engineering owner records it by commenting /approve-spec b7a50775c707326c4b854849d044ae84da8ad700 on this pull request, and the author may do so as an engineering owner. Whatever that decision, two changes are needed:
- Land #8077, #8118 and #8219 separately so this pull request carries only its 945-line indicator fix instead of 30,920 authored lines.
- Correct the gesture-host and press-tracking changesets (every pressable now runs that code), the
holdSelectionexport comment and FR11's cell delay, add a release note for./utils/pressLayer.stylex, and give the Effect atgestures/useGestureRecognizer.ts:81-83the comment naming the system it synchronizes.
[Automated review]
| '@astryxdesign/core': patch | ||
| --- | ||
|
|
||
| [chore] Internal: the gesture system's DOM host (claims, click suppression, the browser's takeover, the pointer behind a contact). Not exported from the package entry point; no component uses it yet. |
There was a problem hiding this comment.
Every pressable now runs the gesture host: usePressFeedback calls usePressAttribute, which builds on getGestureHost (gestures/delegatedPress.ts:135). "No component uses it yet" tells consumers to expect no runtime change.
| '@astryxdesign/core': patch | ||
| --- | ||
|
|
||
| [chore] Internal: UIControl press tracking with control and cell profiles, the shared press paint, delegated press tracking and the gesture hooks. Not exported from the package entry point; no component uses them yet. |
There was a problem hiding this comment.
installPressAttribute creates a PressTracker for every touch on a pressable (gestures/delegatedPress.ts:177), so "no component uses them yet" is not what ships.
| }, [ref, recognizer, manageStyle]); | ||
|
|
||
| useIsomorphicLayoutEffect(() => { | ||
| recognizer.setEnabled(isEnabled); |
There was a problem hiding this comment.
Turning isEnabled off mid-gesture cancels the recognizer here and runs the consumer's onGesture inside this layout Effect. architecture:react-component-runtime INV3 asks the Effect to name the external system it synchronizes; a one-line comment naming the gesture recognizer would do.
| @@ -118,3 +118,15 @@ export {isImeKeyEvent} from './ime'; | |||
| // that paints and composes one of these on it; the controller, its attributes | |||
| // and its clocks are internals. | |||
| export {interactionOverlayStyles} from './interactionOverlay.stylex'; | |||
|
|
|||
| // A list row kept selected while a navigation covers it, deselected as the | |||
| // navigation pops (UIKit's push-from-a-row). Exported for @astryxdesign/lab's | |||
There was a problem hiding this comment.
Nothing in packages/lab calls holdSelection at this head; the comment says it does.
| views, keeps tracking within its expanded bounds (70 pt), turns its highlight | ||
| off and on as the finger drags out and back, and activates on a lift inside. | ||
| A cell (a table or collection cell) highlights 150 ms after touch-down inside | ||
| a scroll view and drops the highlight when the scroll wins. The 150 ms delay |
There was a problem hiding this comment.
The cell profile waits 150 ms wherever the row sits (gestures/PressTracker.ts:129, highlightDelay: 'always'), and .changeset/press-control-profile.md promises the same, so this sentence describes a press no list row has.
b7a5077 to
34a91bb
Compare
|
Posted by Cindy's agent on her behalf. Parts of this PR re-implement behavior that already exists, or is being specified, as a shared part of core. Converging keeps one implementation per behavior:
This is about where the behavior lives, not about the PR's intent. |
|
Agreed: one implementation.
|
There was a problem hiding this comment.
Approval waits on an engineering owner's decision on draft spec:AST-094 (FR1-FR23, DEC-1, DEC-2), which the press profiles, gesture host and new public exports here rest on; an engineering owner records it by commenting /approve-spec <full head SHA> on this pull request, and the author may do so as an engineering owner. Whatever that decision, these changes are needed:
- Land #8077, #8118 and #8219 separately so this pull request carries only its 1,038-line indicator fix instead of 33,015 authored lines.
- Give Button and card press releases the 200 ms Reduce Motion cap the checkbox now has (
design:reduced-motionDR2), and dropholdSelection'sreducedMotion: 'keep'(DR4). - Resolve the adaptive compact rule from
spec:AST-012FR9's definition (below the effectivemdwidth or under 500 px tall) instead of a 744 px width of its own. - Correct the gesture-host and press-tracking changesets, the
holdSelectionexport comment and FR11's cell delay, and name the external system in the Effect atgestures/useGestureRecognizer.ts:81-83.
[Automated review]
| default: null, | ||
| [ENABLED]: {default: null, [PRESSED_FADING]: release}, | ||
| }, | ||
| animationDuration: {default: null, [PRESSED_FADING]: CONTROL_RELEASE}, |
There was a problem hiding this comment.
Under Reduce Motion with a coarse pointer this release still runs 250 ms: a probe at this head reads 250 ms on Button's ::before and on ClickableCard's ::after (controlOver, :222), where the base ran 200 ms. design:reduced-motion DR2 caps it at an opacity change of 200 ms or less; the @media (prefers-reduced-motion: reduce) arm CheckboxInput.tsx:158 adds would fix both layers.
| export const TOUCH_QUERY = '(pointer: coarse)'; | ||
|
|
||
| /** The viewport is compact: layout behaviours. */ | ||
| export const COMPACT_QUERY = '(width < 744px), (height < 500px)'; |
There was a problem hiding this comment.
spec:AST-012 FR9 (current) defines compact as a width below the effective md point or a height below 500, and says the web touch presentation resolver keeps "no size numbers of its own" (architecture:public-component-api INV17 agrees). With 744 px, a coarse screen 744-767 px wide loses the bottom sheet FR9 gives it, and a theme's md override is ignored.
| ? {} | ||
| : {velocity: {opacity: -timing.velocity}}), | ||
| }, | ||
| {at, reducedMotion: 'keep'}, |
There was a problem hiding this comment.
reducedMotion: 'keep' lets a caller's spring (and, at :293, the 350 ms in-transition deselect) run unchanged under Reduce Motion. holdSelection is public, so this is the opt-out design:reduced-motion DR4 rules out; under reduced motion the deselect should be an opacity change of 200 ms or less on a coarse pointer (DR2) and immediate otherwise (DR3).
| '@astryxdesign/core': patch | ||
| --- | ||
|
|
||
| [chore] Internal: the gesture system's DOM host (claims, click suppression, the browser's takeover, the pointer behind a contact). Not exported from the package entry point; no component uses it yet. |
There was a problem hiding this comment.
Every pressable now runs the gesture host: usePressFeedback calls usePressAttribute (hooks/usePressFeedback.ts:82), which builds on getGestureHost (gestures/delegatedPress.ts:135). "No component uses it yet" tells consumers to expect no runtime change.
| '@astryxdesign/core': patch | ||
| --- | ||
|
|
||
| [chore] Internal: UIControl press tracking with control and cell profiles, the shared press paint, delegated press tracking and the gesture hooks. Not exported from the package entry point; no component uses them yet. |
There was a problem hiding this comment.
installPressAttribute creates a PressTracker for every touch on a pressable (gestures/delegatedPress.ts:177), so "no component uses them yet" is not what ships.
| // A list row kept selected while a navigation covers it, deselected as the | ||
| // navigation pops (UIKit's push-from-a-row). Exported for @astryxdesign/lab's | ||
| // navigation stack, which calls it with the activation that pushed. |
There was a problem hiding this comment.
No package in the repository calls holdSelection at this head, so "which calls it" describes a caller that does not exist yet.
| }, [ref, recognizer, manageStyle]); | ||
|
|
||
| useIsomorphicLayoutEffect(() => { | ||
| recognizer.setEnabled(isEnabled); |
There was a problem hiding this comment.
Turning isEnabled off mid-gesture cancels the recognizer here (GestureRecognizer.ts:548-553) and runs the consumer's onGesture inside this layout Effect. architecture:react-component-runtime INV3 asks the Effect to name the external system it synchronizes; a one-line comment naming the gesture recognizer would do, or cancel from the code path that disables it.
| A cell (a table or collection cell) highlights 150 ms after touch-down inside | ||
| a scroll view and drops the highlight when the scroll wins. The 150 ms delay | ||
| applies only inside scroll views. **Proposed; pending owner review:** this |
There was a problem hiding this comment.
The cell profile waits 150 ms wherever the row sits (gestures/PressTracker.ts:129, highlightDelay: 'always'), and .changeset/press-control-profile.md promises the same, so this sentence describes a press no list row has.
34a91bb to
e0b28b4
Compare
There was a problem hiding this comment.
Approval waits on an engineering owner's decision on draft spec:AST-094 (FR1–FR23, DEC-1, DEC-2), which the press profiles, gesture host and new public exports here rest on; an engineering owner records it by commenting /approve-spec <full head SHA> on this pull request, and the author may do so as an engineering owner. Whatever that decision, these changes are needed:
- Land #8077, #8118 and #8219 separately so this pull request carries only its 1,101-line indicator fix instead of 33,172 authored lines.
- Give Button and card press releases the 200 ms Reduce Motion cap the checkbox now has (
design:reduced-motionDR2), and stopholdSelectionplaying its 350 ms deselect or a caller's spring under Reduce Motion (DR4). - Resolve the adaptive compact rule from
spec:AST-012FR9's definition (below the effectivemdwidth or under 500 px tall) instead of a 744 px width of its own. - Keep Button's press off the
::beforecallers style throughxstyle, or ship that change as[breaking]with its migration (spec:AST-017FR7). - Correct the gesture-host, press-tracking, browser-negotiation and constants-table changesets, the
holdSelectionexport comment and draft FR11's cell delay, and name the external system in the Effect atgestures/useGestureRecognizer.ts:81-83.
[Automated review]
| default: null, | ||
| [ENABLED]: {default: null, [PRESSED_FADING]: release}, | ||
| }, | ||
| animationDuration: {default: null, [PRESSED_FADING]: CONTROL_RELEASE}, |
There was a problem hiding this comment.
Under Reduce Motion with a coarse pointer this release still runs 250 ms on Button's ::before (and controlOver at :222 on the cards), where main ran 200 ms. design:reduced-motion DR2 caps it at an opacity change of 200 ms or less; the checkbox already carries that arm (CheckboxInput.tsx:158).
| export const TOUCH_QUERY = '(pointer: coarse)'; | ||
|
|
||
| /** The viewport is compact: layout behaviours. */ | ||
| export const COMPACT_QUERY = '(width < 744px), (height < 500px)'; |
There was a problem hiding this comment.
spec:AST-012 FR9 (current) defines compact as a width below the effective md point or a height below 500, and says the web touch presentation resolver keeps "no size numbers of its own" (architecture:public-component-api INV17 agrees). With 744 px, a coarse screen 744–767 px wide loses the bottom sheet and, as before, a theme that overrides md is ignored.
| ? {} | ||
| : {velocity: {opacity: -timing.velocity}}), | ||
| }, | ||
| {at, reducedMotion: 'keep'}, |
There was a problem hiding this comment.
reducedMotion: 'keep' (here and at :293) plays a caller's spring or the 350 ms deselect unchanged under Reduce Motion, and the animator substitutes only travel anyway, so an opacity deselect is never shortened. holdSelection is public, so this is the opt-out design:reduced-motion DR4 rules out; under reduced motion the deselect should be an opacity change of 200 ms or less on a coarse pointer and immediate otherwise.
| interactionOverlayStyles.backgroundImage, | ||
| // The press the controller writes, on a layer under the label whose | ||
| // release runs on the compositor (UIButton's highlight release). | ||
| pressLayerStyles.control, |
There was a problem hiding this comment.
This layer claims Button's root ::before (opacity: 0 at rest, z-index: -1, isolation: isolate), so a ::before a caller styles through xstyle disappears, and the changeset tells callers to change their source. Under spec:AST-017 FR1/FR7 that is an incompatibility, tagged [breaking] with its migration, not a patch [fix]; painting the press on an element Button owns avoids it.
| '@astryxdesign/core': patch | ||
| --- | ||
|
|
||
| [chore] Internal: the gesture system's DOM host (claims, click suppression, the browser's takeover, the pointer behind a contact). Not exported from the package entry point; no component uses it yet. |
There was a problem hiding this comment.
Every pressable now runs the gesture host: usePressFeedback calls usePressAttribute (hooks/usePressFeedback.ts:82), which builds on getGestureHost (gestures/delegatedPress.ts:135). "No component uses it yet" tells consumers to expect no runtime change.
| '@astryxdesign/core': patch | ||
| --- | ||
|
|
||
| [chore] Internal: UIControl press tracking with control and cell profiles, the shared press paint, delegated press tracking and the gesture hooks. Not exported from the package entry point; no component uses them yet. |
There was a problem hiding this comment.
installPressAttribute creates a PressTracker for every touch on a pressable (gestures/delegatedPress.ts:177), so "no component uses them yet" is not what ships.
| '@astryxdesign/core': patch | ||
| --- | ||
|
|
||
| [chore] Internal: the UIKit constants table for touch presentations (springs, scroll physics, timing curves and navigation values), each value with its source and a checked list of the simulator measurements still owed. Not exported from the package entry point; no component reads it yet. |
There was a problem hiding this comment.
PressTracker, which every pressable runs, reads this table (gestures/PressTracker.ts:125, :130-132), so "no component reads it yet" is not what ships.
|
|
||
| // A list row kept selected while a navigation covers it, deselected as the | ||
| // navigation pops (UIKit's push-from-a-row). Exported for @astryxdesign/lab's | ||
| // navigation stack, which calls it with the activation that pushed. |
There was a problem hiding this comment.
No package in the repository calls holdSelection at this head (only a Storybook story does), so "which calls it" describes a caller that does not exist yet.
| }, [ref, recognizer, manageStyle]); | ||
|
|
||
| useIsomorphicLayoutEffect(() => { | ||
| recognizer.setEnabled(isEnabled); |
There was a problem hiding this comment.
Turning isEnabled off mid-gesture cancels the recognizer here (GestureRecognizer.ts:548-552) and runs the consumer's onGesture inside this layout Effect. architecture:react-component-runtime INV3 asks the Effect to name the external system it synchronizes; a one-line comment naming the gesture recognizer would do.
| off and on as the finger drags out and back, and activates on a lift inside. | ||
| A cell (a table or collection cell) highlights 150 ms after touch-down inside | ||
| a scroll view and drops the highlight when the scroll wins. The 150 ms delay | ||
| applies only inside scroll views. **Proposed; pending owner review:** this |
There was a problem hiding this comment.
The cell profile waits 150 ms wherever the row sits (gestures/PressTracker.ts:129, highlightDelay: 'always'), and .changeset/press-control-profile.md promises the same, so this sentence describes a press no list row has.
e0b28b4 to
dc3093e
Compare
|
Pushed in dc3093e. The release now reads the shared clocks ( |
…tion constant From review on meta#8890: spec:AST-094 FR4 lists the 200 ms Reduce Motion cap among the touch motion roles and says a component never plays a literal. The release capped its clock at a literal 200 in pressFeedback.ts (REDUCED_MOTION_MAX_MS) and at a literal 200ms in the overlay's reduced release duration. Both now read the shared constant: the controller timing.reducedMotionCrossFadeMs, the stylesheet reducedMotionFadeConsts.duration. Both are 200 ms, so nothing on screen changes; the built stylesheet's cap is still 200 ms. Red first: a unit case that stubs timing.reducedMotionCrossFadeMs at 120 ms found the release still fading at 120 ms on the change before this one; it now ends there.
…Motion constant too The row press layer capped its release under reduced motion at a literal 200ms (CELL_RELEASE_REDUCED), the same cap the review on meta#8890 asked the overlay to read from the shared constant (spec:AST-094 FR4: the 200 ms Reduce Motion cap is a role, never a literal). It now reads reducedMotionFadeConsts.duration, as the overlay does; both are 200 ms, and the built stylesheet's cap is still 200 ms. A unit case holds every reduced-motion cap in the layer and the overlay to the constant. Red first: it found the layer's literal on the change before this one.
…ame window Both 'a dip held before its first frame lands once the hold is …' proofs waited for no running animation, then one frame and a timeout, assuming the next step starts within a frame. The dip lands on a timer due on the call's own clock, though, and the proofs passed only because the evaluate round trips covered the gap: in WebKit on a loaded box the window closed first and the proof read the element mid-dip (opacity 0, translate none; reproduced 1 in 4 with the round trips removed). They now wait on the call's own finished, then on the browser's own animations still running or waiting for their first frame, and read nothing from the animator.
…ment at once and settles then A Reduce Motion dip whose landing runs more than its whole fade back in late (a hold released long after the dip was due) played a 1 ms opacity animation, which WebKit keeps pending until its next frame, and settled the call on a 1 ms timer: the call's finished could resolve while the element still showed opacity 0. The animator's notes say the element appears at once there. Fails now: the landing plays one opacity animation and the call has not settled.
…or the Space key's 200 ms, a list row's 200 ms Measures each release on a touch-first device (component:Button FR15): the release animation's own duration, read when `fading` is written; `fading` lasting at least that clock; and `fading` going within a few frames of the animation's end. Frames, not ms, bound the end, so a loaded machine cannot fail it. Passes in Chromium and WebKit on this head, before the fold onto meta#8117's new head, and is kept through it as the proof that the fold changes no timing.
…#8890, meta#8360 045f666) into the press release The release's 250 ms for a finger on a control stays where it was, in the control press layer's CSS, and a mouse's or the Space key's press keeps 200 ms through its input marker. The controller's own profile clock goes with the profiles meta#8117 dropped: the release-end code from meta#8890 waits for the running release animation, so a finger's release still ends at 250 ms. This branch's own copy of that code gives way to meta#8890's.
…element at once and settles A Reduce Motion landing that runs at least the whole fade back in late had nothing left of the cross-fade, but still played a 1 ms opacity animation and settled the call 1 ms later. WebKit keeps that animation pending until its next frame, so the call's finished could resolve with the element still at opacity 0. Such a landing now writes the end opacity at once (handing a borrowed one back to the stylesheet), plays nothing, and settles the call then, as the animator's notes say.
…ot the timing table From review (an inline comment on meta#8077 about this line): the press controller, which every pressable component loads, imported the whole UIKit timing table for one value, its reduced-motion cap, while architecture:component-style-authoring INV6 ships a component only the rows of a shared table it uses. It now reads the fade's own export, reducedMotionCrossFade.durationMs, as the animator does; the value is the same 200 ms. Red first: a bundle of pressFeedback.ts carried the table's other rows (stateChangeMs, buttonReleaseMs) on the change before this one; it now carries the fade alone.
…th dividers drawn From review on meta#8077: component:List FR11's verification asks for a pressed row between two divided rows, and no case used a list with dividers. A touch case opens the Interactive story with hasDividers, holds the middle row and releases it, and checks the divider above it and its own divider below, by computed border and by the pixels of each divider's strip, while held, halfway through the release fade and after it; each strip must differ from the row's surface just inside it at rest, so a match is a drawn divider. It passes in Chromium and WebKit. A page style that hides a pressed row's divider fails it while the row is held.
…duce Motion constant meta#8077's FR4 check (interactionOverlay.cell.test.ts) asks every release cap in the overlay to read the Reduce Motion constant through a min(). This branch's reduced-motion arms used the constant directly; they now take min(<the release's clock>, the constant), so a control's 250 ms and a mouse's or the Space key's 200 ms both cap at 200 ms, as before. The build's reduced-motion check reads the cap.
… or press profile The selection hold and the press profiles are gone from the base, so the Item case and the shared checks' notes no longer mention a held arm, a cell marker or a profile clock; the checks themselves are unchanged.
… judges its first frame on that frame's time 'a spring started right before a block plays in full after it' waited a fixed 300 ms of wall clock after the block, then read the screencast. In the full interaction run on a loaded box no moving frame had reached the screencast by then, and the proof failed on an empty read. It now waits until the page shows the box a quarter of the way along and three more frames reach the screencast. Its bound changes form, not strength: the curve's own start (the handle's started) must come after the block, and the first moving frame may be no further along than a curve started a frame before the block ended, at that frame's own time, which is the old 30 px for a frame right after the block and still holds when a loaded machine sends frames sparsely. A curve dated at the call is 107 px in.
…as it is now meta#8118's fold moves a row's own press layer from interactionOverlayStyles.cell to rowPressLayerStyles.cell (utils/rowPressLayer.ts); the comment that says the checkbox's release runs in step with it now names it there.
…e surface releaseAnimationsOf counted only the press owner's own animations (on it, or its ::before or ::after). A component that draws its release on a descendant, such as CheckboxInput's release on its box wrapper's ::after, then lost the end of it: fading went at the 200 ms clock while the 250 ms release still ran, and the frame it went relaid and repainted the box. It now also counts a CSS release animation on a descendant whose nearest press owner (closest [data-astryx-pressable]) is this element, the same nearest-owner rule the layers read by, so a press owner nested inside plays its own press's release and never extends this one. Red first: a unit case whose release runs on a box inside the row found fading gone at the clock on the change before this one; it now holds to the release's end. A second case keeps a nested press owner's release, on it or inside it, out of the row's.
…ts own timeline One run measured a 250 ms release whose `fading` lasted 241.6 ms on the page's clock: an animation's start is a frame's time, which can come before the write, so a bound on the page's clock read a whole release as short. The check now takes the animation's start from its own timeline (when it is ready) and the timeline's time when `fading` goes, and asks that it ran to its end; WebKit drops the animation at the removal, so its progress cannot be read there.
There was a problem hiding this comment.
Approval waits on an engineering owner's decision on four spec deltas this pull request carries: the reworded component:Button FR15 (a 250 ms finger release where the record says 200 ms), a read-only checkbox painting no press (against design:user-states DR1 and DR2), the case-exact astryx component lookup, and astryxStylex's new treeshakeCompensation default, the last two with no governing spec yet. An engineering owner records that decision by commenting /approve-spec <full head SHA> on this pull request, and the author may do so as one. The indicator release itself now keeps fading after a blocked main thread, but these changes are still needed whatever the decision:
- Land #8118, #8219 and #8360 first and rebase, so this diff is the indicator change alone (it is 35,764 authored lines today).
- If the read-only change stays, add mouse before-and-after evidence for a read-only CheckboxInput and CheckboxList row (
spec:AST-017FR59). - Justify or trim the 1.0–1.4 KB CSS growth the size report flags on 11 components, and let CI report this head.
- Make the checkbox mark honour the mouse press marker as Button's layer does, or narrow that marker's 200 ms contract (
utils/pressFeedback.ts:103-111).
[Automated review]
| const isRowPressSurface = use(CheckboxRowPressContext); | ||
| // A checkbox that cannot change paints no press: disabled, or read-only, | ||
| // whose value is shown but refuses every change. | ||
| const paintsPress = !isDisabled && !isReadOnly; |
There was a problem hiding this comment.
A read-only checkbox now paints no press with a mouse either, which changes fine-pointer behavior: spec:AST-017 FR59 asks for mouse before-and-after evidence, and design:user-states DR1 asks every enabled CheckboxInput indicator to paint during a hold (DR2 exempts only disabled controls). This waits on the open owner decision.
| default: null, | ||
| '@media (pointer: coarse)': 'var(--_indicator-press-release, none)', | ||
| }, | ||
| animationDuration: { |
There was a problem hiding this comment.
The mark has no [data-astryx-press-input] arm, so a mouse press on a touch-first device plays the control's 250 ms ease-in-out here while Button's layer switches to the 200 ms clock the marker promises (utils/pressFeedback.ts:103-111, utils/controlPressLayer.ts:216-226). Add the same arm, or narrow the marker's contract.
Depends on #8118 and #8219. Review only this PR's change: vjeux/astryx@a9fd510...63b0be3 (it also lists #8212's and #8219's commits).
Must land with #8077 and #8118: alone they drop the row indicators' press.
User impact
Under a finger, in the compositor press stack (#8118 with #8077's row layer):
Now box and circle paint their own press and fade on the compositor.
Evidence
iOS 26.2 Mobile Safari, a 0.4 s hold (magenta ring); the busy clip's counter stops while script blocks. Before: the stack without them.
Problem and solution fit
--_press-paint-image(transparent under the cell layer) and faded through the host's main-thread strength.The layer paints the pressed token at full strength, then releases by its own opacity on internal clocks (UIButton's 250 ms standalone, a cell's 200 ms in a row; under Reduce Motion the policy's fade, third commit). Every layer rule sits behind the coarse pointer: a fine pointer keeps main's behavior (fourth commit). RadioListItem drops the cell layer's
::before; its row stays bare. Each press owner declares its image and release, and each layer reads its nearest owner's, so a nested mark answers only its own (fifth commit).Expected behavior and authority
design:user-states(current) DR1: CheckboxInput and RadioList indicators paint during a hold; in a RadioList row only the circle paints.design:reduced-motion(current) DR2: under Reduce Motion a touch cross-fade is opacity only, ≤200 ms.spec:AST-017FR59, DEC-25: touch work leaves a fine pointer's behavior as it is.architecture:component-style-authoringINV4: a mark answers only its own owner.spec:AST-094FR5, FR11, DEC-11;button.highlight,list.highlight.Smallest restoration
Mouse
:active, hover, pressed color and press ownership are unchanged; a test holds mouse pixels to the stack's.Scope
Testing
rowPressLayerStyles.cell), mergeable with main 13e8c25. Local gates are the evidence; no CI run is expected:CI=true pnpm lint0 errors;pnpm buildthen rootpnpm typecheckclean; the full core suite (16 workers) passes with no failure (688 files, 16,828 tests); IndicatorPressRelease, ButtonPressRelease and ListRowPress pass in Chromium (37, 1 skip) and IndicatorPressRelease in WebKit (12, 2 Chromium-only skips); CheckboxListPress and PressModality a11y 13/13; a mouse click still selects a card. Red first: on fix(core): a control's press release runs on the compositor #8118 9d1b17c a CheckboxInput's touch press stoppedfadingat 201 ms while its 250 ms release still ran, and "the press release of a checkbox does no Layout or Paint per frame" failed (a Layout and two Paints at +200 ms); on this headfadingholds 274 ms and that case passes. The node project's reds are main's:story-tree.test.ts(main's A11y story titles, fixed in test(storybook): run the story-tree check where Storybook is built, and bring the tree back to its shape #8509),cache.test.mjs's two npm-lock cases (a Node started under another file name, fixed in fix(cli): a crashed run's release install is recognized by the Node it ran on, whatever its name #8520), andpublic-imports.test.mjs's "lists its findings only" case, which runs four whole integration checks in one case and timed out at 30 s under the suite's load (alone it takes 23 s).CI=true pnpm lint0 errors;pnpm buildthen rootpnpm typecheckclean; IndicatorPressRelease, ButtonPressRelease and ListRowPress in Chromium 35 passed (1 skip), IndicatorPressRelease in WebKit 12/12 (2 Chromium-only skips); CheckboxListPress and PressModality a11y 13/13; units 16,271 underCI=true. On a loaded machine (load average 65) the timeouts that fix(core): BottomSheet releases a drag the way an iOS sheet does #8105, test(Item): wait for the swipe layer's module, not a one-second window #8352, test(DropdownMenu): catch an eager sheet import when it is requested, not by a timer #8353, test(Resizable): load the package root while the file is collected, not inside a timed test #8471, test(Selector, MultiSelector): wait for the on-demand sheet module before the first open #8642 and test(DropdownMenu): the SSR test loads the menu and its sheet at collection #8685 fix failed, as did Table perf's wall-clock budget and Tokenizer.ssr, whose cases each re-import Tokenizer's module graph cold (vi.resetModules(), thenimport('./Tokenizer')) and whose timed-out case's work reaches the next case's hydration; each passes alone. A mouse click on a fine pointer still toggles every story's checkbox and radio.CI=true pnpm lint0 errors;pnpm buildthen rootpnpm typecheckclean; IndicatorPressRelease, ButtonPressRelease and ListRowPress in Chromium 34 passed (1 skip), IndicatorPressRelease in WebKit 12/12 (2 Chromium-only skips); CheckboxListPress and PressModality a11y 13/13; units 16,185 underCI=true; on a loaded machine the load timeouts that test(Item): wait for the swipe layer's module, not a one-second window #8352, test(DropdownMenu): catch an eager sheet import when it is requested, not by a timer #8353, test(Resizable): load the package root while the file is collected, not inside a timed test #8471, test(Selector, MultiSelector): wait for the on-demand sheet module before the first open #8642 and test(DropdownMenu): the SSR test loads the menu and its sheet at collection #8685 fix, Table perf's wall-clock budget and CommandPalette.perf (test(CommandPalette): the lookup-work test finds its options without a visibility check per option #8725) failed, and each passes alone; a mouse click on a fine pointer still toggles every story's checkbox and radio.CI=true pnpm lint0 errors;pnpm buildthen rootpnpm typecheckclean; IndicatorPressRelease, ButtonPressRelease and ListRowPress in Chromium 34 passed (1 skip), IndicatorPressRelease in WebKit 12/12 (2 Chromium-only skips); CheckboxListPress and PressModality a11y 13/13; units 16,195 underCI=true, with load timeouts that test(Item): wait for the swipe layer's module, not a one-second window #8352 (Item's swipe warm-up), test(DropdownMenu): catch an eager sheet import when it is requested, not by a timer #8353 (DropdownMenu's lazy sheet warm-up) and test(Resizable): load the package root while the file is collected, not inside a timed test #8471 (a cold import of the root barrel) fix; a mouse click on a fine pointer still toggles every story's checkbox and radio.test:ui; ItemswipeActions' first-load wait timed out under load, fixed by test(Item): wait for the swipe layer's module, not a one-second window #8352, and passes alone);build-css, isolation, theme template and CLI docs 360;ci-duration-guardand routing 74;pnpm buildthen rootpnpm typecheckclean;CI=true pnpm lint0 errors; IndicatorPressRelease, ButtonPressRelease and ListRowPress in Chromium 34 passed (1 skip), IndicatorPressRelease in WebKit 12/12 (2 Chromium-only skips); CheckboxListPress and PressModality a11y 13/13; a mouse click on a fine pointer still toggles every story's checkbox and radio.build-css, isolation, theme template and CLI 332;ci-duration-guardand routing 67;pnpm buildthen rootpnpm typecheckclean;CI=true pnpm lint0 errors; IndicatorPressRelease, ButtonPressRelease and ListRowPress in Chromium 34 passed (1 skip), IndicatorPressRelease in WebKit 12/12 (2 Chromium-only skips); CheckboxListPress and PressModality a11y 13/13; a mouse click on a fine pointer still toggles every story's checkbox and radio.controlPressConstsandcellPressConsts, with no theme role variables: units 4,732;build-css, isolation, theme template and CLI 332;ci-duration-guardand routing 67;pnpm buildthen rootpnpm typecheckclean;CI=true pnpm lint0 errors; IndicatorPressRelease, ButtonPressRelease and ListRowPress in Chromium 34 passed (1 skip), IndicatorPressRelease in WebKit 12/12 (2 Chromium-only skips); CheckboxListPress and PressModality a11y 13/13; a mouse click on a fine pointer still toggles every story's checkbox and radio.pnpm vitest runCheckboxInput, CheckboxList, RadioList, List, Item,utils,theme,Indicator,motion: 3045 pass. A new check reads every press rule the layer emits and finds none outside the coarse media key (three cases fail without the fourth commit). Each layer paints the token on both arms, releases by opacity on the right clock (with the Reduce Motion policy's fade), fills both ways and reads no strength; the RadioList row removes::before; every press owner declares the press's image and release only in its own press arms, and no layer reads an ancestor's press.pnpm test:interaction IndicatorPressRelease: Chromium 14/14; WebKit 12/12 (2 Chromium-only skips). It covers curves sampled frame by frame against UIButton's and the cell's (all three surfaces), a 700 ms main-thread block, no Layout or Paint, the RadioList row, the mouse controls, and Reduce Motion: the release animates onlyopacityin 200 ms or less (250 on the previous head). New: holding an outer row leaves a mark in its content at rest, for a CheckboxList in a CheckboxList row, a RadioList in a RadioList row and a CheckboxInput in a RadioList row (each fails on the fourth commit: the inner mark paints the outer row's press); stories for the two RadioList nestings are added.pnpm test:a11y-contractCheckboxListPress on this build (Chromium): 7/7.build:cssplusscripts/build-css.test.mjs: 22/22. Core typecheck, eslint, prettier.