Skip to content

chore(core): the touch motion tables register FR4's roles as internal motion roles - #8257

Merged
cixzhang merged 56 commits into
meta:mainfrom
vjeux:uikit/f1-theme-motion-roles
Oct 26, 2026
Merged

cixzhang merged 56 commits into
meta:mainfrom
vjeux:uikit/f1-theme-motion-roles

Conversation

@vjeux

@vjeux vjeux commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

Maintainer impact

Touch presentations play springs, durations and curves from the measured UIKit tables. A few of these values are the motion roles spec:AST-094 FR4 names for their use (springs.settle, timing.stateChangeMs, timing.curves.move, …). This pull request gives those roles one internal registry. No public surface changes: themes don't set motion roles (theme tokens are never component-specific, and the public motion-token set is decided separately), and no shipped component reads the roles yet.

Intended invariant

The registered roles are exactly the 17 FR4 names. Every other table value, a component's measured spring included, is an internal constant that plays as measured.

Change

  • registerMotionTable makes a role only of the values a table lists in roles. The shared springs and timing tables list FR4's names; the scroll and navigation tables list none.
  • Each spring is its own export (SHEET_PRESENT_SPRING, SETTLE_SPRING, …), a literal a bundler can drop when nothing imports it, and springs composes them, so a component bundles only the springs it plays (architecture:component-style-authoring INV6). Registering the roles takes only FR4's twelve springs.
  • A role is carried by a CSS custom property (--motion-springs-settle) that no public API writes. An empty property means the table's value.
  • defineTheme({motion}) keeps only the duration scale, as on main. A test locks the theme entry and the package root against a motion-role surface.
  • [chore] changeset; the motion README and roles.ts describe the internal roles.

Evidence

  • The registry test holds the registered roles to FR4's table, read from the spec.
  • Every role round-trips through its CSS form (springs to 1e-5), and a spring that never settles is refused.
  • The theme-surface test fails if a motion role reappears in @astryxdesign/core/theme or the root.
  • A bundle test imports one spring, and registers the roles, and finds no other spring; it fails on the single-object table.

Scope

  • No intended public API, product behavior, visual direction, or policy change.
  • Product changes discovered during the work were removed or split.
  • Public text and artifacts contain no internal Meta context.

Testing

  • pnpm exec vitest run --project ui packages/core/src/motion packages/core/src/theme: green.
  • pnpm build, pnpm -F @astryxdesign/core typecheck, the node suites (stable-output goldens included) and pnpm check:repo: green.

@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 26, 2026 6:53am UTC

Request Review

@astracat-bot

astracat-bot Bot commented Oct 24, 2026 •

Copy link
Copy Markdown

AI review status for this pull request.

Review status Updated
🟢 Reviewed (for maintainers only) Oct 26, 2026, 7:15 AM UTC

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Meta Open Source bot. label Oct 24, 2026
@vjeux
vjeux force-pushed the uikit/f1-theme-motion-roles branch from ecd60ce to 765a094 Compare October 24, 2026 16:40

@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.

This pull request needs an engineering owner's decision before it can be approved: theme motion roles (defineTheme({motion: {springs, durations, curves}}) writing --motion-<role> variables whose defaults live in the motion tables) and the new platform adaptation condition go beyond architecture:theme-tokens INV1 and spec:AST-012 FR2, and at this head the draft spec:AST-094 FR4 and DEC-1 still say touch motion is not themable. An engineering owner records that decision by commenting /approve-spec <full head SHA> on this pull request once the head carries the intended FR4 (the author may do so as an engineering owner), or by updating those records on main. These fixes are needed either way:

  • The touch-motion docs and the [feat] changeset say components read the roles, but nothing reads a --motion-* variable at this head.
  • Through @astryxdesign/core/theme alone, role validation never runs, so a misspelled or mis-grouped role id is accepted silently (expandMotionRoles.ts:140).
  • Add built-theme and duplicate-condition coverage for platform rules, and rebase once #8052, #8053 and #8054 land so this carries only its own commit.

[Automated review]

`${location}: a motion role is named by its table path, such as "sheet.present" or "springs.navigationPush".`,
);
}
const registered = motionRole(id);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Through @astryxdesign/core/theme alone no motion table is registered when this runs, so motionRole(id) is always undefined: durations: {'springs.sheetPresent': 300} and a misspelled springs.sheetPresnt are both accepted and emitted. The refusal test passes only because expandMotionRoles.test.ts imports ../motion/uikit first. Register the shared tables where this validation runs and test without that import.

Comment thread packages/cli/assets/docs/motion.doc.mjs Outdated
},
{
type: 'prose',
text: 'A theme writes one CSS variable per role it sets, `--motion-` followed by the path in kebab case: a spring as its duration and bounce (`0.6s 0.1`), a duration in milliseconds, a curve as `cubic-bezier()`. Components read the variable when a motion starts and fall back to the default when it is empty. Gesture thresholds, the delays that decide whether a touch scrolls or presses, and haptics are not motion roles. The `platform` condition matches iOS and iPadOS browsers (`ios`) or every other browser (`other`).',

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

At this head no component reads a --motion-* variable (nothing outside tests imports the motion tables), so "Components read the variable when a motion starts" and the section's opening sentence describe behavior that does not exist yet. Ship this section with the change that makes a component read the roles, or say here that none does yet.

measures (0.55 for a scroll view and a sheet), and a release MUST pick its
rest point by projecting its velocity the way its UIKit counterpart does:
with a deceleration rate, or with a measured projection time.
- **FR4 — Physics are not themable in touch presentations.** A touch

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This FR4, with DEC-1 at line 271, says touch physics are not themable: the opposite of the theme motion roles this pull request adds. The revised FR4 is only on the spec pull request; rebase onto it so the record and the code agree before owners decide.

@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 34.2KB 34.2KB 0B

Accessibility Audit

Status: No accessibility violations detected.

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

@cixzhang
cixzhang merged commit 61480b5 into meta:main Oct 26, 2026
66 checks passed
@vjeux
vjeux deleted the uikit/f1-theme-motion-roles branch October 26, 2026 08:20
cixzhang added a commit to vjeux/astryx that referenced this pull request Oct 26, 2026
…to the motion-role animator

# Conflicts:
#	packages/core/src/motion/README.md
#	packages/core/src/motion/roles.test.ts
#	packages/core/src/motion/uikit/timing.ts
#	packages/core/src/motion/uikit/uikit.test.ts
vjeux added a commit to vjeux/astryx that referenced this pull request Oct 26, 2026
…plays its table value) into the experimental touch exports

Four files both sides changed: coast.ts takes the animator's wording (the
edge spring plays its table value); useAnimator.ts takes its registration
line and keeps this side's note that a component may play its own measured
spring; roles.test.ts takes the menu spring's motionRoleOf check beside this
side's keyboard checks; resolveMotion.test.ts takes the animator's tests and
keeps this side's coast test (scroll.bounce is no role).
vjeux added a commit to vjeux/astryx that referenced this pull request Oct 26, 2026
…; a motion role plays its table value) into the presented start

The animator's browser proof: its role check takes the inverted
expectation (both boxes play the table's spring) and keeps this side's
0.26 px tolerance for a start at the commit.
cixzhang added a commit to vjeux/astryx that referenced this pull request Oct 26, 2026
…motion clock and Reduce Motion plan

The motion README takes main's internal-roles sentence with this layer's
animator; uikit.test.ts takes main's version (rows, docAbove) and keeps the
check of values bound from an exported group, which timing.ts's Reduce
Motion fade bindings need.
cixzhang 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
…nternal) into this change

# Conflicts:
#	packages/core/src/motion/README.md
#	packages/core/src/motion/uikit/index.ts
cixzhang added a commit to vjeux/astryx that referenced this pull request Oct 26, 2026
…nimator

# Conflicts:
#	packages/core/src/motion/README.md
#	packages/core/src/motion/uikit/uikit.test.ts
cixzhang added a commit to vjeux/astryx that referenced this pull request Oct 26, 2026
…ard module

# Conflicts:
#	packages/core/src/BottomSheet/BottomSheetPanel.tsx
vjeux added a commit to vjeux/astryx that referenced this pull request Oct 26, 2026
…plays its table value) into uikit/c09-search-table
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
…keyboard notification export

uikit/uikit.test.ts: main's leavesOf (a one-line export is a row, sourced
or not) keeps this branch's check of values bound from an exported group
(const {curve: move} = group). uikit/timing.ts merged cleanly.
@vjeux

vjeux commented Oct 26, 2026

Copy link
Copy Markdown
Contributor Author

Since you asked for my take on tokenizing touch motion: do as you want. The public motion-token set is your call, and we'll implement whichever set you choose. Until then touch motion stays internal and defineTheme keeps only the duration and easing scale, as your 03:35 comment says.

If it's useful as input, the measured touch values and the FR4 roles they map to are in #8502's tables: springs, and durations and curves.

This branch was successfully deployed

1 active deployment
Preview — 10fcb7b2 Deployed Oct 26, 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