Repository navigation
spec(AST-094, AST-057): gestures work on the web for every pointer (FR43) - #8646
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
/approve-spec 10d85dd |
|
AI review status for this pull request.
|
PR Analysis Report📚 Storybook Preview🧪 Sandbox PreviewComponent 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. Bundle Size SummaryGzip figures measure each package's entry file, not the code an import pulls in.
Accessibility AuditStatus: No accessibility violations detected. Visual Regression55 of 504 shot(s) changed. A change here is a question, not a failure: check whether the after is the
and 35 more. Generated by PR Enrichment workflow | Storybook | Sandbox | View full report |
…ller at once; the scroll brake stays touch-only
|
/approve-spec c037c2f |
|
Questions the FR43 / DEC-21 text leaves open, collected from the lanes building on it, one line each, the reason first. None of these hold the open pull requests; they shape the FR43 follow-ups built on the host pieces. Where a lane already has a reading, it's in brackets; "yes, that" is a fine answer. Shared: wheel, trackpad and ctrl-wheel
Shared: a mouse on a pan
Shared: press model under a fine pointer
Shared: touch-only rules on a touchscreen laptop
Context request
Per component
|
There was a problem hiding this comment.
Requesting changes: the every-pointer direction is owner-approved, but it leaves current records that now contradict it, so please amend them in this pull request.
useMenuPress.spec.mdFR1, FR2, FR7, FR9 and its long-press-delay row still keep a fine-pointer finger on the module's own rules by citing the old FR19, and the menu scope no longer says whether a mouse keeps the module's FR3 and DEC-3.spec:AST-057AR2's trackpad reveal contradicts the same record's DEC-4, non-goals, intent, platform line and AR1–AR3 verification row, and AR1 and AR2 disagree about a row a trackpad leaves open.- FR11 is still titled "Two press profiles", and the user-states design (DR1, DR3) and the buttons family still hand FR11 their timing only under a finger.
- The FR24 row and the INV13 row of
architecture:react-component-runtimestill fail a touch listener under a fine pointer.
[Automated review]
| views and activates by the browser's click on a lift inside. On the web a | ||
| control's press ends when the browser takes the touch (FR20), and its | ||
| activation stays the browser's click. | ||
| Under a fine pointer a control uses a third profile with no delay: its |
There was a problem hiding this comment.
This adds a third profile, but FR11 is still titled "Two press profiles" and says "Press feedback MUST follow one of two native profiles" (line 185). The records that own the press look, the user-states design (DR1, DR3) and the buttons family's visual-state row, still hand FR11 their timing only under a finger, so they need the fine-pointer profile too.
| and Selector's listbox, whose options count as rows) under every pointer | ||
| (FR43); FR29 applies to DropdownMenu's trigger under the same |
There was a problem hiding this comment.
With FR26–FR30 under every pointer, packages/core/src/DropdownMenu/modules/useMenuPress.spec.md now contradicts this: its FR1, FR2, FR7 and FR9 keep a fine-pointer finger on the module's rules "(spec:AST-094 FR19)", and its delay row says "500 ms where it is fine", while FR29 opens on press. Dropping "A mouse keeps today's behavior" also leaves open whether a mouse follows FR28 and FR29 or keeps the module's FR3 and DEC-3 (a mouse release outside closes the menu).
| a mouse drag MUST NOT reveal the panel, and hover MUST NOT uncover it. A | ||
| trackpad's horizontal scroll swipe MAY reveal the panel, as a pan that takes |
There was a problem hiding this comment.
This MAY contradicts the rest of the record, which is unchanged: DEC-4 ("On a pointer that can hover, swipeActions does nothing"), the non-goal "two-finger trackpad swipes", the intent ("touch-only … changes nothing on any other input"), the platform line and the AR1–AR3 verification row ("any effect on a fine pointer fails"). AR1 also makes a resting-open panel's entries focusable buttons while this line keeps the gesture out of the tab order on a hovering pointer.
| - **FR24 — Gesture work costs nothing where it is not used.** A component | ||
| that uses no gesture registers no gesture listener, observer or timer, and | ||
| while a touch presentation is disabled none is active. Under a fine pointer | ||
| a component that uses a gesture keeps one listener on its element and adds |
There was a problem hiding this comment.
The FR24 verification row (line 559) still fails "a touch listener on a fine pointer", but FR43 gives a finger gestures under every primary pointer and FR19 claims a finger through a non-passive touchmove listener on the element, so a conforming touchscreen-laptop implementation fails that row. Please also say whether the shared document-level press controller, which components install on mount, counts as gesture work under this budget.
| (`spec:AST-094` FR19, FR24). The modules are not public exports and are not | ||
| small glue in its own build over these modules. The gesture host attaches for | ||
| every pointer, with no touch-only query, and pointer and hover queries live | ||
| with the environment queries; under a fine pointer only a component that |
There was a problem hiding this comment.
The INV13 verification row (line 342) still fails "a touch listener under a fine primary pointer"; it needs the new budget this paragraph states.
| mouse, a pen, and a finger, not only a coarse primary pointer. These stay | ||
| off: swipe actions under a mouse, which would take the browser's text | ||
| selection and native drag (`spec:AST-057` AR2); a pinch under a pen; the | ||
| screen-edge pan on every web input; and the 44 px touch target, which stays |
There was a problem hiding this comment.
"the screen-edge pan on every web input" reads as covering a finger in a browser too, which would switch off FR18's screen-edge pan; "under a mouse, a trackpad or a pen" says what DEC-21 means. "the 44 px touch target" is also not defined by any current record yet.
…ry pointer; FR11 three profiles; row swipes stay touch-only; FR24 and INV13 rows
|
/approve-spec 0329096 |
…a mouse never does (DEC-7)
|
/approve-spec 91bc491 |
There was a problem hiding this comment.
Requesting changes: the every-pointer direction is owner-approved at this head, but a few current claims still contradict it, so please amend them in this pull request.
useMenuPress.spec.mdFR6 and FR11 (with FR3, the Behavior row and the compatibility line) still limit the finger's menu rules to a coarse primary pointer, against the module's own FR2 and AST-094 FR33.- FR43's "a reorder starts on a drag from its handle without a hold" names no pointer, so it overrides
spec:AST-103FR3's finger hold; scope it to a mouse, a trackpad or a pen, or amend AST-103. - The INV13 verification row, and AST-094's FR24 row, fail the shared press controller that FR24 admits; add an exception for it.
The Current-state impact, the FR43 row's "web edge pan", AST-057's trackpad identification and ctrl-wheel page zoom each need a line too (details inline).
[Automated review]
| | FR3 | A press that began inside a menu and is released outside it MUST act on nothing. Under a mouse the menu MUST then close; under a finger or pen it MUST stay open (where the primary pointer is coarse, as `spec:AST-094` FR28 states). | DEC-3; `useMenuPress.test.tsx`, `DropdownMenu.test.tsx`, `Selector.test.tsx` | verify | | ||
| | FR4 | A menu whose rows fit MUST declare `touch-action: pinch-zoom`; one that scrolls MUST declare `touch-action: pan-y pinch-zoom` and `overscroll-behavior: contain`. Neither admits a one-finger pan the menu does not scroll, and both leave a two-finger pinch to the browser, so the page stays zoomable over an open menu. The model MUST treat `pointercancel` and a second pointer as the end of the gesture with nothing acting, and MUST NOT re-implement scrolling. | Browser scroll ownership; WCAG 2.2 SC 1.4.4 under `spec:AST-020` FR1; `DropdownMenu.test.tsx` touch-action case, `useMenuPress.test.tsx`, `MenuPinchZoom.a11y.chromium.spec.ts` | verify | | ||
| | FR5 | The click the browser reports for a gesture this model tracked MUST NOT act on any row; only the release acts. A click with no tracked gesture behind it, or one whose `detail` is 0, MUST act on its target as before. The swallower arms at a tracked release and disarms at the next `pointerdown` or after the stray-click window. | One gesture, one activation; `useMenuPress.test.tsx` FR5 suite | verify | | ||
| | FR6 | At most one row of a menu is lit. Under a finger the row a press began on MUST NOT keep a pressed or hovered look once the pointer left it, so menu rows paint `:active` only under `(hover: hover)`. Where the primary pointer is coarse, `spec:AST-094` FR27 and FR11 govern a finger's lit row and its press look instead. | `DropdownMenuItem.tsx` styles | verify | |
There was a problem hiding this comment.
This still gives a finger AST-094 FR27 and FR11 only where the primary pointer is coarse, while FR2 (line 157) now applies them "under every primary pointer". On a touchscreen laptop the two disagree: :active at once under (hover: hover) here, FR11's cell profile there. Suggest: "A finger or pen follows spec:AST-094 FR27 and FR11 instead, under every primary pointer."
| | FR9 | Where the primary pointer is coarse, opening a menu from its trigger under a finger and the opening finger's handoff are governed by `spec:AST-094` FR29. Where it is fine, a finger keeps the released behavior (`spec:AST-094` FR19): a tap on the trigger opens the menu through its click, and a press held on the trigger for the long-press delay opens the menu with the finger down, prevents the default of the finger's later `touchmove`s until the gesture ends, and continues under FR1–FR4. | `spec:AST-094` FR29 | as `spec:AST-094` states | | ||
| | FR9 | Opening a menu from its trigger under a finger and the opening finger's handoff are governed by `spec:AST-094` FR29, under every primary pointer (`spec:AST-094` FR43); a mouse opens the menu as `component:DropdownMenu` FR8 states. | `spec:AST-094` FR29 | as `spec:AST-094` states | | ||
| | FR10 | A finger released on a sub-menu's row MUST open that sub-menu, as the row's own click would (the lift acts, `spec:AST-094` FR26; on a compact touch screen an `adaptive` row drills in, FR34); a finger resting on the row MUST NOT open it. A finger that slides from a row onto the rows of an open flyout picks there under ORD1. | DEC-8; `DropdownMenuSubMenu.test.tsx` press cases | verify | | ||
| | FR11 | A pointer that went down elsewhere, such as a context menu's long press, and is handed to the menu as it opens MUST be the opening press. Where the primary pointer is coarse, a finger picks only after the handoff distance, counted from the first position the menu sees after it opened as `spec:AST-094` FR33 counts it, and its lift before that acts on nothing and leaves the menu open. | DEC-8; `useMenuPress.test.tsx` continuation suite | verify | |
There was a problem hiding this comment.
AST-094 FR33 now reaches a finger under every primary pointer, so "Where the primary pointer is coarse" lets a laptop finger handed to a ContextMenu pick with no handoff travel here and only after 10 pt there. FR3's parenthetical (line 158), the Behavior row (line 41) and the compatibility line (lines 84–87) carry the same coarse-only wording.
| takes wheel and trackpad scrolling; a pinch takes a ctrl-wheel; one shared | ||
| context request answers a long press, a right click, and Shift+F10; the | ||
| press model has a fine-pointer profile with no delay (FR11); and a reorder | ||
| starts on a drag from its handle without a hold. Under a fine pointer only a |
There was a problem hiding this comment.
This names no pointer, so with "Gestures attach for every pointer" it covers a finger too, which contradicts spec:AST-103 FR3 ("under a finger, a hold on the handle picks the row up … and a flick still scrolls"), not amended here. Scope it to a mouse, a trackpad or a pen, or amend AST-103 FR3.
| | INV10 | Shared test over every adopter of the announcement module | An adopter holding its own Effect, observer, timer, or listener for its own-content announcement | | ||
| | INV11 | Source boundary check plus hook tests across StrictMode replay and unmount | A second keyboard listener or measurement in Core; a consumer reading a CSS custom property on the root or a shared ancestor for the height; an export of the hook; a component outside Core measuring by a rule other than AST-099 FR2, or riding the keyboard; a listener left after unmount; a Core rider driven by JavaScript per frame or by its own transition; or more than one layout read per keyboard change | | ||
| | INV12 | Source boundary check plus real-browser tests on a throttled phone profile over a 10-level by 50-sibling tree and two changes in one frame | A per-frame height or layout, a walk past the area or into a subtree other than `display: contents`, an element moved twice for one frame, an added element, or an exported reporting function fails | | ||
| | INV13 | Source boundary check plus bundle analysis of a page with several touch components, and real-browser tests on a throttled phone profile, under a fine pointer, and on a hybrid device | A second copy of a touch module on one page, a component bundling its own copy of a module's primitive, an `import()` of a module in a component, a public export or doc entry for the modules, an install call or installed check in the public API, a gesture listener in a component that uses no gesture, or an idle document listener outside a press, or an import of `@astryxdesign/core/internal/*` outside Core and Lab fails | |
There was a problem hiding this comment.
FR24 admits the shared press controller, whose window and document listeners stay installed between presses (packages/core/src/utils/pressFeedback.ts lines 396–409), but this row fails "an idle document listener outside a press" with no exception, so a page that follows FR24 fails it. Suggest "… outside a press, other than the one shared press controller"; AST-094's FR24 row (line 564) needs the same.
| | FR34, FR35 | Browser tests and storyboards under a finger | a sub-menu drilling in and its Back row returning; a tap and a drag during a popover's and a menu's growth; a pinch during growth; a mouse | a drill-in that leaves the parent's rows in place, a change between levels that does not play on its roles, a tap during growth that closes, scrolls or moves focus, a pinch blocked other than by the author's `touch-action`, or a changed mouse path fails | | ||
| | FR36–FR40 | Release tests on synthetic touch streams with explicit timestamps, per use | below and past each commit point; a flick that projects past it; a slow drag past it; a release moving back; Reduce Motion; keyboard and mouse paths | a decision that changes mid-settle, a release moving back that commits, a settle that does not start at the release velocity, or an action with no keyboard or mouse path fails | | ||
| | FR41 | Browser tests with real touch input (`spec:AST-013` FR10) | a tap; a long press on a ContextMenu trigger, a DropdownMenu trigger the caller renders, a row with a context menu, and a slider thumb; a long press on a checkbox row's label and on plain text; a cancelled press; mouse and keyboard selection | a tap that selects, a hold with an action that starts a selection or callout, a label or plain text whose long press does not select natively, a suppression that outlives the press, or a change to native selection elsewhere or to mouse, keyboard, or assistive-technology selection fails | | ||
| | FR43 | Browser tests with a mouse, a trackpad, a pen, a finger, and the keyboard, and resource counts | a pan by drag and by wheel; a ctrl-wheel pinch; a long press, a right click, and Shift+F10; a mouse press; a handle drag; a row swipe with a mouse and a trackpad; a component with no gesture | a gesture missing under a mouse or pen, a mouse swipe action, a pen pinch, a web edge pan, a press delay under a fine pointer, an idle listener in a component with no gesture, or added stylesheet bytes, fails | |
There was a problem hiding this comment.
This row's inputs include a finger, so "a web edge pan … fails" reads as failing FR18's finger edge pan. FR43 now says "under a mouse, a trackpad, or a pen"; say the same here.
|
/approve-spec 874ee55 |
|
A question on FR43 and Reduce Motion. Today a touch drag's release cross-fades under Reduce Motion whatever the primary pointer ( With FR43 a pan also takes a mouse drag, wheel and trackpad scrolling. Under Reduce Motion, should a release from one of those cross-fade like a touch drag's release, or complete at once like any other fine-pointer call? The answer decides whether the animator's release flag means any drag's release or only a finger's. |
There was a problem hiding this comment.
Requesting changes: the every-pointer direction is owner-approved at this head, but three current claims still contradict it, so please amend them in this pull request.
- FR43's "a reorder starts on a drag from its handle without a hold" names no pointer, so it overrides
spec:AST-103FR3's finger hold; scope it to a mouse, a trackpad or a pen, or amend AST-103. useMenuPress.spec.mdFR3, FR6 and FR11, with its Behavior row and compatibility line, still limit the finger's menu rules to a coarse primary pointer, against the module's own FR2 and AST-094 FR33.spec:AST-057FR5 cross-fades a trackpad-started swipe "because the swipe's own touch pointer started it", whiledesign:reduced-motionDR3 completes it at once on a fine primary pointer; state which one applies.
The trackpad-versus-mouse-wheel signal in AST-057, a shared-press-controller exception in the INV13 and FR24 verification rows, and AST-094's Current-state impact list each need a line too (details inline).
[Automated review]
| takes wheel and trackpad scrolling; a pinch takes a ctrl-wheel; one shared | ||
| context request answers a long press, a right click, and Shift+F10; the | ||
| press model has a fine-pointer profile with no delay (FR11); and a reorder | ||
| starts on a drag from its handle without a hold. Under a fine pointer only a |
There was a problem hiding this comment.
This names no pointer, and FR43 opens with "Gestures attach for every pointer, a mouse, a pen, and a finger", so it also covers a finger on a phone. spec:AST-103 FR3 (not amended here) says "under a finger, a hold on the handle picks the row up at spec:AST-094's values, and a flick still scrolls". Suggest scoping it: "… and under a mouse, a trackpad or a pen a reorder starts on a drag from its handle without a hold", or amend AST-103 FR3 in this pull request.
| | FR3 | A press that began inside a menu and is released outside it MUST act on nothing. Under a mouse the menu MUST then close; under a finger or pen it MUST stay open (where the primary pointer is coarse, as `spec:AST-094` FR28 states). | DEC-3; `useMenuPress.test.tsx`, `DropdownMenu.test.tsx`, `Selector.test.tsx` | verify | | ||
| | FR4 | A menu whose rows fit MUST declare `touch-action: pinch-zoom`; one that scrolls MUST declare `touch-action: pan-y pinch-zoom` and `overscroll-behavior: contain`. Neither admits a one-finger pan the menu does not scroll, and both leave a two-finger pinch to the browser, so the page stays zoomable over an open menu. The model MUST treat `pointercancel` and a second pointer as the end of the gesture with nothing acting, and MUST NOT re-implement scrolling. | Browser scroll ownership; WCAG 2.2 SC 1.4.4 under `spec:AST-020` FR1; `DropdownMenu.test.tsx` touch-action case, `useMenuPress.test.tsx`, `MenuPinchZoom.a11y.chromium.spec.ts` | verify | | ||
| | FR5 | The click the browser reports for a gesture this model tracked MUST NOT act on any row; only the release acts. A click with no tracked gesture behind it, or one whose `detail` is 0, MUST act on its target as before. The swallower arms at a tracked release and disarms at the next `pointerdown` or after the stray-click window. | One gesture, one activation; `useMenuPress.test.tsx` FR5 suite | verify | | ||
| | FR6 | At most one row of a menu is lit. Under a finger the row a press began on MUST NOT keep a pressed or hovered look once the pointer left it, so menu rows paint `:active` only under `(hover: hover)`. Where the primary pointer is coarse, `spec:AST-094` FR27 and FR11 govern a finger's lit row and its press look instead. | `DropdownMenuItem.tsx` styles | verify | |
There was a problem hiding this comment.
This still gives a finger AST-094 FR27 and FR11 only where the primary pointer is coarse, while FR2 (line 157) now applies them "under every primary pointer" and AST-094 FR11 amends this :active sentence. On a touchscreen laptop the two disagree: :active under (hover: hover) here, FR11's cell profile there. Suggest: "A finger or pen follows spec:AST-094 FR27 and FR11 instead, under every primary pointer." Please also say whether a menu row keeps :active under a mouse, since DEC-21 says a mouse press now uses the press model while FR11 gives the no-delay profile only to a control.
| | FR9 | Where the primary pointer is coarse, opening a menu from its trigger under a finger and the opening finger's handoff are governed by `spec:AST-094` FR29. Where it is fine, a finger keeps the released behavior (`spec:AST-094` FR19): a tap on the trigger opens the menu through its click, and a press held on the trigger for the long-press delay opens the menu with the finger down, prevents the default of the finger's later `touchmove`s until the gesture ends, and continues under FR1–FR4. | `spec:AST-094` FR29 | as `spec:AST-094` states | | ||
| | FR9 | Opening a menu from its trigger under a finger and the opening finger's handoff are governed by `spec:AST-094` FR29, under every primary pointer (`spec:AST-094` FR43); a mouse opens the menu as `component:DropdownMenu` FR8 states. | `spec:AST-094` FR29 | as `spec:AST-094` states | | ||
| | FR10 | A finger released on a sub-menu's row MUST open that sub-menu, as the row's own click would (the lift acts, `spec:AST-094` FR26; on a compact touch screen an `adaptive` row drills in, FR34); a finger resting on the row MUST NOT open it. A finger that slides from a row onto the rows of an open flyout picks there under ORD1. | DEC-8; `DropdownMenuSubMenu.test.tsx` press cases | verify | | ||
| | FR11 | A pointer that went down elsewhere, such as a context menu's long press, and is handed to the menu as it opens MUST be the opening press. Where the primary pointer is coarse, a finger picks only after the handoff distance, counted from the first position the menu sees after it opened as `spec:AST-094` FR33 counts it, and its lift before that acts on nothing and leaves the menu open. | DEC-8; `useMenuPress.test.tsx` continuation suite | verify | |
There was a problem hiding this comment.
AST-094 FR33's handoff rule has no pointer condition and now reaches a finger under every primary pointer, so "Where the primary pointer is coarse" lets a laptop finger handed to a ContextMenu pick with no handoff travel here and only after 10 pt there. FR3's parenthetical (line 158), the Behavior row (line 41), the compatibility line (lines 84–87) and DEC-8 carry the same coarse-only wording.
| | FR3 | `swipeBehavior` MUST be one enum with the closed values `reveal` and `commit`, default `reveal`. `commit` with more than one entry on a side MUST warn in development, because the other entries have no touch path. | DEC-2; `spec:AST-002` FR15 | Proposed | | ||
| | FR4 | A sideways drag on a coarse pointer MUST move the row and uncover the side's panel, showing each entry's `label` and `icon` as a filled surface in its `variant`'s colour; the panel MUST NOT be visible at rest, by any pixel. | Real-browser finding: a zero-width panel still paints its padding | Proposed; real-Chromium evidence required | | ||
| | FR4 | A sideways drag on a coarse pointer, or a trackpad's horizontal scroll, MUST move the row and uncover the side's panel, showing each entry's `label` and `icon` as a filled surface in its `variant`'s colour; the panel MUST NOT be visible at rest, by any pixel. | Real-browser finding: a zero-width panel still paints its padding | Proposed; real-Chromium evidence required | | ||
| | FR5 | Under `reveal`, a release past the panel's width MUST leave the row resting open with every entry tappable, and a drag past the commit point or a fling MUST fire the **outermost** entry and no other. Under `commit`, a release past the commit point or a fling MUST slide the row out and fire the outermost entry; nothing MUST rest. After any entry fires, under either model, the row MUST spring back unless the entry has `hasRemoval`, in which case it MUST hold out at the edge it travelled toward. That bit is the only thing an entry declares about the row; the component MUST NOT wait on, observe, or act on what the caller does next, and MUST NOT warn if a held-out row is never removed. A release short of the threshold MUST spring back under both models. Under reduced motion the slide out, the spring back, and the hold-out follow `design:reduced-motion`: the row reaches its resting place with no travel or spring and cross-fades (DR2), because the swipe's own touch pointer started it. `onActivate` MUST fire at activation and may return a Promise. | DEC-1, DEC-2; UIKit `performsFirstActionWithFullSwipe` | Proposed; real-device evidence required | |
There was a problem hiding this comment.
DEC-7 lets a trackpad start this swipe, but the reduced-motion sentence still says the row "cross-fades (DR2), because the swipe's own touch pointer started it". design:reduced-motion DR2 covers only a coarse primary pointer or "the release of a touch drag", and DR3 makes every other transition on a fine primary pointer complete at once, so a trackpad swipe gets two answers. Please state which applies (DR3 conforms to the current design record; covering trackpad releases needs a DR2 amendment).
| Pointer type is read from the pointer event. | ||
| - Unsupported behavior: a device that never produces a coarse pointer gets | ||
| no swipe and nothing else changes. | ||
| - Unsupported behavior: a device with neither a touch screen nor a trackpad |
There was a problem hiding this comment.
A trackpad's two-finger scroll reaches the page as wheel events, and WheelEvent has no pointerType; a mouse's tilt or thumb wheel (and Shift+wheel on macOS) produces the same horizontal wheel events. As written, "Pointer type is read from the pointer event" cannot identify the trackpad path, and a mouse-only device with a horizontal wheel would swipe rows despite AR2 and DEC-7. Suggest defining the path by the input the page receives (a horizontal wheel or scroll gesture), saying what a horizontal mouse wheel does, and updating the modality row (line 220) to match.
| | INV10 | Shared test over every adopter of the announcement module | An adopter holding its own Effect, observer, timer, or listener for its own-content announcement | | ||
| | INV11 | Source boundary check plus hook tests across StrictMode replay and unmount | A second keyboard listener or measurement in Core; a consumer reading a CSS custom property on the root or a shared ancestor for the height; an export of the hook; a component outside Core measuring by a rule other than AST-099 FR2, or riding the keyboard; a listener left after unmount; a Core rider driven by JavaScript per frame or by its own transition; or more than one layout read per keyboard change | | ||
| | INV12 | Source boundary check plus real-browser tests on a throttled phone profile over a 10-level by 50-sibling tree and two changes in one frame | A per-frame height or layout, a walk past the area or into a subtree other than `display: contents`, an element moved twice for one frame, an added element, or an exported reporting function fails | | ||
| | INV13 | Source boundary check plus bundle analysis of a page with several touch components, and real-browser tests on a throttled phone profile, under a fine pointer, and on a hybrid device | A second copy of a touch module on one page, a component bundling its own copy of a module's primitive, an `import()` of a module in a component, a public export or doc entry for the modules, an install call or installed check in the public API, a gesture listener in a component that uses no gesture, or an idle document listener outside a press, or an import of `@astryxdesign/core/internal/*` outside Core and Lab fails | |
There was a problem hiding this comment.
AST-094 FR24 admits "the shared press controller, installed once per page", whose document-level scroll and visibilitychange listeners stay installed between presses while any pressable surface is mounted (packages/core/src/utils/pressFeedback.ts lines 395–409). This row fails "an idle document listener outside a press" with no exception, so a page that follows FR24 fails it. Suggest "… an idle document listener outside a press, other than the one shared press controller …"; AST-094's FR24 row (line 569) needs the same.
| | FR34, FR35 | Browser tests and storyboards under a finger | a sub-menu drilling in and its Back row returning; a tap and a drag during a popover's and a menu's growth; a pinch during growth; a mouse | a drill-in that leaves the parent's rows in place, a change between levels that does not play on its roles, a tap during growth that closes, scrolls or moves focus, a pinch blocked other than by the author's `touch-action`, or a changed mouse path fails | | ||
| | FR36–FR40 | Release tests on synthetic touch streams with explicit timestamps, per use | below and past each commit point; a flick that projects past it; a slow drag past it; a release moving back; Reduce Motion; keyboard and mouse paths | a decision that changes mid-settle, a release moving back that commits, a settle that does not start at the release velocity, or an action with no keyboard or mouse path fails | | ||
| | FR41 | Browser tests with real touch input (`spec:AST-013` FR10) | a tap; a long press on a ContextMenu trigger, a DropdownMenu trigger the caller renders, a row with a context menu, and a slider thumb; a long press on a checkbox row's label and on plain text; a cancelled press; mouse and keyboard selection | a tap that selects, a hold with an action that starts a selection or callout, a label or plain text whose long press does not select natively, a suppression that outlives the press, or a change to native selection elsewhere or to mouse, keyboard, or assistive-technology selection fails | | ||
| | FR43 | Browser tests with a mouse, a trackpad, a pen, a finger, and the keyboard, and resource counts | a pan by drag and by wheel; a ctrl-wheel pinch; a long press, a right click, and Shift+F10; a mouse press; a handle drag; a row swipe with a mouse and a trackpad; a component with no gesture | a gesture missing under a mouse or pen, a mouse swipe action, a pen pinch, a web edge pan, a press delay under a fine pointer, an idle listener in a component with no gesture, or added stylesheet bytes, fails | |
There was a problem hiding this comment.
This row's inputs include a finger, so "a web edge pan … fails" reads as failing FR18's finger edge pan. FR43 says "under a mouse, a trackpad, or a pen"; say the same here, and in DEC-21's "the screen-edge pan".
| selection and native drag, while a trackpad's horizontal scroll swipes a | ||
| row (`spec:AST-057` AR2, DEC-7); a | ||
| pinch under a pen; the screen-edge pan under a mouse, a trackpad, or a pen; | ||
| and the touch target size, which stays coarse-only and never overlaps a |
There was a problem hiding this comment.
No current record defines "the touch target size" (DEC-21 names a 44 px target; the draft control-rhythm design record leaves target intent open as its OQ1). Please name the size here, apart from AST-020's WCAG 2.5.8 minimum, which holds under every pointer.
| | ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ||
| | Public contract | `Item` carries touch swipe actions. `swipeActions` declares, per side, the verbs a sideways drag uncovers as `ItemSwipeAction[]` — `label`, `icon`, `onActivate`, `variant`, `hasRemoval` — and `Item` renders the panel from them (DEC-1). `swipeBehavior` is `reveal` (rest open) or `commit` (fire on release) (DEC-2). The capability is `Item`'s, available on any row whose role permits interactive descendants — a `listitem`, a role-less row — and out of scope where it does not (`option`, `menuitem*`, a radio) (DEC-3). No hover reveal and no mouse swipe (DEC-4); a trackpad's horizontal scroll swipes as a finger does (DEC-7). No new DOM: the row's root translates and its panels counter-translate (DEC-5). | | ||
| | Behavior | On a coarse pointer a sideways drag, or a trackpad's horizontal scroll, moves the row and uncovers the side's panel. Under `reveal` (default) the row rests open so each action is tappable and a long drag fires the outermost; under `commit` the row slides out and the outermost fires; nothing rests. After an action fires the row springs back, or holds out when the action has `hasRemoval` — one bit the component cannot infer, independent of the model. The component waits for nothing afterwards. A short release springs back under both. A mouse never starts the drag; a mostly vertical drag stays the scroller's; a pointer anywhere outside a resting row closes it. | | ||
| | End-user impact | A person on a phone gets the flick they expect on a mail row or a settings row, with the row's own look. Nothing changes for anyone else: a mouse, a keyboard, or a screen reader meets the row exactly as today. | |
There was a problem hiding this comment.
With DEC-7, trackpad users now swipe rows, so "Nothing changes for anyone else" no longer holds; AST-094's Current-state impact list also needs an entry for this record, the INV13 rewrite, and the fine-pointer profile in the user-states and buttons entries.
|
/approve-spec 848021a |
…t morph's source copy AST-057's record takes main's text throughout (meta#8646 drops two-finger trackpad swipes from the non-goals); this branch keeps only its own change there: the row's slide and spring motion plays on theme motion roles (spec:AST-094 FR4), so its durations leave the behavior constants.
…nter gestures) into the theme motion knobs
…nimator's theme springs
…SS part's settle clock
…any primary pointer, a mouse's never (spec:AST-094 FR41) Merged FR41 makes a pen's hold a finger's and keeps a mouse's released behavior, with no coarse-pointer condition: that carve-out was FR19's, and meta#8646 removed it. locksSelectionFor still required a coarse primary pointer, so on a touchscreen laptop a finger's hold on a stepper, a slider thumb or a ContextMenu trigger still selected text. Fails until the coarse gate goes.
…FR43 current) under the press release Conflict in packages/core/src/Button/Button.spec.md, resolved on main's text: FR15 is meta#8646's rewrite (a press paints through the press controller, at once under a mouse or a pen, on FR11's control profile under a finger), with this pull request's own additions kept: under a coarse primary pointer the overlay paints on Button's own ::before and a touch press fades over UIButton's measured release (250 ms), a Space press keeps the 200 ms fade (spec:AST-017 FR59), and under reduced motion a touch release under a coarse primary pointer cross-fades within 200 ms (design:reduced-motion DR2, DR3). The table is re-padded by Prettier; no other row changes.
|
A question on FR43's wheel scrolling, for RefreshControl (#8697). FR43 has a pan take wheel scrolling with no notch exception, so a wheel notch at the top now pulls through a finger's mapping. One 100 px notch maps to about 76 px of overscroll: on a scroll view shorter than about 369 px, where the trigger is its 73 px floor, a single notch starts a refresh; on a taller one (the trigger is 19.8% of the height) it takes more, three on a 714 px page. Should a notch pull less there, or should RefreshControl leave a wheel's notches off? Either needs an amendment; until then #8697 follows FR43 as written. |
…e Popover touch presentation AST-057 takes the dependencies' text, which is main's (meta#8646): the tuning bullet this branch once restored is superseded, and the Popover touch presentation does not touch row actions.
…ws the primary pointer spec:AST-094 FR43, current since meta#8646: under reduced motion a release from a mouse, a trackpad, or a pen follows design:reduced-motion DR2 on a coarse primary pointer and DR3 otherwise. The pager marks only a finger's release as a touch release, so these pass as they stand: - a mouse drag's release on a fine primary pointer lands at once, with no cross-fade (DR3); - the same release on a coarse primary pointer cross-fades in at most 200 ms (DR2); - a trackpad scroll's release on a fine primary pointer lands at once. The comment that touch reaches the pager only under a coarse primary pointer goes: the pager takes every pointer.
…ter proofs (#8666) ## Why Gesture proofs could drive only a finger. The every-pointer requirement (`spec:AST-100` FR4: the same motion from touch, pen and mouse) and #8646, which has gestures attach for every pointer, need a pen stroke and a wheel step as real browser input. A hand-made wheel step was also wrong on phones: Chromium divides it by the device-pixel ratio, so a 120 px step reached a phone page as 40. ## What led to it We are bringing Astryx's touch components to the behavior of native controls, and their proofs drive real browser input through the interaction harness. With gestures now accepting every pointer, the harness had to drive a pen and a wheel the same way it drives a finger. ## Options and tradeoffs - Synthetic DOM events (`new WheelEvent`, `new PointerEvent`): untrusted, and a component can tell (`isTrusted`), so they prove nothing about real input. - Playwright's own wheel as is: a third of the delta in a phone context, and no pen at all. - The browser's own input commands (this pull request): in Chromium, its DevTools input, with the wheel delta scaled back and pen strokes carrying pressure, tilt and their scripted times; in WebKit, its own wheel in a desktop context. WebKit's phone emulation has no wheel and Playwright has no WebKit pen, so the driver says so with flags and proofs skip with a stated reason. ## Why this choice It is the only option whose events are trusted and go through the browser's own input pipeline, and it states each engine's limits instead of hiding them. A run of wheel steps, as a trackpad page swipe sends, goes through `wheelBurst({x, y, steps, everyMs, maxGapMs})`: it sends every step on its schedule without waiting for the one before, resolves once the page has the run's whole delta, and, past `maxGapMs`, throws `WheelPageStallError` when the page's own wheel listeners held the main thread on the step before (WebKit dispatches the next step only once the page is free), and `WheelPacingError`, naming the gaps, when the dispatch stalled before the page. `retakeOnWheelPacing` re-takes a proof once, only on `WheelPacingError`, and lists the re-take; a stall the page caused fails the proof. ## How to check it 1. `pnpm exec playwright test --config playwright.interaction.config.ts internal/interaction-harness/src/harness.interaction.browser.spec.ts --project chromium -g "wheel and pen input"`. 2. Expected: a wheel step is on the page by the time `wheel()` resolves, as exactly its deltas, trusted, with Control when held, whether the page's wheel listeners are passive or not; a pen stroke arrives as trusted pen pointer events with its pressure and its scripted 100 ms between the first and last points. 3. With `--project webkit`, the desktop wheel cases pass and the phone wheel and pen cases skip, each naming why. 4. `pnpm exec vitest run internal/interaction-harness/src/webInput.pacing.test.ts`: the re-take stays red when both attempts are off pace, never re-takes a product failure, and passes and is listed when an off-pace attempt is followed by a good one. 5. `-g "burst"`: a burst paced at 180 ms against a 100 ms bound throws `WheelPacingError` in both engines, as does one paced at 125 ms over a page whose listener takes 30 ms (the page is free before each step arrives); a page that blocks 150 ms on each step is never re-taken (`WheelPageStallError` in WebKit, where a gap is the page's only when the step reached it within a frame of its listeners finishing the one before; in Chromium every gap is the dispatch's). 6. Two earlier mistakes now have their own cases: reading the page right after `wheel()` missed the step when every listener was passive, and a stroke sent right after a navigation pressed at 41 px for 100. ## Evidence - Gestures for every pointer: #8646 - WebKit's one red in the harness self-test, "a run going backward is dated from when it began", is main's own check, which #8535 fixes: at a CI runner's size (four CPUs), main fails it in 4 of 6 full harness runs.
Decided (Cindy, 2026-10-26 20:37, approving her 15:39/15:40 direction: "a lot of these gestures work well in web as well, so we can likely keep it enabled on web", "aside from the 44px tap size requirement").
User impact
Desktop users get the same direct manipulation as phone users: drag a sheet or a card, pan with a trackpad, pinch with ctrl-wheel, long-press or right-click (or Shift+F10) for the same context actions, and press-tracking on buttons and menus. Gestures that would fight the browser stay off: swiping rows with a mouse would steal text selection and native drag.
Records
spec:AST-094FR43 (new)::active).spec:AST-057(amended throughout): a mouse drag never swipes a row and hover never uncovers it; a trackpad's horizontal scroll (and a mouse's horizontal wheel, which sends the same events) reveals or fires row actions as a finger does. Under reduced motion a trackpad-started swipe arrives at once (DR3). The amendment covers the intent, ownership, non-goals, FR4, AR2 (a trackpad-opened row follows AR1), the platform line, the AR row, and DEC-4, and adds DEC-7. AST-094 FR43 matches.module:DropdownMenu/useMenuPressFR1, FR2, FR7, FR9 and the delay row: a finger or pen follows AST-094 FR26–FR30 under every primary pointer, and a mouse keeps the module's rules.design:user-statesDR1/DR3 andfamily:buttons' visual-state row use FR11's profiles under every pointer.architecture:react-component-runtimeINV13: the gesture host drops its touch-only query; pointer and hover queries live with the environment queries.Reconciliation: #8622's action-group FR19/FR24 exception becomes the general rule here (whichever merges second takes the general wording). #8628's scroll-edge exception is about cancelling moves and is unaffected. DEC-21's number assumes DEC-15–DEC-20.
Writing rubric results