Skip to content

feat(lab): NavigationStack's content back swipe answers every pointer (spec:AST-094 FR43) - #8699

Draft
vjeux wants to merge 1990 commits into
meta:mainfrom
vjeux:uikit/c14-navigation-fr43
Draft

vjeux wants to merge 1990 commits into
meta:mainfrom
vjeux:uikit/c14-navigation-fr43

Conversation

@vjeux

@vjeux vjeux commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

Depends on #8204, #8665, #8666 and #8535. Review only this PR's change.

User need

What led here: we are bringing Astryx's touch components to iOS quality on the mobile web, and spec:AST-094 FR43, now on main, has gestures work for every pointer. On a laptop or a desktop, NavigationStack's back swipe did nothing under a mouse or a pen: only the bar's Back returned.

Problem and solution fit

A sideways drag across a pushed screen now goes back under a mouse or a pen as it does under a finger: tracked 1:1 and released by the same rule. The swipe from the screen edge stays a finger's, since FR43 keeps the screen-edge pan off under a mouse, a trackpad or a pen.

Current specification

spec:AST-094 FR43 and DEC-21 (#8646). The edge band and its 10 px decision are FR18 and DEC-22.

Public delta and caller burden

No API change. isInteractivePopEnabled's doc now names every pointer for the content swipe and a finger for the edge swipe.

Design and alternatives

Evidence

In Chromium with a real mouse on a desktop page, a drag across a pushed screen pops it; on the base, the same drag leaves it in place.

Scope

  • One capability is added.
  • Unrelated fixes, cleanup, and policy changes were removed or split.
  • Consumer docs, focused tests, and migration evidence ship with the feature.
  • Public text and artifacts contain no internal Meta context.

Testing

How to check it, in a desktop browser with a mouse, "Lab/NavigationStack › Pushed two levels":

  1. Press on the content, drag right past the middle and release: the screen pops.
  2. Drag slowly and release before halfway: it springs back.
  3. In Chrome's phone mode with touch, everything behaves as before.

What I ran and what it showed: "under a mouse a drag across the content pops, as a finger's does" fails on the base, where the coarse-pointer gate is kept, and passes here. The NavigationStack, SearchField and row-selection browser proofs passed in Chromium and WebKit; the unit tests, typecheck and lint passed.

Takes #8108's latest test fix at its next push, or when #8108 merges; it does not merge before that. Under a finger nothing changes; touch behavior was recorded on the iOS 26.2 simulator and the Android 15 emulator, and a real-device check is the release gate.

cixzhang and others added 30 commits October 26, 2026 16:59
…tion) as a dependency

The action group's press filter reads acceptsTouchInput through internals(),
as the rest of the gesture host does now that the getter is protected.
…on) into the host line

The host keeps both fine-pointer exceptions: perEventPointer (this PR's
press-only tracker) and actionGroup (meta#8309's). Conflicts were the two
option docs, useGestureRecognizer's option read beside meta#8309's deliver
queue, and two test files' neighbouring additions.
…an isEnabled sync) into the arena fixes

This PR keeps usePressTracker in its own module, so meta#8309's deliver queue
(a press tracker's callbacks wait for a microtask while a gesture hook's
isEnabled sync runs) moves into usePressTracker.ts, and the sync depth both
hooks read moves into useAttachment.ts (syncingGestures,
isSyncingGestures). Gestures, utils and hooks 1432/1432; tsc clean.
…t serves under a fine pointer

family:action-groups FR5: the host's attach takes actionGroup
(AttachOptions.actionGroup), and the hook did not pass it through. In a
document whose primary pointer is fine (a jsdom frame, with no matchMedia),
a mouse press on an action of a non-group element reaches no recognizer;
with actionGroup the hook attaches the element again and the press reaches
it. Fails on this head (no such option).
UseGestureRecognizerOptions gains actionGroup (default false), passed to
the host's attach and part of the attachment's key, so changing it
attaches the element again. An adapter that drives an action group (an
AlertDialog's action row) attaches its recognizer with it and adds no
listener of its own. The README says so.
facebook#8056 landed squashed; main's motion README, animator and its tests equal
facebook#8056's final head (2b264d3), so this PR's versions, built on that head,
stand.
… listening

uninstall() removed the scroll and scrollend listeners but kept
fingerScrollers and endedWhileDown, so a finger scroll still running when
the host stopped listening (a per-event press lifting under a fine
pointer, or the pointer turning fine mid-momentum) never ended, and
isFingerScroll answered true for later program scrolls. Both sets now
clear with the listeners. The new test fails on the previous head.

Co-authored-by: Christopher Chedeau <vjeuxx@gmail.com>
…ning) into the arena fixes

Here the finger-scroll watch lives in fingerScroll.ts, whose listen(false)
already clears both sets, so the host keeps this PR's version; the new
test reads the watch through fingerScroll's functions.
…nce, so a still finger's long press begins

A browser with Touch Events sends them beside the pointer events for one
finger. With inputMode 'pointer-events' the host still read both, so the
finger was two touches and a long press (one touch required) never
began; a moving finger's pan still worked, which hid it. The host now
registers no Touch Event listener in that mode: the finger comes from its
pointer events alone.

Test (failing first): under 'pointer-events', a touch pointerdown plus its
touchstart is one touch, and a long press held past its duration begins
and ends.
spec:AST-094 FR20: a watcher registers only passive listeners, but the
host gave every tracked finger's target a cancelable touchend. The host
now counts, per target, the fingers a participant could claim (a
recognizer that claims touches, or a press consumer): touchend is
cancelable only while that count is above zero and passive otherwise,
swapped like touchmove. The new test (a watcher's touch gets passive
touchmove, touchend and touchcancel; a tap's touchend stays cancelable)
fails on the previous head.
…ond route around ./internal/*

Lab's per-module tsconfig paths (core's motion, gestures and
touchPresentation) and the Sandbox/docsite resolver that served them opened
a route around architecture:react-component-runtime INV13's ./internal/*
subpath exports. Nothing in Lab imports those paths here; they're dropped,
and the import guard now fails on any Lab import core doesn't export and on
any Lab tsconfig alias for core.

Co-authored-by: Christopher Chedeau <vjeux@users.noreply.github.com>
…) into the host line

The watchTarget call keeps this PR's blocking test (a kept-press consumer
under a coarse pointer blocks too) beside meta#8589's claiming flag.
vjeux added 12 commits October 27, 2026 05:05
… a mouse press

At the document, an event from inside an open shadow root is retargeted to
the shadow host, and so is a touch's target, so the host looked for
participants from the shadow host and found none inside the root: the
browser then sent its own mousedown and click. Both cases, a finger and a
mouse, attach a tap to an element in the shadow root. Fail until the host
resolves targets through the composed path.
…composed path

Inside an open shadow root the document's listeners see the shadow host,
in event.target and in a touch's target alike, so a recognizer on an
element in the root never got its finger, and nothing claimed it. landedOn
takes the composed path's first element for the event's own target and for
a touch whose target is the event's (another finger keeps its own): a
touch's start, a pointer's press and its pairing, the last touch a scroll
reads, a cancelled pointer's touch and hover.
…he host resolves targets through the composed path) into meta#8320
…s finger: the host resolves targets through the composed path) into meta#8293

# Conflicts:
#	packages/core/src/gestures/GestureHost.ts
…s finger, which this pull request already carries; landedOn moves above eventTime's comment) into the every-pointer host
…wn start

Under load the trace's round trip could begin well after the rise had
started (38 % up at its first frame), so "150 ms into the rise" landed about
190 ms in, at 84 % of the rise, and the bar's carried-on turn passed the
85 % bound (92 %; once in two WebKit runs). The sampler now times the cancel
from the rise animation's start; when a busy runner begins the trace too late
for three frames before the cancel point, the bar is let back down and the
activation tried again, up to three times.
…aa98): the review base

The bases merged with none of this change, so a compare from here shows only
this change.
… (spec:AST-094 FR43)

Gestures attach for every pointer: a mouse or pen drag across the content is
the content swipe, tracked 1:1 and released by the same rule as a finger's.
The edge swipe stays a finger's, the screen-edge pan's default. Both
recognizers lose their coarse-pointer gate; the docs and the mouse proof say
so ("under a mouse a drag across the content pops").
@vercel

vercel Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
astryx Ready Ready Preview Oct 27, 2026 2:08pm UTC

Request Review

@astracat-bot

astracat-bot Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

AI review status for this pull request.

Review status Updated
❓ Needs maintainer input (for maintainers only) Oct 30, 2026, 5:42 AM UTC

@github-actions

github-actions Bot commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

PR Analysis Report

📚 Storybook Preview

View Storybook for this PR

🧪 Sandbox Preview

View Sandbox for this PR

New Components

SearchField (@astryxdesign/lab) · View in Storybook
Metric Value
Entry File (ESM) 253B
Lines of Code 745
Source Files 4
Complexity Very High (31)
Exports SearchField, type, type
Props Count N/A
Has Tests Yes
Has Stories Yes

Modified Components

Item (@astryxdesign/core) · View in Storybook
Metric Before After Delta
Entry File (ESM) 284B 284B 0B
Lines of Code 1372 1377 -
Complexity Very High Very High (122) -
TreeList (@astryxdesign/core) · View in Storybook
Metric Before After Delta
Entry File (ESM) 648B 648B 0B
Lines of Code 872 878 -
Complexity Very High Very High (100) -

Component Size

Component Import before Import after Delta CSS before CSS after Delta
core/AlertDialog 56.9KB 57.7KB +803B 12.4KB 12.5KB +24B
core/AppShell 57.9KB 58.6KB +735B 11.6KB 11.7KB +22B
core/AspectRatio 3.7KB 3.8KB +66B 3.5KB 3.5KB +40B
core/Avatar 37.5KB 37.5KB +79B 6.3KB 6.3KB +23B
core/AvatarGroup 39.8KB 39.9KB +77B 6.4KB 6.5KB +22B
core/AvatarGroupOverflow 39.9KB 40.7KB +829B 6.4KB 6.4KB +23B
core/AvatarStatusDot 7.1KB 7.1KB +61B 3.4KB 3.4KB +41B
core/Badge 3.9KB 4.0KB +64B 3.8KB 3.9KB +39B
core/Banner 47.8KB 48.6KB +780B 9.9KB 10.0KB +22B
core/BaseTypeahead 54.3KB 55.0KB +734B 9.9KB 9.9KB +22B
core/Blockquote 3.5KB 3.6KB +66B 3.4KB 3.4KB +39B
core/BottomSheet 34.0KB 34.0KB +69B 7.9KB 7.9KB +24B
core/BottomSheetSwitcher 14.8KB 14.8KB +67B 3.3KB 3.3KB +41B
core/BreadcrumbItem 66.4KB 67.8KB +1.4KB 11.0KB 11.4KB +412B
core/Breadcrumbs 22.4KB 22.5KB +96B 3.3KB 3.3KB +40B
core/Button 43.3KB 44.1KB +808B 9.2KB 9.2KB +21B
core/ButtonGroup 8.4KB 8.5KB +67B 3.3KB 3.3KB +40B
core/Calendar 54.9KB 55.7KB +801B 10.0KB 10.0KB +22B
core/Card 6.4KB 6.5KB +66B 6.3KB 6.4KB +42B
core/Carousel 50.2KB 51.0KB +807B 10.2KB 10.2KB +21B
core/Center 4.2KB 4.2KB +63B 4.6KB 4.6KB +42B
core/ChatComposer 60.6KB 61.4KB +791B 10.8KB 10.8KB +19B
core/ChatComposerDrawer 23.9KB 24.0KB +93B 4.5KB 4.6KB +39B
core/ChatComposerInput 55.7KB 56.5KB +762B 10.1KB 10.1KB +21B
core/ChatComposerTokenElement 55.7KB 56.5KB +762B 10.1KB 10.1KB +21B
core/ChatDictationButton 46.5KB 47.3KB +786B 9.5KB 9.5KB +20B
core/ChatLayout 48.8KB 49.6KB +798B 9.9KB 10.0KB +20B
core/ChatLayoutScrollButton 46.4KB 47.1KB +763B 9.6KB 9.6KB +20B
core/ChatMessage 23.1KB 23.1KB +85B 3.5KB 3.6KB +39B
core/ChatMessageBubble 7.0KB 7.1KB +67B 3.6KB 3.6KB +42B
core/ChatMessageList 28.9KB 29.0KB +99B 7.8KB 7.8KB +21B
core/ChatMessageMetadata 25.8KB 25.9KB +85B 3.9KB 3.9KB +23B
core/ChatSendButton 45.8KB 46.6KB +780B 9.2KB 9.2KB +21B
core/ChatSystemMessage 6.7KB 6.8KB +71B 3.7KB 3.7KB +39B
core/ChatTokenizedText 6.9KB 7.0KB +63B 3.9KB 3.9KB +40B
core/ChatToolCalls 31.9KB 31.9KB +89B 8.9KB 8.9KB +22B
core/CheckIndicator 9.0KB 9.1KB +68B 3.6KB 3.6KB +39B
core/CheckboxIndicator 4.1KB 4.1KB +65B 3.8KB 3.8KB +22B
core/CheckboxInput 49.9KB 50.7KB +757B 9.3KB 9.3KB +22B
core/CheckboxList 46.0KB 46.1KB +90B 8.1KB 8.2KB +22B
core/CheckboxListItem 54.0KB 55.3KB +1.2KB 10.5KB 11.0KB +541B
core/Citation 25.9KB 26.0KB +93B 4.1KB 4.2KB +41B
core/ClickableCard 12.8KB 13.6KB +810B 6.9KB 7.0KB +46B
core/Code 3.5KB 3.6KB +63B 3.4KB 3.4KB +41B
core/CodeBlock 60.5KB 61.3KB +779B 10.7KB 10.7KB +27B
core/Collapsible 11.7KB 12.5KB +771B 4.2KB 4.3KB +39B
core/CollapsibleGroup 6.4KB 6.4KB +71B 3.2KB 3.2KB +40B
core/CommandPalette 50.9KB 51.7KB +778B 11.8KB 11.8KB +23B
core/CommandPaletteEmpty 6.1KB 6.2KB +64B 3.3KB 3.4KB +42B
core/CommandPaletteFooter 23.3KB 23.4KB +95B 3.5KB 3.6KB +39B
core/CommandPaletteGroup 3.5KB 3.5KB +64B 3.3KB 3.4KB +39B
core/CommandPaletteInput 32.3KB 32.4KB +86B 8.1KB 8.1KB +22B
core/CommandPaletteItem 8.4KB 9.2KB +764B 3.5KB 3.5KB +40B
core/CommandPaletteList 22.4KB 22.5KB +89B 3.3KB 3.3KB +41B
core/ComplexSelector 55.8KB 56.6KB +831B 10.6KB 10.7KB +20B
core/ContextMenu 77.7KB 79.1KB +1.4KB 14.6KB 15.2KB +550B
core/ContextMenuItem 16.6KB 17.7KB +1.1KB 5.2KB 5.6KB +420B
core/DateInput 99.9KB 100.5KB +668B 14.8KB 14.8KB +20B
core/DateRangeInput 69.4KB 70.1KB +769B 11.2KB 11.2KB +21B
core/DateTimeInput 110.1KB 110.8KB +696B 15.2KB 15.2KB +20B
core/Dialog 16.2KB 16.3KB +64B 7.1KB 7.1KB +23B
core/DialogHeader 48.8KB 49.6KB +801B 9.8KB 9.8KB +20B
core/Divider 6.5KB 6.5KB +64B 3.6KB 3.6KB +43B
core/Drawer 31.6KB 31.7KB +84B 8.0KB 8.1KB +23B
core/DrawerHeader 48.9KB 49.7KB +760B 11.2KB 11.2KB +22B
core/DropdownMenu 72.7KB 74.0KB +1.3KB 14.7KB 15.3KB +545B
core/DropdownMenuCheckboxItem 16.1KB 17.3KB +1.2KB 5.6KB 6.0KB +404B
core/DropdownMenuDivider 6.6KB 6.6KB +61B 3.6KB 3.6KB +42B
core/DropdownMenuGroup 6.1KB 6.1KB +69B 3.3KB 3.3KB +39B
core/DropdownMenuItem 15.4KB 16.6KB +1.2KB 5.2KB 5.6KB +421B
core/DropdownMenuRadioGroup 6.0KB 6.1KB +62B 3.2KB 3.2KB +43B
core/DropdownMenuRadioItem 16.2KB 17.4KB +1.2KB 5.6KB 6.0KB +404B
core/DropdownMenuSubMenu 58.1KB 59.5KB +1.4KB 9.5KB 9.9KB +408B
core/EmptyState 7.7KB 7.8KB +64B 3.5KB 3.6KB +42B
core/Field 44.2KB 44.3KB +96B 8.0KB 8.0KB +22B
core/FieldLabel 39.8KB 39.9KB +81B 6.5KB 6.5KB +23B
core/FieldStatus 10.4KB 10.5KB +74B 4.2KB 4.3KB +23B
core/FileInput 54.1KB 54.8KB +741B 10.3KB 10.4KB +23B
core/FormLayout 6.3KB 6.3KB +66B 3.4KB 3.4KB +40B
core/Grid 4.3KB 4.4KB +67B 3.7KB 3.7KB +40B
core/GridSpan 3.4KB 3.5KB +63B 3.2KB 3.3KB +40B
core/HStack 7.3KB 7.3KB +62B 4.9KB 4.9KB +44B
core/Heading 9.1KB 9.2KB +64B 6.8KB 6.8KB +23B
core/Icon 8.8KB 8.9KB +60B 3.6KB 3.6KB +39B
core/IconButton 43.3KB 44.1KB +792B 9.2KB 9.2KB +21B
core/InputGroup 44.8KB 44.9KB +88B 8.0KB 8.1KB +21B
core/InputGroupText 6.3KB 6.4KB +66B 3.7KB 3.8KB +40B
core/Item 11.4KB 12.7KB +1.3KB 4.7KB 5.1KB +423B
core/Kbd 6.7KB 6.7KB +67B 3.5KB 3.5KB +40B
core/LayerProvider 63.3KB 64.1KB +808B 10.1KB 10.1KB +23B
core/Layout 8.1KB 8.2KB +63B 5.5KB 5.5KB +43B
core/LayoutContent 7.4KB 7.5KB +64B 4.9KB 5.0KB +43B
core/LayoutFooter 7.3KB 7.4KB +68B 4.8KB 4.9KB +42B
core/LayoutHeader 7.4KB 7.4KB +65B 4.8KB 4.9KB +42B
core/LayoutPanel 7.6KB 7.6KB +65B 4.8KB 4.9KB +43B
core/Lightbox 51.2KB 51.9KB +751B 9.8KB 9.8KB +21B
core/Link 43.5KB 44.3KB +780B 7.6KB 7.6KB +25B
core/List 6.7KB 6.8KB +59B 3.4KB 3.5KB +40B
core/ListItem 53.5KB 54.6KB +1.2KB 10.5KB 11.0KB +541B
core/Markdown 144.5KB 145.8KB +1.4KB 17.4KB 17.9KB +545B
core/MetadataList 23.1KB 23.2KB +91B 3.6KB 3.6KB +42B
core/MetadataListItem 6.5KB 6.5KB +64B 3.4KB 3.4KB +43B
core/MobileNav 51.0KB 51.7KB +739B 10.1KB 10.1KB +22B
core/MobileNavToggle 46.2KB 47.0KB +761B 9.4KB 9.4KB +20B
core/MoreMenu 72.9KB 74.2KB +1.3KB 14.7KB 15.3KB +545B
core/MultiSelector 76.2KB 76.9KB +730B 14.6KB 14.7KB +24B
core/NavHeadingMenu 8.9KB 9.0KB +65B 3.2KB 3.3KB +42B
core/NavIcon 3.4KB 3.5KB +63B 3.3KB 3.3KB +42B
core/NumberInput 55.7KB 56.4KB +776B 10.7KB 10.7KB +31B
core/Outline 28.6KB 29.4KB +794B 4.0KB 4.1KB +40B
core/OverflowList 8.5KB 8.6KB +67B 3.5KB 3.5KB +40B
core/Overlay 19.5KB 19.6KB +43B 3.8KB 3.9KB +17B
core/Pagination 87.7KB 89.0KB +1.3KB 15.3KB 15.7KB +395B
core/Popover 49.8KB 50.5KB +692B 12.1KB 12.1KB +20B
core/PowerSearch 155.1KB 156.4KB +1.3KB 18.6KB 19.0KB +406B
core/ProgressBar 7.7KB 7.8KB +66B 6.6KB 6.6KB +20B
core/RadioIndicator 3.8KB 3.9KB +61B 3.7KB 3.7KB +20B
core/RadioList 45.6KB 45.7KB +87B 8.0KB 8.0KB +22B
core/RadioListItem 52.1KB 53.3KB +1.2KB 9.7KB 10.1KB +401B
core/ResizeHandle 25.1KB 25.2KB +99B 3.9KB 3.9KB +41B
core/Section 7.5KB 7.6KB +67B 6.2KB 6.2KB +42B
core/SegmentedControl 38.8KB 38.9KB +99B 6.3KB 6.4KB +22B
core/SegmentedControlItem 8.6KB 9.3KB +757B 3.9KB 3.9KB +41B
core/SelectableCard 12.8KB 13.6KB +808B 7.1KB 7.2KB +46B
core/Selector 79.1KB 80.5KB +1.4KB 15.0KB 15.4KB +403B
core/SelectorOption 14.5KB 15.9KB +1.3KB 5.1KB 5.5KB +421B
core/SideNav 58.0KB 58.8KB +770B 10.6KB 10.6KB +21B
core/SideNavCollapseButton 46.6KB 47.4KB +774B 9.5KB 9.5KB +20B
core/SideNavHeading 54.7KB 55.5KB +764B 10.1KB 10.2KB +22B
core/SideNavItem 54.5KB 55.2KB +747B 10.1KB 10.1KB +22B
core/SideNavSection 6.5KB 6.6KB +59B 3.6KB 3.6KB +38B
core/Skeleton 3.8KB 3.8KB +60B 3.6KB 3.6KB +22B
core/Slider 48.5KB 48.5KB +84B 8.5KB 8.5KB +21B
core/Spinner 26.3KB 26.4KB +83B 7.5KB 7.5KB +19B
core/Stack 7.1KB 7.1KB +62B 4.9KB 4.9KB +44B
core/StackItem 6.0KB 6.1KB +63B 3.3KB 3.3KB +38B
core/StatusDot 19.0KB 19.0KB +59B 6.2KB 6.2KB +19B
core/Step 32.4KB 33.2KB +788B 5.3KB 5.3KB +22B
core/Stepper 47.4KB 48.2KB +780B 9.6KB 9.6KB +20B
core/Switch 48.4KB 49.2KB +785B 9.3KB 9.4KB +21B
core/Tab 9.7KB 10.5KB +780B 4.1KB 4.1KB +41B
core/TabList 40.2KB 40.3KB +90B 7.2KB 7.2KB +23B
core/TabMenu 51.6KB 52.4KB +786B 10.0KB 10.0KB +21B
core/Table 84.0KB 85.3KB +1.3KB 15.1KB 15.7KB +537B
core/TableBody 3.3KB 3.3KB +63B 3.2KB 3.2KB +42B
core/TableCell 78.8KB 80.1KB +1.3KB 14.8KB 15.4KB +534B
core/TableFooter 3.3KB 3.3KB +63B 3.2KB 3.2KB +42B
core/TableHeader 3.3KB 3.3KB +63B 3.2KB 3.2KB +42B
core/TableHeaderCell 78.6KB 79.9KB +1.3KB 14.8KB 15.3KB +547B
core/TableRow 6.2KB 6.3KB +66B 3.5KB 3.6KB +40B
core/TableSelectionToolbar 52.4KB 53.1KB +801B 11.2KB 11.2KB +21B
core/Text 8.6KB 8.6KB +66B 6.8KB 6.8KB +23B
core/TextArea 49.4KB 49.5KB +96B 9.4KB 9.4KB +21B
core/TextInput 53.5KB 54.3KB +772B 10.5KB 10.5KB +22B
core/Thumbnail 50.7KB 51.4KB +799B 11.0KB 11.0KB +25B
core/TimeInput 86.8KB 87.6KB +763B 13.8KB 13.8KB +21B
core/Timer 9.2KB 9.3KB +70B 6.8KB 6.8KB +23B
core/Timestamp 27.5KB 27.6KB +103B 9.5KB 9.6KB +21B
core/Toast 59.8KB 60.6KB +764B 9.7KB 9.7KB +22B
core/ToggleButton 44.1KB 44.9KB +762B 9.3KB 9.3KB +21B
core/ToggleButtonGroup 6.3KB 6.4KB +68B 3.2KB 3.3KB +42B
core/Token 29.6KB 30.4KB +783B 4.5KB 4.5KB +42B
core/Tokenizer 64.8KB 65.6KB +826B 11.7KB 11.7KB +21B
core/Toolbar 39.8KB 39.9KB +91B 7.7KB 7.8KB +46B
core/TopNav 51.9KB 52.6KB +727B 10.4KB 10.4KB +23B
core/TopNavHeading 54.2KB 54.9KB +748B 9.9KB 10.0KB +20B
core/TopNavItem 9.0KB 9.8KB +777B 3.8KB 3.9KB +39B
core/TopNavMegaMenu 54.2KB 54.9KB +783B 10.8KB 10.9KB +21B
core/TopNavMegaMenuFeaturedCard 6.7KB 6.8KB +66B 3.4KB 3.4KB +38B
core/TopNavMegaMenuItem 9.1KB 9.9KB +776B 4.1KB 4.1KB +38B
core/TopNavMenu 53.6KB 54.3KB +753B 10.1KB 10.1KB +23B
core/TreeList 32.5KB 33.8KB +1.3KB 4.7KB 5.1KB +423B
core/Typeahead 61.5KB 62.2KB +770B 11.3KB 11.3KB +21B
core/TypeaheadItem 6.2KB 6.3KB +60B 3.4KB 3.5KB +39B
core/VStack 7.3KB 7.3KB +61B 4.9KB 4.9KB +44B
lab/NavigationScreen N/A 63.9KB N/A N/A 1.1KB N/A
lab/NavigationStack N/A 63.9KB N/A N/A 1.1KB N/A
lab/SearchField N/A 40.9KB N/A N/A 1.5KB N/A

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 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
@astryxdesign/lab 5.1KB 1.9KB 2.1KB +185B
Package stylesheet Gzip before Gzip after Delta
@astryxdesign/core 34.6KB 35.1KB +555B
@astryxdesign/lab 11.1KB 12.1KB +987B

Accessibility Audit

Status: 2 accessibility violation(s) found — 2 serious.

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)
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/14 stories · Learn more
    • WCAG: 1.4.3 (Level AA)

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

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Approval is on hold for an engineering owner's spec decision. Letting NavigationStack's content back swipe answer a mouse or pen contradicts spec:AST-094 FR19 and FR24 (and architecture:react-component-runtime INV13) on main, and the FR43/DEC-21 amendment it implements is approved only in the still-open #8646. 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), or #8646 lands first; either way, the decision should cover the rightward mouse drag clearing text selection across a pushed screen and the deferred trackpad pan. Separately from that decision, please:

  • Once #8204, #8665, #8666 and #8535 have landed, rebase so this pull request carries only its three-file change instead of 44,878 authored lines.
  • Make shouldFinishPop cancel a release that moves back (packages/lab/src/NavigationStack/navigationTransition.ts:121-130), as spec:AST-094 FR37 requires.
  • Let a mouse drag start in the leading 40.2 px band in a browser tab (NavigationStackTouch.ts:227-234), or say in the doc that it cannot.

[Automated review]

Comment on lines +121 to +130
export function shouldFinishPop(
progress: number,
velocityWidthsPerSecond: number,
): boolean {
return (
progress +
navigation.interactivePopFinishProjection * velocityWidthsPerSecond >
navigation.interactivePopFinishThreshold
);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

spec:AST-094 FR37 says a release whose velocity points back toward the start must cancel, whatever the travel so far. Here, progress 0.9 released at −0.5 widths/s gives 0.9 + 0.35 × (−0.5) = 0.725 > 0.5, so the pop commits. Returning false for a negative velocity, and adding a row such as [0.9, -0.5, false] to the table in navigationTransition.test.ts, would conform. Mouse and pen releases now go through this rule as well.

Comment on lines +227 to +234
shouldReceiveTouch: (_recognizer, touch) =>
!(
browserOwnsEdgeRef.current &&
startsInEdgeBand(
touch.startX,
stackRef.current?.direction() ?? 'ltr',
)
),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

In a browser tab this refuses every pointer that starts in the leading 40.2 px, including a mouse or a pen, though the band only protects a finger's browser back swipe (FR22). The edge recognizer is off in a tab, so a mouse drag from the left of a full-width stack does nothing. The isInteractivePopEnabled doc promises a drag "anywhere in the content, under any pointer". Skipping the band for touch.pointerType === 'mouse', and adding the tab case to the mouse proof (which runs with standalone: true), would close the gap.

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Meta Open Source bot. label Oct 27, 2026
@cixzhang cixzhang closed this Oct 28, 2026
@cixzhang cixzhang reopened this Oct 28, 2026
@cixzhang

Copy link
Copy Markdown
Contributor

Posted by Cindy's agent on her behalf: closed and reopened only to restart this PR's CI on the current workflow after tonight's queue-wide cancel; nothing in the PR changed.

@vjeux
vjeux marked this pull request as draft October 29, 2026 22:39

This branch was successfully deployed

1 active deployment
Preview — 46bcd7b4 Deployed Oct 27, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Meta Open Source bot. needs:design-review Affects visuals — Design should review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants