Skip to content

chore(core): the UIKit constants table for touch presentations - #8053

Merged
cixzhang merged 14 commits into
meta:mainfrom
vjeux:wip/uikit-pr-m1
Oct 25, 2026
Merged

cixzhang merged 14 commits into
meta:mainfrom
vjeux:wip/uikit-pr-m1

Conversation

@vjeux

@vjeux vjeux commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

Review the last four commits; the last removes the earlier AST-094 draft commit's file, which is revised elsewhere.

User need

Phone presentations of core components should feel like iOS. Today each component carries its own unsourced numbers, so nothing can be checked against UIKit; touch-presentation work needs one place to read UIKit's values, each with proof of its origin.

Problem and solution fit

packages/core/src/motion/uikit/ is that place (spec:AST-094 FR1, FR2):

  • springs.ts: every interaction spring (navigation, sheets, alerts, menus, context menus, keyboard, banner, and the platform presets, named for their use as spec:AST-094 FR4 states: settle, quickChange, emphasis, fingerTracking …).
  • scroll.ts, timing.ts, navigation.ts: UIScrollView physics, press clocks and named curves, navigation push and interactive pop.
  • gestures.ts: recognizer thresholds, UIKit's velocity filter and the haptic stand-ins, as {value, unit} entries the gesture system reads.
  • uikit.test.ts: fails on an unsourced value, an unknown source kind, a guess without a listed measurement task, or a source shipped in a value, and keeps the press clocks equal to the press model.
  • MEASUREMENTS.md: the simulator captures still owed (app, gesture, value, method), with rows that component pull requests cite.

Current specification

spec:AST-094 FR1–FR3 (draft, not yet on main).

Public delta and caller burden

None: internal, not exported, and no component reads it yet. The changeset is [chore].

Design and alternatives

  • Values stay in their source's vocabulary (response and damping ratio, duration and bounce, or mass, stiffness and damping), so each reads as published or measured; the next pull request's math normalizes them.
  • Every source is JSDoc, gesture thresholds included: only {value, unit} ships, so no page pays for provenance strings.

Evidence

  • Every entry carries @source apple-docs …, wwdc …, webkit Source/… (confirmed by a measurement), measured: <app>, iOS <version>, <method>, <date>, or GUESS — … with @measure M-…. Most are UIKit read on the iOS 26.2 simulator or Apple documentation.
  • The test checks every domain file, so a new component file is checked as soon as it is added.
  • Production bundle (minified, gzip): the gesture table alone 2,397 → 785 B; a Carousel on the gesture system 63,389 → 61,489 B.

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

  • pnpm exec vitest run --project ui packages/core/src/motion: 2 files, 131 tests passed; a value with a runtime source, an entry without @source, or the haptic block without its @measure fails it.
  • Every {value, unit} and haptic pattern, evaluated before and after: identical.
  • pnpm -F @astryxdesign/core typecheck: clean.
  • ASTRYX_STRICT_LINT=1 pnpm exec eslint packages/core/src/motion: clean.
  • Prettier, pnpm check:knowledge, pnpm check:changesets, pnpm check:sync, pnpm check:repo (pre-commit): clean.
  • Size: 11 files, 1,684 lines added.

Stack, in merge order: #8053, #8058, #8059, #8060, #8061, #8054, #8257, #8055, #8056, #8067, #8258, #8071, #8072, #8073, #8074, #8076, #8340; #8067 also follows #8043, #8044, #8045, and #8062 merges any time.

@vercel

vercel Bot commented Oct 24, 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 25, 2026 11:25pm UTC

Request Review

@astracat-bot

astracat-bot Bot commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

AI review status for this pull request.

Review status Updated
🟢 Reviewed (for maintainers only) Oct 25, 2026, 11:41 PM UTC

@github-actions

github-actions Bot commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

PR Analysis Report

📚 Storybook Preview

View Storybook for this PR

🧪 Sandbox Preview

View Sandbox for this PR

No new or modified components detected.

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 33.8KB 33.8KB 0B

Accessibility Audit

Status: No accessibility violations detected.

Visual Regression

48 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 556,900
RichTextEditor Markdown parity: side by side probe light 538,816
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 28 more.


Generated by PR Enrichment workflow | Storybook | Sandbox | View full report

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Approval is on hold for a spec decision: the new constants table implements draft spec:AST-094 FR1–FR2, and that record (FR1–FR23 and DEC-1/DEC-2, carried here from #8052) has no engineering-owner approval yet. An engineering owner records the decision by commenting /approve-spec 28343b4215c178ba43c8f3fa9eb0478da1db995c on this pull request (the author may do so as an engineering owner), or asks for changes to the record. Independently of that decision, these need fixing:

  • The diff is 2,039 authored lines because it carries the #8052 record; landing #8052 first and rebasing brings it to 1,746.
  • MEASUREMENTS.md reports switch and context-menu captures as done in uikit/switch.ts and uikit/contextMenu.ts, which this branch does not contain, while springs.switchThumbMove and switchThumbStretch still wait on M-SW02 and M-SW03.
  • The shared gesture sources (UIKIT_LAB, PRESS_MEASUREMENT, SAFARI_SCROLL_CLAIM) carry no date, and the measured: pattern in uikit.test.ts accepts that.
  • Several headers name files that do not exist (sources.test.ts, packages/core/src/gestures/README.md), and springs.ts says most springs are guesses when 2 of 31 are.

[Automated review]

* Every leaf carries one or more `@source` (apple-docs | wwdc | webkit |
* measured | GUESS); webkit and GUESS values also carry the `@measure` task
* that will confirm or replace them (motion/README.md lists them all).
* sources.test.ts enforces both.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The check is uikit.test.ts; there is no sources.test.ts (the same name appears in scroll.ts:14 and timing.ts:14), and the measurement tasks are listed in MEASUREMENTS.md, not motion/README.md.

* that will confirm or replace them (motion/README.md lists them all).
* sources.test.ts enforces both.
*
* Most interaction springs below are GUESSES today: UIKit does not document

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

At this head 2 of the 31 springs entries are GUESS (segmentedThumb, banner); 20 cite a measurement, 7 Apple documentation and 2 WebKit. This paragraph, and the guesses wording at line 31, misstates the provenance the table exists to record.

* chosen for consistency with UIKit" (= response 0.30 s, ratio 0.75);
* its settling duration is WebKit's iOS switch duration 0.48801 s
* (RenderThemeIOS.h)
* @measure M-SW02

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

MEASUREMENTS.md:123-124 reports M-SW02 and M-SW03 as done, with the values in uikit/switch.ts, which this branch does not contain. Either move that note to the pull request that adds the switch table, or record the measured springs here and drop the @measure tags (README step 4).

Text Size (Bold Text row); a minimal host app holds a UISwitch on a plain view
where a switch outside any scroll view is needed.

Done (iOS 26.2 simulator, Oct 23 2026; in the switch's own table,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

uikit/switch.ts (and uikit/contextMenu.ts at line 254) does not exist at this head, so this note points readers at captures they cannot find while springs.switchThumbMove and switchThumbStretch still read as unconfirmed.

* tap-to-click, long press, double tap and the start of a page scroll, so
* many guesses can be settled by measuring Safari itself.
*
* SYNC: When modified, update packages/core/src/gestures/README.md (the

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

packages/core/src/gestures/README.md does not exist at this head (nor does ../../gestures, named at line 7), so this SYNC: line asks maintainers to update a file they cannot find.

* recognizer state log), iPhone 17 Pro simulator, iOS 26.2, Oct 2026.
*/
const UIKIT_LAB =
'measured: UIKit lab, iOS 26.2 simulator, ivar reads confirmed with HID touches';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This shared source has no capture date, unlike the measured: <app>, iOS <version>, <method>, <date> rule in motion/README.md:44; PRESS_MEASUREMENT and SAFARI_SCROLL_CLAIM lack one too, and 18 of the 20 sourced gesture entries use these three strings.

)
.sort();
const SOURCE_KINDS =
/^@source (apple-docs https:\/\/developer\.apple\.com\/|wwdc \d{4}-\d+ |webkit Source\/|measured: .*iOS \d|GUESS — )/;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The measured: branch only requires iOS <digit>, so measured: iOS 2 passes. Requiring the date the README rule names would catch the dateless gesture sources.

@astracat-bot astracat-bot Bot mentioned this pull request Oct 26, 2026
4 tasks done
vjeux added a commit to vjeux/astryx that referenced this pull request Oct 26, 2026
…into the animator's React hook and browser proofs

The motion table files keep this branch's copy, main's meta#8053 table plus the
chain's additions. The harness takes main's fail-closed compareCurve and its
tests beside meta#8446's modules, index and README.
vjeux added a commit to vjeux/astryx that referenced this pull request Oct 26, 2026
…a#8053) into the role-reading animator

The motion README takes meta#8053's opening with the animator added and keeps
this branch's file table; the harness takes main's fail-closed compareCurve
beside meta#8045's modules. The animator and its tests take the use names with
the next merge (meta#8067).
cixzhang added a commit to vjeux/astryx that referenced this pull request Oct 26, 2026
…n recognizer

# Conflicts:
#	packages/core/src/motion/MEASUREMENTS.md
#	packages/core/src/motion/uikit/gestures.ts
cixzhang added a commit to vjeux/astryx that referenced this pull request Oct 26, 2026
meta#8053's constants table is on main. Its nine motion files were add/add
conflicts against this branch's older copy: they take main's version, plus
this branch's own two velocity entries, which its tracker reads:
scrollReleaseWeight (0.25, interaction-modality INV10) and stillness (40 ms,
spec:AST-100 FR2), with their M-G14 and M-G16 rows. The tracker reads no
horizon, maxSamples or assumeStopped entry.
vjeux added a commit to vjeux/astryx that referenced this pull request Oct 26, 2026
Brings the touch foundation's current head, which folds the new main (meta#8053,
meta#8462), meta#8067, meta#8257 and F2's meta#8060: the canonical ci.yml and harness, F2's
FR14 removal and the AST-094 FR4 preset renames. The generated motion role
table and role kinds are regenerated (`pnpm generate:motion-roles`) with this
branch's switch table in them; the motion README keeps the foundation's
gesture row and adds the switch row.
vjeux added a commit to vjeux/astryx that referenced this pull request Oct 26, 2026
vjeux added a commit to vjeux/astryx that referenced this pull request Oct 26, 2026
meta#8076 folds the new main (with meta#8053's squash and meta#8462) and the strict
lint fix. The merge is clean; meta#8076's side stands for the foundation, the
harness, ci.yml and package.json.
vjeux added a commit to vjeux/astryx that referenced this pull request Oct 26, 2026
meta#8076's head folds main 99ad3f6 (meta#8053, meta#8462) into the motion
foundation, so the keyboard module no longer conflicts with main.

One conflict: motion.doc.mjs takes meta#8076's text and keeps this branch's
sentence that ChatLayout's Reduce Motion cross-fade reads
`timing.curves.move`. timing.ts keeps this branch's `keyboardNotification`
group, which meta#8076's next head adopts verbatim. The motion role table
regenerates unchanged (45 roles), as do the CLI goldens.
vjeux added a commit to vjeux/astryx that referenced this pull request Oct 26, 2026
vjeux added a commit to vjeux/astryx that referenced this pull request Oct 26, 2026
vjeux added a commit to vjeux/astryx that referenced this pull request Oct 26, 2026
meta#8270's newest head: cixzhang's 425922b and meta#8076 746871a (the new
main, meta#8053, meta#8060, the role naming). Resolutions:
- pressFeedback.ts keeps C01's press slots (meta#8118) and takes meta#8270's FR41
  narrowing: the press controller no longer locks text selection; a held
  finger suppresses it only where the hold does something, through the
  gesture host.
- The generated motion role table and role kinds are regenerated with the
  button, list and switch tables in them; the motion README keeps the
  foundation's rows and this branch's button, list and switch rows; the docs
  golden takes the motion topic's new skeleton hash.
vjeux added a commit to vjeux/astryx that referenced this pull request Oct 26, 2026
vjeux added a commit to vjeux/astryx that referenced this pull request Oct 26, 2026
vjeux added a commit to vjeux/astryx that referenced this pull request Oct 26, 2026
… this change

Takes meta#8076's ci.yml whole: the CheckboxList row press contract no longer
needs a workflow step of its own, since the pull-request accessibility run
finds CheckboxListPress.a11y.chromium.spec.ts by its file name and runs it
for a CheckboxList change (meta#8367).
vjeux added a commit to vjeux/astryx that referenced this pull request Oct 26, 2026
cixzhang pushed a commit that referenced this pull request Oct 26, 2026
…rena (#8058)

Depends on #8053 (and its predecessors); review the last commit.

<!-- Template: other.md (internal infrastructure: nothing is exported, so new-feature.md does not fit). -->

## Primary intent

Add the core of an internal gesture-recognizer system for touch presentations: UIKit's recognizer state machine and the arena that settles which recognizer owns a touch sequence. Recognizers, the DOM host and component migrations follow in their own pull requests; nothing is exported from the package.

## User or maintainer impact

Touch components each carry their own pointer handling (sheet drags, swipe actions, toast dismissal, long press, press feedback), with their own answer to "who owns this touch". Two can act on one touch, and a second finger can break a drag. With one arena every component gets UIKit's answers: exclusive recognition by default, `requireToFail`, simultaneous recognition only on request, a deterministic resolution order, and the view's touches cancelled (no click, no pressed state) once a gesture takes them.

## Problem and solution fit

- Why does it happen? Ownership of a touch is decided separately in each component.
- Why does that hurt? Ghost clicks, double actions, and rules that differ from component to component.
- Why does it matter? Touch presentations aim at UIKit parity, and UIKit settles this in one place.

Deltas:

- `GestureRecognizer`:
  - UIKit's states (possible, began, changed, ended, cancelled, failed) and allowed transitions;
  - discrete and continuous recognizers;
  - `requireToFail`, `recognizeSimultaneouslyWith`, `shouldBegin`, `shouldReceiveTouch`;
  - `cancelsTouchesInView`, `delaysTouchesBegan` and `delaysTouchesEnded`, `allowedPointerTypes`.
- `GestureArena`, with no DOM:
  - participants per touch sequence, resolution and claims;
  - delivery to the view;
  - click dispositions: allow, suppress, or defer and replay if the recognizer fails;
  - timers as deadlines on the event clock.

## Authority

`spec:AST-094` FR15 (arbitration) and FR16 (the event clock), proposed; pending owner review.

## Change and evidence

The arena's rules are tested with scripted recognizers, test doubles that begin, fail or wait on command. The rules hold independently of any real recognizer:

- exclusivity and simultaneity;
- require-to-fail: waiting, failing, delegate requirements, idle recognizers;
- delegate hooks and innermost resolution;
- independent touches;
- delivery to the view, deferred and replayed clicks, the browser taking a touch;
- cancellation;
- a deadline that runs at its own time before a late input.

Size: 2,198 lines, about 400 of them tests. This is one indivisible intent: the recognizer base and the arena call each other, so neither builds or tests alone, and the doubles-only tests are the smallest proof of the rules.

## Scope

- [x] This PR has one primary intent.
- [x] Independently removable deltas were removed or split.
- [x] Public text and artifacts contain no internal Meta context.

## Testing

- `pnpm exec vitest run --project ui packages/core/src/gestures packages/core/src/motion`: 159 tests passed.
- `tsc --project packages/core/tsconfig.json --noEmit`: clean.
- `eslint packages/core/src/gestures`: clean.
- `pnpm check:changesets`, `node scripts/release/changeset-coverage.mjs pr --base origin/main`: clean.
- Size: 6 files, 2,205 lines added.


Stack (merge in this order): #8052 spec → #8053 constants table → #8058 gesture core → #8059 tap and long press → #8060 pan → #8061 other recognizers → #8054 math → #8055 animator → #8056 animator curves; the gesture DOM host, press tracking and browser proof follow the animator.
vjeux added a commit to vjeux/astryx that referenced this pull request Nov 3, 2026
vjeux added a commit to vjeux/astryx that referenced this pull request Nov 3, 2026
…; the press release's curve) into the motion math

The README keeps this pull request's mention of the math beside meta#8053's
sentence on who reads the table.
vjeux added a commit to vjeux/astryx that referenced this pull request Nov 3, 2026
…he animator

The README keeps this pull request's animator beside meta#8053's sentence on
who reads the table, and this pull request's tests and animator take the
presets' use names (spec:AST-094 FR4).
vjeux added a commit to vjeux/astryx that referenced this pull request Nov 3, 2026
… and the velocity note

MEASUREMENTS.md takes meta#8053's table with the M-PR1 row added.
vjeux added a commit to vjeux/astryx that referenced this pull request Nov 3, 2026
vjeux added a commit to vjeux/astryx that referenced this pull request Nov 3, 2026
scroll.ts keeps naming the coast (coast.ts), which this pull request adds.
vjeux added a commit to vjeux/astryx that referenced this pull request Nov 3, 2026
vjeux added a commit to vjeux/astryx that referenced this pull request Nov 3, 2026
…olicy with meta#8055's decisions) into the animator's React hook and browser proofs

The README's file table keeps this pull request's hook and browser-proof
rows beside the coast; the browser proofs and the hook's test take the
presets' use names (spec:AST-094 FR4).
vjeux added a commit to vjeux/astryx that referenced this pull request Nov 3, 2026
…to the theme motion roles

scroll.ts and timing.ts keep this pull request's registry import beside
meta#8053's DELAYS_CONTENT_TOUCHES_MS and press export; springs.viewChange keeps
its own copy of UIKit's default spring, so each role has its own leaf; the
reference-reading note on springs.menuReducedMotion also says it is not a
theme motion role. The role table is regenerated under the use names, and
this pull request's tests and docs take them (spec:AST-094 FR4).
vjeux added a commit to vjeux/astryx that referenced this pull request Nov 3, 2026
vjeux added a commit to vjeux/astryx that referenced this pull request Nov 3, 2026
…8055's Reduce Motion decisions; every AST-094 FR4 role; the harness on main) into the role-reading animator

coast.ts resolves the edge spring under the element's theme
(scroll.bounce, the scroll table registered first); the animator and
reducedMotion.ts resolve the fade's curve, timing.curves.move, under it.
vjeux added a commit to vjeux/astryx that referenced this pull request Nov 3, 2026
vjeux added a commit to vjeux/astryx that referenced this pull request Nov 3, 2026

This branch was successfully deployed

1 active deployment
Preview — 0032cb6a Deployed Oct 25, 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.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants