Skip to content

spec(AST-108): the fluid character's motion tables - #9704

Draft
cixzhang wants to merge 2 commits into
mainfrom
spec/ast-108-fluid-character
Draft

cixzhang wants to merge 2 commits into
mainfrom
spec/ast-108-fluid-character

Conversation

@cixzhang

@cixzhang cixzhang commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

People using a theme with character: 'fluid' get motion that feels like the native platform: a flick keeps its speed into a measured spring, and entrances and moves take curves fitted to those springs. Every other character gets the same split, with its own curves and its own gesture spring.

Draft, stacked on main. This pull request introduces 'fluid' as the fourth character: the type value, both tables, and the gesture springs. It depends on #9663, which makes the theme's character and tempo reach the glide; it does not touch #9663's branch. Off the release path: this spec merges on its own, no release gate, plan, or changeset depends on it, and the build lands whenever it is ready.

Exact decision needed

What each character presets: the token table that non-gesture motion plays, and the spring that gesture motion plays, and what 'fluid' puts in both.

Existing authority searched

Decision

  • Gesture motion plays a spring; other motion plays the tokens (FR3a). On every pointer and at every character, the settle, snap, dismissal, or page after a drag, swipe, flick, or pan starts from the gesture's velocity on a spring. character sets its bounce and response; tempo scales its duration. Every other timed motion, the glide, swap, and edge reveal included, plays the character's easing token over its duration token. Finger tracking, momentum, and the rubber band stay fixed.

  • Each character is two tables (FR8).

    Token crisp standard expressive fluid
    --ease-entrance cubic-bezier(0, 0, 0.2, 1) cubic-bezier(0.24, 1, 0.4, 1) cubic-bezier(0.3, 1.2, 0.5, 1) cubic-bezier(0.31, 0.35, 0.12, 1.12) (menu spring's shape)
    --ease-move same as entrance same same cubic-bezier(0.23, 0.17, 0.2, 0.99) (settle spring's shape)
    --ease-exit unchanged unchanged unchanged unchanged
    --ease-standard unchanged unchanged unchanged unchanged
    --duration-* base × tempo base × tempo base × tempo base × tempo
    Gesture spring crisp standard expressive fluid
    from velocity bounce −0.1, response 0.239 s bounce 0, response 0.293 s bounce 0.2, response 0.286 s the gesture's measured spring
  • How the fluid curves are made. Each is its spring's travel from rest over the spring's 99 % settle time (347 ms for the menu spring, 528 ms for the settle spring), fitted as a cubic Bézier: maximum error 1.9 % and 1.4 % of the travel. A cubic Bézier keeps the token's current form and works in every browser; linear() would add nothing visible at that error.

  • How the gesture springs are made. From rest, each crisp, standard, and expressive spring stays within 1 % of its travel after --duration-medium-min (310 ms), the glide's length. Standard does not overshoot, crisp is overdamped, expressive overshoots about 1.5 %, as its curve does.

  • Reduce Motion is the same at every character: no spring, no travel; a cross-fade of 200 ms or less under a coarse pointer, an immediate change otherwise; a throw still coasts.

  • No engine switch. The glide, swap, and edge reveal are non-gesture motion, so at 'fluid' they play fluid's spring-shaped move token over --duration-medium-min, the same way they play every other character's token (spec(AST-105, AST-108): glides follow the theme's character and tempo; the measured spring becomes character 'native' #9663). No character switches non-gesture motion to the spring engine.

Rejected alternatives

  • Duration tokens taken from the springs' settle times: every motion under a mouse would get longer, and tempo already owns length.

Claim scope and non-goals

  • No change to --ease-exit, --ease-standard, the duration scale, the press clocks, or the fixed list.
  • spec:AST-094's pointer wording is out of scope here.
  • No implementation, changeset, or release-plan entry.

Evidence and implementation relationship

Values computed from the springs' closed forms (SwiftUI duration and bounce, response and damping ratio), settle time at 1 % of travel, and a cubic-Bézier fit minimizing the maximum error. Engine requirement for #8677: the knob reader must accept 'fluid'; at 'fluid' a gesture plays its measured spring, and at the other characters it plays the character's normalized spring, both from the gesture's velocity; no non-gesture motion switches engines. themeMotion.ts's curve table must let --ease-entrance and --ease-move differ.

Writing rubric results

  • R1 narration: none; the new lines describe the system in the present tense and are marked proposed.
  • R2 research in the record: none; the fits and settle-time numbers stay in this description, and the record keeps only the derived values and the 2 % bound.
  • R3 evidence naming a change: none.
  • R4 rejected alternatives over one line or outside a decision: one line in DEC-3.
  • R5 pull request or issue references remaining: none in the record.
  • R6 ruled open questions still listing options: none.

Scope

  • This PR contains specification records only.
  • It records observable behavior and preserves equivalent internal implementations.
  • Any named internal mechanism is intentionally public, names its dependent caller/system, and explains why an equivalent implementation would not satisfy the contract.
  • Verification states the evidence layer and failure signal without prescribing CI job/workflow topology.
  • It records one intentional durable decision, not a rescue for a separable tagalong.
  • The canonical knowledge owner, affected current claims, and overlapping open work are named.
  • Adjacent API, visual, accessibility, composition, and implementation facts remain outside scope unless this exact decision depends on them.
  • Public text and artifacts contain no internal Meta context.

Validation

  • pnpm check:knowledge: aligned
  • Prettier: clean
  • git diff --check: clean

@vercel

vercel Bot commented Nov 3, 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 Nov 3, 2026 4:50am UTC

Request Review

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Meta Open Source bot. label Nov 3, 2026
@github-actions github-actions Bot added the needs:spec-owner-review Current knowledge records await owner approval label Nov 3, 2026
@astracat-bot

astracat-bot Bot commented Nov 3, 2026 •

Copy link
Copy Markdown

AI review status for this pull request.

Review status Updated
❓ Needs maintainer input (for maintainers only) Nov 3, 2026, 5:16 AM UTC

@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 of this spec change waits on an engineering owner's decision on the proposed motion direction in spec:AST-108 FR3a, FR8, DEC-3 and DEC-4. Those clauses replace each gesture's measured release spring with one spring per character at the default, move touch entrances and the animated-layout glide onto the token table, and add 'fluid'. That decision also covers the amendments they need to AST-108 FR1/FR3/FR5, AST-094 FR1/FR4 and AST-105 FR4. An engineering owner records it by commenting /approve-spec <full head SHA> on this pull request; the author may do so as an engineering owner.

Separately from that decision, the record needs these fixes:

  • The FR8 verification row (line 264) still fails "a glide that plays a curve" and "a touch role off its measured value", although FR8 and FR3a now have the glide and touch entrances play the token table.
  • The settle-time definition (lines 187–190) does not match 347 ms for springs.menuOpen: by "stays within 1 %" the spring settles at about 324 ms, where the fluid entrance curve is about 6 % off, so state the envelope-bound window the 347 ms comes from.
  • FR1, FR3 and FR5 and their verification rows still say three characters, measured springs at 'standard', and unchanged existing themes, so rewrite them here (marked proposed) instead of noting that approval amends them.
  • The Current-state impact should also list architecture:theme-authoring-contract INV15, AST-094 FR1 and AST-105 FR4 as records to amend, not only AST-094 FR4.
  • The Reduce Motion bullet (lines 205–206) lands every non-touch release at once, but the reduced-motion design record cross-fades a trackpad-started release on a coarse primary pointer.

[Automated review]

| FR5 | Theme tests with the released configurations | a `MotionScaleConfig` theme, explicit tokens, an extending theme; an adaptation rule that sets `tempo` or `character` | changed output for an existing theme, or a knob that beats an explicit field, fails |
| FR6 | Server-render tests and import checks | a server render with each `reducedMotion`; importing the theme and motion modules; `'always'` with the person's setting off, across components that key on `prefers-reduced-motion`; a nested theme under an `'always'` theme | a DOM access at import or on the server, theme CSS emitted for `'system'`, a hydration change, or a component style, script reader, or `when: {motion}` rule that ignores the root mark under `'always'`, or a nested theme that restores motion under an ancestor's `'always'`, fails |
| FR7 | Docs review | each listed doc | a doc without the knobs, the fixed list, or the precedence fails |
| FR8 (proposed) | Theme unit tests over generated CSS, resolver tests, and browser frame capture | `'fluid'` at each `tempo` bound and 1; each table row; a glide under a mouse and a finger; Reduce Motion under a coarse and a fine pointer | a fluid curve more than 2 % of the travel off its spring, a CSS exit, fade, or duration token that differs from `'standard'`, a touch role off its measured value, a glide that plays a curve, or a spring under Reduce Motion fails |

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 row still fails "a touch role off its measured value" and "a glide that plays a curve", but FR8's token table (line 173), the impact line at 242–244 and FR3a (103–107) now have the glide play --ease-move and non-gesture touch roles play the token table. Suggest "a glide that plays a spring" and "a fluid gesture spring off its measured value".

Comment on lines +187 to +190
- A fluid curve is its spring's travel from rest over the spring's 99 %
settle time (the time after which it stays within 1 % of its travel:
347 ms for `springs.menuOpen`, 528 ms for `springs.settle`), fitted as a
cubic Bézier within 2 % of the travel at every point. The spring's

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

By "the time after which it stays within 1 % of its travel", springs.menuOpen (response 0.32 s, damping ratio 0.8) settles at about 324 ms. 347 ms is the decay-envelope bound the spring model's settle time returns. Over 324 ms, cubic-bezier(0.31, 0.35, 0.12, 1.12) is about 6 % off the spring, beyond the 2 % bound, so a test built from this definition rejects the table's curve. Define the window as the envelope bound, or state 347 ms without the gloss.

Comment on lines +236 to +241
- FR3a and FR8 are proposed. When approved, they amend FR1 (a fourth
`character`), FR3 (at `'standard'`, a gesture plays the standard gesture
spring, not its measured one, and a non-gesture motion plays the token
table on every pointer), and FR5 (an existing theme's gesture springs and
touch timed motion change with that default); `spec:AST-094` FR4 states the
pointer side.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

FR1 (line 65), FR3 (90–92: "every role plays its measured value exactly"), FR5 (134–135: "springs included") and their verification rows (257, 261) still say the opposite of FR8's standard gesture spring. Approving this head would approve both. Rewrite those clauses and rows here, marked proposed.

Comment on lines +245 to +250
- `defineTheme` does not accept `'fluid'` yet. It needs `spec:AST-105` FR4 to
time the glide from the theme's tokens, the animator to read the theme's
knobs at each gesture's start, and the character's curve table to set
`--ease-entrance` and `--ease-move` to different values. Today the glide
plays `springs.settle` at every character, no spring reads the knobs, and
no Core component reads `--ease-entrance` or `--ease-move`.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

architecture:theme-authoring-contract INV15 (three characters), AST-094 FR1 ("MUST default to the measured native value") and AST-105 FR4 (the glide reads springs.settle) also need amending for FR3a and FR8. Please list them with the change each needs.

Comment on lines +202 to +206
- Reduce Motion is the same at every character (`design:reduced-motion`
DR1–DR3): no spring plays and nothing travels; a coarse primary pointer
gets a cross-fade of 200 ms or less, any other pointer an immediate
change; a throw still coasts, and a release that snaps, dismisses, or
pages cross-fades after a touch drag and lands at once otherwise.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

"lands at once otherwise" departs from the reduced-motion design record, which has a release a trackpad started follow DR2 (cross-fade) on a coarse primary pointer. Suggest "cross-fades after a touch drag or under a coarse primary pointer, and lands at once otherwise", or cite DR2 and DR3 without restating them.

This branch was successfully deployed

1 active deployment
Preview — af24d7b2 Deployed Nov 3, 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:spec-owner-review Current knowledge records await owner approval

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant