Repository navigation
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
AI review status for this pull request.
|
a157048 to
c56cb3a
Compare
There was a problem hiding this comment.
Approval waits on an engineering owner's decision: no current spec covers AlertDialog's touch input rule as implemented here (every tap, press and click is held for 435 ms after the alert starts presenting, and while it fades out its content is inert and focus leaves it), so an engineering owner needs to approve or reject that rule and say which record states it, by commenting /approve-spec <full head SHA> on this pull request; the author may do so as an engineering owner. Independently of that decision, these need changes:
- The branch carries the unmerged #8084, #8342 and touch-foundation commits (about 37,000 authored lines in 214 files); once they land, rebase so this pull request is only the 7-file AlertDialog commit.
gestures/VelocityTracker.tsoffers four selectable release filters and no stillness reset, which contradictsarchitecture:interaction-modalityINV10 andspec:AST-100FR2.- AlertDialog's touch action tracking has no
presentationopt-out, whicharchitecture:public-component-apiINV16 requires unless a current AlertDialog record defines it. - Reopening the alert during its fade leaves focus on the document body, because the dismissal hold blurs the focused action and nothing restores it.
- Under React StrictMode, an alert mounted open loses its presentation hold: the unmount-only effect's cleanup releases it during replay and the transition effect never places it again.
- The description should give element counts for the hold's shield, as
architecture:component-style-authoringINV7 asks for added elements.
[Automated review]
PR Analysis Report📚 Storybook Preview🧪 Sandbox PreviewModified ComponentsAlertDialog (@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 | +31.7KB |
+74B |
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 | 34.6KB | +43B |
Accessibility Audit
Status: No accessibility violations detected.
Visual Regression
55 of 504 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
|
Deployment failed for project astryx with the following error: |
There was a problem hiding this comment.
Approval waits on an engineering owner's decision: no current spec covers AlertDialog's touch input rule as implemented here (every tap, press and click is held for 435 ms after the alert starts presenting, and while it fades out its content is inert and focus leaves it), so an engineering owner needs to approve or reject that rule and say which record states it, by commenting /approve-spec <full head SHA> on this pull request; the author may do so as an engineering owner. Independently of that decision, these need changes:
- The branch carries the unmerged #8084, #8342 and touch-foundation commits (about 37,600 authored lines in 198 files); once they land, rebase so this pull request is only its 7-file AlertDialog commit.
- AlertDialog's touch action tracking has no
presentationopt-out, whicharchitecture:public-component-apiINV16 requires unless a current AlertDialog record defines it. - AlertDialog's import now carries the whole touch layer and the complete spring table, while
architecture:component-style-authoringINV6 asks for a lazy load under a coarse pointer or a stated justification, and only the table rows the alert uses. - Reopening the alert during its fade leaves focus on the document, because the dismissal hold blurs the focused action and nothing restores it.
- Under React StrictMode an alert mounted open loses its presentation hold, since the unmount-only effect's cleanup releases it during replay and the transition effect never places it again.
- The description should give element counts for the hold's shield, as
architecture:component-style-authoringINV7 asks for added elements.
[Automated review]
| useEffect( | ||
| () => () => { | ||
| releaseHoldRef.current?.(); | ||
| releaseHoldRef.current = null; | ||
| }, | ||
| [], | ||
| ); |
There was a problem hiding this comment.
Under StrictMode this cleanup runs during the replay and releases the hold the transition effect below placed; the replayed transition effect then returns early (was === now), so an alert mounted open has no hold or shield during its presentation in development. architecture:react-component-runtime INV3 asks the effect that acquires a resource to release it in its own cleanup: let the transition effect own the hold and re-place it when a replayed setup finds the alert still presenting, and have the StrictMode test touch during the presentation.
| if (now === 'open' && was === 'leaving') { | ||
| // Reopened mid-exit: fade back from where it is, with its velocity. | ||
| // Touches come back a presentation's time after that fade starts. | ||
| exitRef.current = null; | ||
| holdWhilePresenting( | ||
| fadeFromHere(panel, backdrop, 1, springs.alertPresent), | ||
| ); | ||
| return; | ||
| } |
There was a problem hiding this comment.
Releasing the 'layer' hold here removes inert but leaves focus where the dismissal hold put it, on the document, and Dialog does not re-run its [data-autofocus] focus because isOpen stayed true. The reopened alert has nothing focused, so Enter does nothing until Tab. Refocus the element the hold blurred, or Cancel, when a reopen releases a 'layer' hold.
| import {useMergedRefs} from '../hooks/useMergedRefs'; | ||
| import {alertController} from '../motion/uikit/alert'; | ||
| import {useTouchPresentation} from '../utils/touchPresentation'; | ||
| import {useAlertDialogTouch} from './AlertDialogTouch'; |
There was a problem hiding this comment.
This static import brings the animator, the Layer hold, the gesture host and the long-press recognizer into every AlertDialog import, though they run only under a coarse pointer (the size report for the previous revision measured +30.1 KB gzip). architecture:component-style-authoring INV6 asks for a lazy load under a coarse pointer or a stated justification.
| useActionGroupTracking(surfaceRef, { | ||
| itemSelector, | ||
| highlightOnsetMs: alertController.actions.highlightOnsetMs, | ||
| highlightExpansion: alertController.actions.highlightExpansion, | ||
| shouldTrack: target => target !== surfaceRef.current, | ||
| isEnabled: isTracking, | ||
| // A mouse keeps selecting the alert's text: the host's permanent | ||
| // long-press styles stay off. A held finger selects nothing, since the | ||
| // group's hold does something (`spec:AST-094` FR41). | ||
| manageStyle: false, | ||
| }); |
There was a problem hiding this comment.
This group tracking switches how the open alert follows a finger, and AlertDialog has no presentation value that keeps each button's own click: architecture:public-component-api INV16 requires one unless a current AlertDialog record defines the tracking.
…ill needs the element, and the listeners that cancel The release note, the stream's header, the host's comment and the README said the stream is one passive listener per event type and that a document-capture stopPropagation no longer starves the recognizers. Such a stop never hid a listener on the same node, and some subscriptions cancel. They now say what changed: an app's window-capture stopPropagation, or a document-capture stopImmediatePropagation registered before core, no longer hides a touch; a recognizer that must claim still needs the touch to reach its element; the gesture host's touchend, click and contextmenu cancel while a claimer is attached and the page hold's touchmove while it holds; and the stream stays ahead of an app's window-capture listener only until a type's listener is re-added. The README's list of blocking touchmove listeners gains the claimed touch's target and the document while a claiming element moves.
…default it moves
The {stylexOptions} form of astryxStylex now passes StyleX
treeshakeCompensation: true unless the caller sets it. spec:AST-017 FR3
asks the release note to name the old and new default and the option
that restores the old one; it now does.
…er and for the two stops the input stream fixes - A still finger with sub-pixel jitter over a scroll view keeps its tap and scrolls nothing (spec:AST-094 FR18–FR22). - A tap is still recognized under an app's window-capture stopPropagation, and under a document-capture stopImmediatePropagation registered before core's: the recognizers hear touches on the window's capture phase. Chromium and WebKit pass.
…s once Where nothing that claims is attached, the host's touchend listener is passive and cannot cancel the engine's own click after a retargeted tap clicks what is drawn. The shared click guard swallows that late click whatever is attached; this case pins it, as asked on meta#8293.
meta#8360 now carries the review fixes of the foundation it holds. The two conflicts are this pull request's own files: - GestureHost.ts: meta#8360 counts the attachments that ask for an action group; here the fine-pointer group listener and the per-event recognizer set are gone (every pointer is served, FR43), so the merge keeps the count and drops those two. - GestureHost.test.ts: both sides added the same assertion to the second-finger case; the merge keeps this side's comment. meta#8360's action-group lifetime case counts a group's own pointerdown under a fine pointer, which this pull request replaces with one passive pointerdown on any element that uses a gesture (its own idle-listener case), so the merge leaves it out.
… attached, in this host's terms meta#8360's action-group lifetime case pins that an element stays a group while any attachment asks and stops with the last. Here every element that uses a gesture is served as a group under a fine pointer, so the same invariant reads as the element's one passive pointerdown: with two gestures attached, detaching either keeps it, and detaching the last removes it. It passes on this head.
meta#8360 carries this pull request's predecessors and the review fixes of the foundation it holds; this pull request's own diff is now its six commits (a second finger fails a one-finger pan wherever fingers come from, and the check reruns once that finger is delivered; a press's selection lock gets what it landed on; a pinch's second finger on another pan claims neither; the wider-band edge pan's real-touch proof; text). Two conflicts: - GestureHost.ts: the selection lock's target takes meta#8360's presented target, which is what the press landed on unless an element under it is moving, so it keeps this pull request's fix and meta#8293's correction. - .changeset/gesture-press-tracking.md: meta#8360's text, which names both writers of the press attribute.
…esture host's subscriptions The gesture host subscribes no click: the one cancelling click subscriber is the click guard's, added at a window's first arm and kept. The release note, the stream's header and the gestures README listed click with the host's touchend and contextmenu; they now name the guard's own.
… lift and the browser's menu, keeps a kept press's finger, and goes when the page hides From the review of this pull request, three cases fail on its head: - an armed press installed the page listeners without the claiming ones, so a claimed finger's touchend stayed passive and the browser's menu opened during a claimed long press (spec:AST-094 FR21); - a kept press got its blocking touchmove during an armed press, but its moves were cancelled only where the primary pointer is coarse, so a held stepper on a touchscreen laptop lost its finger (FR11, DEC-21); - a page hidden mid-press cancelled the press but kept the page's listeners until the next lift anywhere (FR24).
…lift and the browser's menu, keeps a kept press's finger, and goes when the page hides - A finger's or a pen's armed press now brings the claiming listeners (the cancelable touchend and the contextmenu canceller) with the page listeners, as wherever the host installs, so a claimed lift clicks nothing and a claimed hold opens no browser menu (spec:AST-094 FR21). A mouse's press needs neither and adds neither. - A kept press keeps its finger under any pointer: its target blocks and its moves are cancelled once its press is delivered, not only where the primary pointer is coarse (FR11, DEC-21). - A page hidden mid-press releases the armed press as a blur does, so the page's listeners go with the cancelled press (FR24).
…'s selection An action group's press is a long press with no minimum duration, so it claims at the press's pointerdown, before any movement. The drag selection hold then cleared the page's selection, so a plain click on a group's button left its Copy or Cut with nothing to act on. The case fails on this head: the selection is gone after the pointerdown.
…age's selection The drag selection hold cleared the page's selection when any mouse or pen contact was claimed, but an action group's press claims at its pointerdown, before it has moved or selected anything, so a plain click on a group's button left its Copy or Cut with nothing to act on. A claim before any movement now keeps the page's selection: the hold still refuses a new one, and puts the page's back over a selection that appears while it runs. A claim after movement clears what its drag selected, as before (spec:AST-094 FR41). The case before the new one now waits for its hold to end a task after the lift, so no hold outlives that test.
…t every pointer does - GestureHost's header, the gestures README and coarsePointer.ts no longer describe the coarse-only host: a finger or a pen takes the touch-down lock under any primary pointer, a kept press's finger is kept during a fine pointer's press too, and recognizers attach for every pointer while the page listeners last a press (spec:AST-094 FR19, FR24, FR41, FR43). - The fine-pointer selection case asserted the replaced rule and passed only because it read iOS WebKit's inline lock without iOS WebKit: it now asserts the lock a fine pointer's finger and pen take, and that a mouse takes none. - FR41's sentence and its verification row say that a press claimed at its own pointerdown, before it moves, keeps the page's selection, while a drag that moved before its claim loses what it selected.
…ion, in the present tense The sentence said a drag that moved before its claim loses the selection it started, but the hold clears the page's selection then, whatever made it; and an unclaimed mouse drag kept "today's" behavior, a phrase that ages. It now says a claim after movement clears the page's selection, a press claimed at its own pointerdown keeps it, and an unclaimed mouse drag keeps its released behavior.
This pull request now sits on meta#8665, so a stack carrying it and meta#8665's other dependants (meta#8676) needs no second GestureHost resolution. Two hunks in GestureHost.ts's pointerdown, both this pull request's: - the second-finger check where fingers come from pointer events stays, followed by meta#8665's rule that a right click, a pen's barrel button or a macOS ctrl-click starts no press; - the check that runs again once the new finger is delivered stays, followed by meta#8665's selection lock call, which no longer takes the window.
Once a component in the root entry holds its layer's input (a popover's presentation hold, meta#8223), Layer/inputHold's static imports of gestures/GestureHost and gestures/nativeScroll put eight gesture modules (about 47 KB) in the root entry, against architecture:react-component-runtime INV13. Bundle a component that imports the input hold and require no gesture bytes. Fails while the input hold imports from gestures/.
…e host asks it Layer/inputHold registered a touch gate with the gesture host (getGestureHost(doc).addTouchGate(isInHeldLayer)) and walked the flat tree through gestures/nativeScroll, so any component that holds its layer statically carried the gesture system into its bundle and the root entry (architecture:react-component-runtime INV13). Invert it: the host asks isInHeldLayer for every touch itself (gestures may import Layer), the hold registers nothing, and it walks the flat tree through utils/flatTree, where parentAcrossShadow already lives. addTouchGate stays for other gates. A host test holds a held layer's touches with no gate (it fails if the host stops asking the hold).
There was a problem hiding this comment.
Approval waits on engineering-owner decisions: this head amends spec:AST-094 (two alert spring rows in FR4's table and a claimed mouse or pen drag selection rule in FR41), and AlertDialog's exit hold, UIKit motion default and 320 px phone width rest only on the draft component:AlertDialog, with no presentation value that keeps today's dialog. An engineering owner records a decision on the spec text by commenting /approve-spec <full head SHA> on this pull request (the author may do so as an engineering owner), and provides or identifies the current spec that governs those AlertDialog touch defaults, such as an approved component:AlertDialog. Independently of those decisions, these need changes:
- Land #8342 and #8084 and rebase, so this pull request is its own change rather than 34,664 authored lines that also carry an unrelated build-package fix.
- Correct
AlertDialog.doc.mjslines 24 and 199, which say a mouse keeps each button's click while the actions are tracked under every pointer. - Let a mouse through the entrance hold on a coarse-primary device, as FR35 keeps a mouse's behavior, or have an owner narrow that sentence, and test that case.
- Add the before-and-after mouse evidence
spec:AST-017FR59 asks for, land #8671 before this, and reword the three changesets that say no component uses the hold, the group-item marker or the tracker.
[Automated review]
| usage: { | ||
| description: | ||
| 'AlertDialog asks the user to confirm a destructive or irreversible action before it happens. Use it for things like deleting content, revoking access, or discarding unsaved changes.\n\nIt implements the WAI-ARIA APG [Alert Dialog pattern](https://www.w3.org/WAI/ARIA/apg/patterns/alertdialog/): `role="alertdialog"`, a title linked by `aria-labelledby`, a consequence description linked by `aria-describedby`, focus moved into the dialog on open and returned to the trigger on close, and no dismissal by clicking outside. Escape cancels.\n\nAlertDialog passes its requested width through to Dialog, which clamps the surface to the container and dynamic viewport with token gutters. Generic Dialog footers should wrap, but Dialog does not own action semantics or order; consumer composition controls that. AlertDialog owns its confirmation semantics: above 640px, actions render horizontally and may move onto another row; at 640px and below, the destructive action appears above Cancel and both buttons fill the footer width. Button labels retain their standard single-line behavior. The breakpoint follows available width, not pointer or hover capability. The body scrolls when block space is constrained.\n\nFor cases where you want to show an alert without managing open state, use the `useImperativeAlertDialog` hook: call `alert.show(options)` and render `alert.element` in your tree.', | ||
| 'AlertDialog asks the user to confirm a destructive or irreversible action before it happens. Use it for things like deleting content, revoking access, or discarding unsaved changes.\n\nIt implements the WAI-ARIA APG [Alert Dialog pattern](https://www.w3.org/WAI/ARIA/apg/patterns/alertdialog/): `role="alertdialog"`, a title linked by `aria-labelledby`, a consequence description linked by `aria-describedby`, focus moved into the dialog on open and returned to the trigger on close, and no dismissal by clicking outside. Escape cancels.\n\nAlertDialog passes its requested width through to Dialog, which clamps the surface to the container and dynamic viewport with token gutters. Generic Dialog footers should wrap, but Dialog does not own action semantics or order; consumer composition controls that. AlertDialog owns its confirmation semantics: above 640px, actions render horizontally and may move onto another row; at 640px and below, the destructive action appears above Cancel and both buttons fill the footer width. Button labels retain their standard single-line behavior. The breakpoint follows available width, not pointer or hover capability. The body scrolls when block space is constrained.\n\nOn a touch screen the two actions are tracked as one group under a finger or a pen, the way iOS tracks the actions of an alert: the action under the finger highlights as soon as it is touched, the highlight follows the finger from one action to the other, and lifting the finger runs the action under it. Lifting it off both actions runs nothing. A mouse, the keyboard and assistive technology keep the click of each button.\n\nUnder a finger, on any touch device, the alert also appears and leaves the way an iOS alert does: it scales down slightly into place while it and the dimming fade in, and it fades out with the dimming when it closes. While it appears a touch does nothing (keys and assistive technology still act), and while it leaves nothing reaches it: a touch, a click, a key or an assistive-technology activation does nothing. With reduced motion it only cross-fades. With a mouse it keeps the theme entrance and closes at once.\n\nFor cases where you want to show an alert without managing open state, use the `useImperativeAlertDialog` hook: call `alert.show(options)` and render `alert.element` in your tree.', |
There was a problem hiding this comment.
This says a mouse keeps each button's click, but the actions are tracked as one group under every pointer: AlertDialog.tsx passes no pointerTypes (the recognizer takes every pointer, useActionGroupTracking.ts:167), and AlertDialog.test.tsx:754 pins a mouse press slid from the action to Cancel cancelling. Please describe the every-pointer tracking here and in the dense docs at line 199.
| const SWALLOWED = [ | ||
| 'pointerdown', | ||
| 'pointerup', | ||
| 'touchstart', | ||
| 'touchend', | ||
| 'mousedown', | ||
| 'mouseup', | ||
| 'click', | ||
| 'contextmenu', | ||
| ] as const; | ||
|
|
||
| const swallow = (event: Event) => { | ||
| // A touch's start stays uncancelled: cancelling it would cancel the page's | ||
| // pinch-zoom too. The shield's `touch-action: pinch-zoom` keeps a finger | ||
| // from panning, and a tap's mouse events and click land on the shield, | ||
| // which swallows them. | ||
| if (event.cancelable && event.type !== 'touchstart') { | ||
| event.preventDefault(); | ||
| } | ||
| event.stopPropagation(); | ||
| }; |
There was a problem hiding this comment.
The shield swallows these events whatever the pointer type, and the alert places a 'viewport' hold whenever (pointer: coarse) matches. On a tablet whose primary pointer stays coarse with a mouse or trackpad attached, a click in the first 435 ms does nothing, while spec:AST-094 FR35 says "A mouse keeps today's behavior (DEC-16)". Either let a mouse's input through during the entrance or have an engineering owner narrow FR35's sentence to a fine primary pointer, and add that case to the tests (both mouse cases today run under a fine pointer).
| '@astryxdesign/core': patch | ||
| --- | ||
|
|
||
| [chore] Internal: one input hold for every layer (`Layer/inputHold`). While a layer presents, a touch, a press or a click anywhere reaches nothing (keys and assistive technology still act, and a pinch still zooms the page); while it dismisses, the same, and the layer's content is inert. The gesture host holds the layer's touches through its touch gates, and `isInputShield` lets a hit test see past a hold's shield. Not exported from the package entry point; no component uses it yet. |
There was a problem hiding this comment.
"no component uses it yet" is not true at this head: useAlertMotion.ts calls holdInput. Please reword so the release notes match what ships.
| '@astryxdesign/core': patch | ||
| --- | ||
|
|
||
| [chore] Internal: the touch press controller leaves the press of an action marked as tracked by its group (`data-astryx-press-group-item`) to that group's own tracker, so one writer paints each press. No component carries the marker yet, so nothing visible or behavioral changes. |
There was a problem hiding this comment.
"No component carries the marker yet" is not true at this head: AlertDialog.tsx:295 spreads pressGroupItemProps on both actions.
| '@astryxdesign/core': patch | ||
| --- | ||
|
|
||
| [chore] The internal action-group tracker paints its highlight through the one press writer in `utils/pressFeedback.ts`, as the document's press controller does, so neither cuts the other's fade short and a `held` paint is left alone. No component uses the tracker yet, and no package entry point exports it. |
There was a problem hiding this comment.
"No component uses the tracker yet" is not true at this head: AlertDialog drives it through useActionGroupTracking.
| | `springs.alertPresent` | mass 1, stiffness 522.35, damping 45.71 | measured: UIAlertController alert appearing, iOS 26.2 simulator, 2026-10-23 | | ||
| | `springs.alertDismiss` | mass 1, stiffness 522.35, damping 45.71 | measured: UIAlertController alert dismissing, iOS 26.2 simulator, 2026-10-23 | |
There was a problem hiding this comment.
These two rows add the alert's own roles to a current record, where FR4 says further roles a component's own record measures join through the generated motion role table (DEC-5). They need an engineering owner's /approve-spec on this exact head, or a move into component:AlertDialog.
| (`spec:AST-017` FR59; DEC-9). A mouse or pen drag that a recognizer claims | ||
| takes no new text selection for that drag only. A claim made after the | ||
| drag has moved clears the page's selection; a press claimed at its own | ||
| pointerdown, before it moves, keeps it. An unclaimed mouse drag keeps its | ||
| released behavior. |
There was a problem hiding this comment.
This changes FR41's mouse rule (a claimed mouse or pen drag now takes no selection and can clear the page's), where FR41 today keeps a mouse on its released behavior (spec:AST-017 FR59). It needs an engineering owner's /approve-spec on this exact head, or its own spec pull request.
… as a dependency [skip ci] meta#8220 no longer carries the withdrawn meta#7839. This PR's action-sheet card used two things only meta#7839 provided: its opensSubMenu helper, and header and footer on a data row. isPlainAction now checks for a nested level itself, by main's rule (a non-empty items), and drops the per-row header and footer checks, since rows have neither. The level's own header and footer still keep the sheet (hasRegions). The DropdownMenu bottom sheet takes main's regions again, inside this PR's card-or-sheet branch. DropdownMenuTouch.ts names both register functions it calls, and says the card plays the alert's two springs through useAlertMotion (meta#8373). The presentation description keeps this PR's card sentence and meta#8080's wording.
Depends on #8084 and #8342. Review only this PR's change: vjeux/astryx@a0a8843...fd8cec9 (the base merges #8084 and #8342; the compared head has this PR's tree, c3a4cdc)
Why
Under a finger, #8084 makes AlertDialog ignore input while it animates with code of its own: the button tracker drops touches through an
isInertcheck, and render guards drop a click, Enter or Escape while it leaves. #8342 adds one shared input hold to the Layer module for every touch presentation (ContextMenu and MobileNav use it). Two mechanisms for one rule drift apart.What led to it
The UIKit-parity work, which brings Astryx's touch behavior to iOS's, gave AlertDialog its touch presentation in #8084 before the shared hold existed. Cindy's decision extends the alert's entrance rule to every touch presentation through #8342's hold, recorded by #8618 (
spec:AST-094FR35, DEC-16): during an entrance a tap or a drag reaches nothing, keys and assistive technology work, and a pinch still zooms. #8595 (INV16) makes the runtime own that one hold.Options and tradeoffs
holdInput. One implementation. One detail moves: while the alert fades out its content is inert and focus leaves it; Dialog returns focus to the opener at the end.Why this choice
Option 2.
useAlertMotionholds the whole viewport from the entrance's start until UIKit's 435 ms window ends (or the reduced-motion cross-fade does), and the alert alone until the fade-out ends; its unusedacceptsTouchesgoes. AlertDialog drops the tracker'sisInertcheck and its click guards, and keeps its Escape guard, because inert content doesn't stop the dialog's owncancel. A mouse is unchanged. Known public-API gap (spec:AST-107FR7): the input hold.How to check it
On a phone or the iOS simulator, in the
Core/AlertDialogDelete story:All five were run as browser tests in Chromium (the VoiceOver step as the click VoiceOver sends), and all but the drag and the pinch in WebKit, which can't inject them: each behaved as written.
Evidence
acceptsTouchesretired, entrance probes on the window; the rest merge newer heads.touch-support-bundle.test.mjs(test(core): a touch component's import carries its gesture code, and nothing else does #8671 fixes it).