Skip to content

feat(ProgressBar): iOS Safari's indeterminate bar under a finger - #8239

Draft
vjeux wants to merge 864 commits into
meta:mainfrom
vjeux:uikit/c07-progressbar-indeterminate
Draft

vjeux wants to merge 864 commits into
meta:mainfrom
vjeux:uikit/c07-progressbar-indeterminate

Conversation

@vjeux

@vjeux vjeux commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

Depends on #8225. Review only this PR's change: vjeux/astryx@bd4ecc6...9f35484

User impact

Part of making Astryx match iOS under a finger (spec:AST-094): an indeterminate ProgressBar slides a 40% segment in an eased sweep, a desktop pattern. iOS's only indeterminate bar is Safari's valueless <progress>: a quarter of the track sweeping linearly, 1 s each way. ProgressBar now draws that on touch devices, and keeps moving on a busy page (Safari's freezes).

Options and tradeoffs:

  • Keep the desktop sweep: nothing on iOS moves like it.
  • UIKit's spinner instead: a different shape.
  • Safari's drawing (this PR). Its Reduce Motion pulse is too faint (1.52:1), so this one pulses brighter (0.74 to 1.0 alpha): accent, success and error reach 3:1; warning and neutral don't.

Visual authority

  • Existing component/module DD row reused or amended.
  • Durable component-local intent added to the optional Design decisions table by a team design owner.
  • Correction already settled by current family, design, theme, or objective accessibility authority.
  • New shared or cross-component direction settled in docs/design/.

None is settled. Proposed, pending owner review: Safari's <progress> under a finger, in the theme's colors and radius, with the brighter pulse (spec:AST-020's 3:1).

Before and after

This bar (top) and Safari's <progress> (bottom), iOS 26.2 simulator:
The bar and Safari's own progress sweeping

Right to left, and Reduce Motion:
The sweep right to left The Reduce Motion pulse, 0.74 to full opacity

A 0.7 s busy script, without and with Reduce Motion:
The bar moving and Safari's own frozen during the block
With Reduce Motion, the pulse going on and Safari's own frozen through the block

Android, through a busy script:
The sweep on Android through a block

State matrix

  • Sweep: a quarter of the track, linear, 1 s each way; mirrored right to left.
  • Reduce Motion: full width, pulsing.
  • A mouse: unchanged.

Size (gzip, ProgressBar's import, #8225 → head): JavaScript 9,450 → 9,554 B (+104), CSS 7,137 → 7,400 B (+263); astryx.css 35,719 → 36,013 B (+294).

Scope

  • Appearance is the primary intent.
  • Behavior, API, interaction model, and unrelated layout changes were removed or split.
  • The evidence shows the changed pixels actively running, not only source or computed styles.
  • Public text and artifacts contain no internal Meta context.

Testing

What I ran and what it showed:

  1. Beside Safari's <progress> on the iOS 26.2 simulator: the same segment and 1 s reversal; through a 0.7 s busy script, this bar moves and Safari's freezes. Android emulator: the same.
  2. Chromium tests under a coarse pointer: each state above, a busy page, the track behind the sweep, and the pulse's contrast in light and dark.
  3. A test for the track behind the bar, which an earlier version erased; it failed before the fix.

@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 29, 2026 10:56pm 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
❓ Needs maintainer input (for maintainers only) Oct 29, 2026, 11:13 PM UTC

@github-actions github-actions Bot added the needs:design-review Affects visuals — Design should review label Oct 24, 2026
@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Meta Open Source bot. label Oct 24, 2026
@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

Modified Components

ProgressBar (@astryxdesign/core) · View in Storybook
Metric Before After Delta
Entry File (ESM) 455B 455B 0B
Lines of Code 387 570 -
Complexity High Very High (33) -

Component Size

Component Import before Import after Delta CSS before CSS after Delta
core/AlertDialog 56.9KB 57.0KB +117B 12.4KB 12.4KB 0B
core/AppShell 57.9KB 58.0KB +90B 11.6KB 11.6KB 0B
core/AvatarGroupOverflow 39.9KB 40.0KB +125B 6.4KB 6.4KB 0B
core/Banner 47.8KB 47.9KB +123B 9.9KB 9.9KB 0B
core/BaseTypeahead 54.3KB 54.4KB +111B 9.9KB 9.9KB 0B
core/BreadcrumbItem 66.4KB 66.5KB +105B 11.0KB 11.0KB 0B
core/Button 43.3KB 43.4KB +102B 9.2KB 9.2KB 0B
core/Calendar 54.9KB 55.0KB +110B 10.0KB 10.0KB 0B
core/Carousel 50.2KB 50.3KB +82B 10.2KB 10.2KB 0B
core/ChatComposer 60.6KB 60.7KB +100B 10.8KB 10.8KB 0B
core/ChatComposerInput 55.7KB 55.8KB +99B 10.1KB 10.1KB 0B
core/ChatComposerTokenElement 55.7KB 55.8KB +100B 10.1KB 10.1KB 0B
core/ChatDictationButton 46.5KB 46.6KB +107B 9.5KB 9.5KB 0B
core/ChatLayout 48.8KB 48.9KB +104B 9.9KB 9.9KB 0B
core/ChatLayoutScrollButton 46.4KB 46.5KB +84B 9.6KB 9.6KB 0B
core/ChatSendButton 45.8KB 45.9KB +96B 9.2KB 9.2KB 0B
core/CheckboxInput 49.9KB 50.0KB +105B 9.3KB 9.3KB 0B
core/CheckboxListItem 54.0KB 54.1KB +92B 10.5KB 10.5KB 0B
core/ClickableCard 12.8KB 12.9KB +117B 6.9KB 6.9KB 0B
core/CodeBlock 60.5KB 60.6KB +106B 10.7KB 10.7KB 0B
core/Collapsible 11.7KB 11.8KB +113B 4.2KB 4.2KB 0B
core/CommandPalette 50.9KB 51.0KB +104B 11.8KB 11.8KB 0B
core/CommandPaletteItem 8.4KB 8.5KB +110B 3.5KB 3.5KB 0B
core/ComplexSelector 55.8KB 55.9KB +132B 10.6KB 10.6KB 0B
core/ContextMenu 77.7KB 77.8KB +87B 14.6KB 14.6KB 0B
core/ContextMenuItem 16.6KB 16.7KB +117B 5.2KB 5.2KB 0B
core/DateInput 99.9KB 100.0KB +88B 14.8KB 14.8KB 0B
core/DateRangeInput 69.4KB 69.5KB +103B 11.2KB 11.2KB 0B
core/DateTimeInput 110.1KB 110.2KB +77B 15.2KB 15.2KB 0B
core/DialogHeader 48.8KB 48.9KB +101B 9.8KB 9.8KB 0B
core/DrawerHeader 48.9KB 49.0KB +97B 11.2KB 11.2KB 0B
core/DropdownMenu 72.7KB 72.8KB +106B 14.7KB 14.7KB 0B
core/DropdownMenuCheckboxItem 16.1KB 16.2KB +106B 5.6KB 5.6KB 0B
core/DropdownMenuItem 15.4KB 15.5KB +101B 5.2KB 5.2KB 0B
core/DropdownMenuRadioItem 16.2KB 16.3KB +110B 5.6KB 5.6KB 0B
core/DropdownMenuSubMenu 58.1KB 58.2KB +95B 9.5KB 9.5KB 0B
core/FileInput 54.1KB 54.2KB +102B 10.3KB 10.3KB 0B
core/IconButton 43.3KB 43.4KB +113B 9.2KB 9.2KB 0B
core/Item 11.4KB 11.5KB +98B 4.7KB 4.7KB 0B
core/LayerProvider 63.3KB 63.4KB +121B 10.1KB 10.1KB 0B
core/Lightbox 51.2KB 51.3KB +95B 9.8KB 9.8KB 0B
core/Link 43.5KB 43.6KB +112B 7.6KB 7.6KB 0B
core/ListItem 53.5KB 53.6KB +103B 10.5KB 10.5KB 0B
core/Markdown 144.5KB 144.5KB +79B 17.4KB 17.4KB 0B
core/MobileNav 51.0KB 51.1KB +97B 10.1KB 10.1KB 0B
core/MobileNavToggle 46.2KB 46.3KB +104B 9.4KB 9.4KB 0B
core/MoreMenu 72.9KB 73.0KB +103B 14.7KB 14.7KB 0B
core/MultiSelector 76.2KB 76.3KB +110B 14.6KB 14.6KB 0B
core/NumberInput 55.7KB 55.8KB +108B 10.7KB 10.7KB 0B
core/Outline 28.6KB 28.7KB +113B 4.0KB 4.0KB 0B
core/Pagination 87.7KB 87.8KB +105B 15.3KB 15.3KB 0B
core/Popover 49.8KB 49.9KB +84B 12.1KB 12.1KB 0B
core/PowerSearch 155.1KB 155.1KB +87B 18.6KB 18.6KB 0B
core/ProgressBar 7.7KB 9.3KB +1.6KB 6.6KB 7.2KB +626B
core/RadioListItem 52.1KB 52.3KB +111B 9.7KB 9.7KB 0B
core/SegmentedControlItem 8.6KB 8.7KB +116B 3.9KB 3.9KB 0B
core/SelectableCard 12.8KB 12.9KB +114B 7.1KB 7.1KB 0B
core/Selector 79.1KB 79.2KB +81B 15.0KB 15.0KB 0B
core/SelectorOption 14.5KB 14.6KB +97B 5.1KB 5.1KB 0B
core/SideNav 58.0KB 58.1KB +97B 10.6KB 10.6KB 0B
core/SideNavCollapseButton 46.6KB 46.7KB +90B 9.5KB 9.5KB 0B
core/SideNavHeading 54.7KB 54.8KB +96B 10.1KB 10.1KB 0B
core/SideNavItem 54.5KB 54.6KB +97B 10.1KB 10.1KB 0B
core/Step 32.4KB 32.5KB +118B 5.3KB 5.3KB 0B
core/Stepper 47.4KB 47.5KB +106B 9.6KB 9.6KB 0B
core/Switch 48.4KB 48.5KB +111B 9.3KB 9.3KB 0B
core/Tab 9.7KB 9.8KB +119B 4.1KB 4.1KB 0B
core/TabMenu 51.6KB 51.7KB +83B 10.0KB 10.0KB 0B
core/Table 84.0KB 84.0KB +71B 15.1KB 15.1KB 0B
core/TableCell 78.8KB 78.9KB +89B 14.8KB 14.8KB 0B
core/TableHeaderCell 78.6KB 78.7KB +92B 14.8KB 14.8KB 0B
core/TableSelectionToolbar 52.4KB 52.5KB +98B 11.2KB 11.2KB 0B
core/TextInput 53.5KB 53.6KB +100B 10.5KB 10.5KB 0B
core/Thumbnail 50.7KB 50.8KB +103B 11.0KB 11.0KB 0B
core/TimeInput 86.8KB 86.9KB +83B 13.8KB 13.8KB 0B
core/Timestamp 27.5KB 27.5KB 0B 9.5KB 9.5KB 0B
core/Toast 59.8KB 59.9KB +105B 9.7KB 9.7KB 0B
core/ToggleButton 44.1KB 44.2KB +91B 9.3KB 9.3KB 0B
core/Token 29.6KB 29.7KB +111B 4.5KB 4.5KB 0B
core/Tokenizer 64.8KB 64.9KB +114B 11.7KB 11.7KB 0B
core/TopNav 51.9KB 52.0KB +104B 10.4KB 10.4KB 0B
core/TopNavHeading 54.2KB 54.3KB +101B 9.9KB 9.9KB 0B
core/TopNavItem 9.0KB 9.2KB +116B 3.8KB 3.8KB 0B
core/TopNavMegaMenu 54.2KB 54.3KB +121B 10.8KB 10.8KB 0B
core/TopNavMegaMenuItem 9.1KB 9.2KB +119B 4.1KB 4.1KB 0B
core/TopNavMenu 53.6KB 53.7KB +97B 10.1KB 10.1KB 0B
core/TreeList 32.5KB 32.6KB +111B 4.7KB 4.7KB 0B
core/Typeahead 61.5KB 61.6KB +101B 11.3KB 11.3KB 0B

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
Package stylesheet Gzip before Gzip after Delta
@astryxdesign/core 34.6KB 35.2KB +632B

Accessibility Audit

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

ProgressBar - 1 issue(s)
  • 🟠 serious: Ensure the contrast between foreground and background colors meets WCAG 2 AA minimum contrast ratio thresholds
    • Rule: color-contrast · Affects 2/18 stories · Learn more
    • WCAG: 1.4.3 (Level AA)
Progressbar - 1 issue(s)
  • 🟠 serious: Ensure the contrast between foreground and background colors meets WCAG 2 AA minimum contrast ratio thresholds
    • Rule: color-contrast · Affects 1/1 stories · Learn more
    • WCAG: 1.4.3 (Level AA)

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,816
Chat Full AI Chat probe dark 229,799
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,404
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.

This ProgressBar touch change needs an engineering-owner decision before it can be approved: it rests only on the draft spec:AST-094, so an engineering owner, the author included, can record approval of that direction (the touch indeterminate sweep, its Reduce Motion pulse and its timing outside the theme's motion tokens) by commenting /approve-spec <full head SHA>, or point to another governing spec. Whatever that decision, two changes are needed:

  • Land the stacked predecessors first (or retarget) so this pull request carries only its own 461-line commit instead of 16,939 authored lines.
  • Under a coarse pointer, a value that reverses to the one it just left animates in half the stated time (400 ms instead of 800 ms), because CSS shortens reversed transitions.

[Automated review]

transitionProperty: 'transform',
transitionDuration: {
default: durationVars['--duration-medium'],
'@media (pointer: coarse)': 'var(--_progress-bar-duration, 0s)',

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Under a coarse pointer, a change that reverses to the value it just left runs at half the duration written here. CSS shortens a transition that reverses to its own start value, so 10 → 90 paused at 50% and sent back to 10 runs 400 ms although --_progress-bar-duration is 800 ms (433 ms when reversed live at 52%); a retarget to any other value keeps its full duration. Keep reversals off the browser's reversing path, or document that they follow the drawn distance, and add the case to ProgressBarTouch.a11y.chromium.spec.ts.

follows.
- **FR7 — Exits animate as UIKit's do.** A touch presentation MUST play the
exit its UIKit counterpart plays. **Proposed; pending owner review:** this
departs from the draft `spec:AST-090` FR1 (exits off by default) and FR10

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-090 is not in this head or on main (it is proposed in open #8003), so FR7 asks owners to amend text that is not in the repository. Land AST-090 first, or restate FR7 against records present at the head.

@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 ProgressBar touch change needs an engineering-owner decision before it can be approved: it rests only on the draft spec:AST-094, so an engineering owner (the author included, if they are one) can record approval of that direction (the touch sweep, its trackless Reduce Motion pulse and its timing outside the theme's motion tokens) by commenting /approve-spec <full head SHA>, or point to another governing spec. Whatever that decision, these changes are needed:

  • trackIndeterminate sets default: null, which removes the grey track from every indeterminate ProgressBar, with a mouse too; keep the default colour in the override and test the mouse path.
  • Land the stacked predecessors first (or retarget) so this pull request carries only its own 500-line commit instead of 16,976 authored lines.
  • Under a coarse pointer, a value that reverses to the one it just left animates in half the stated time (400 ms instead of 800 ms).
  • AST-094 FR7 amends spec:AST-090, which is not in the repository.

[Automated review]

// fill's rules under it; the compiled CSS kept both.)
trackIndeterminate: {
backgroundColor: {
default: null,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

stylex.props() merges by property, so this backgroundColor (a null default plus one media key) replaces track.backgroundColor whole: every indeterminate track loses --color-background-muted, with a mouse and with a finger, not only under coarse pointer plus Reduce Motion. On CI's static Storybook build of this head the Indeterminate story's track computes to rgba(0, 0, 0, 0) with a mouse, against rgb(212, 212, 212) on the base. Keeping the default in the override (default: colorVars['--color-background-muted']) and asserting the track colour on the mouse and touch-sweep paths would fix and guard it.

@github-actions github-actions Bot removed the needs:design-review Affects visuals — Design should review label Oct 24, 2026

@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 ProgressBar change needs an engineering-owner decision before it can be approved: it rests only on the draft spec:AST-094, so an engineering owner (the author included, if they are one) can record approval of that direction for the iOS bar (the quarter-track sweep, the trackless Reduce Motion pulse and its theme-retunable timing) by commenting /approve-spec <full head SHA>, or point to another governing spec. Whatever that decision, these changes are needed:

  • trackIndeterminate keeps default: null, which removes the grey track from every indeterminate ProgressBar, with a mouse and on Android too; keep the track colour as the default and test those paths.
  • Land the stacked predecessors first (or retarget) so this pull request carries only its own two commits instead of 19,658 authored lines.
  • The iOS gate infers the engine from -webkit-touch-callout, which spec:AST-013 FR6 does not allow, and the theme platform condition is outside spec:AST-012 FR2's closed vocabulary, so each needs its record amended first or has to go.
  • AST-094 FR4 and DEC-1 say touch motion is not themable while the new theme motion roles retune it, the motion docs advertise roles no component reads, and FR7 amends spec:AST-090, which is not in the repository.
  • On iOS a fill that reverses to its previous value runs at half its written duration, lint and test-node (1/4) fail on a missing @astryxdesign/cli changeset and a stale docs golden, and the description needs AST-094's writing-rubric results.

[Automated review]

default: durationVars['--duration-medium'],
'@media (pointer: coarse)': {
'@supports (-webkit-touch-callout: none)':
'calc(var(--_progress-bar-change, 0) * var(--motion-progress-progress-view-whole-track-ms, 1000ms))',

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

On iOS a value that reverses to the one it just left runs at half this duration: CSS shortens a transition that reverses to its own start value. With the iOS gate emulated in Chromium, 10 → 90 held halfway and sent back to 10 runs 400 ms while this computes 0.8 s; a retarget to 30 keeps its 600 ms. Keep reversals off the browser's reversing path, or document that they follow the drawn distance, and add the case to ProgressBarTouch.a11y.chromium.spec.ts.

'pointer',
'contrast',
'motion',
'platform',

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-012 FR2 closes the when vocabulary to width, pointer, contrast and motion: "Future environment fields require explicit additions to this closed vocabulary." platform needs that amendment first (architecture:theme-authoring-contract also says an adaptation change preserves the fixed condition vocabulary), so it would be easier to split it out with the amendment.

* The engine is WebKit on iOS or iPadOS: the only one that implements
* `-webkit-touch-callout`. With TOUCH_QUERY, the iOS idiom.
*/
export const IOS_SUPPORTS_QUERY = '(-webkit-touch-callout: none)';

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-013 FR6: "Components detect the API or CSS capability they use rather than inferring it from a browser name. A narrow engine workaround is allowed only for a reproduced defect with a regression fixture and a review or removal condition." This query is read only to tell iOS WebKit from every other engine, so the iOS idiom built on it needs FR6's owner to rule before it ships.

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.

FR4, and DEC-1's "Rejected: tuning touch motion per theme", say touch motion is not themable, but this pull request adds defineTheme({motion: {springs, durations, curves}}) and ProgressBar and Spinner read --motion-* role variables a theme can set. Bring FR4 and DEC-1 in line with the roles, or drop the roles, before owners rule on this record.

Comment thread packages/cli/assets/docs/motion.doc.mjs Outdated
content: [
{
type: 'prose',
text: 'On a touch screen, sheets, alerts, menus, navigation and scrolling play the springs, durations and curves measured from iOS. Each value is a motion role named by its table path, such as `springs.sheetPresent` or `timing.curves.easeInOut`. A theme retunes a role through `motion.springs`, `motion.durations` or `motion.curves`, at its root or in an adaptation rule; a role it leaves alone keeps its measured default.',

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 says sheets, alerts, menus, navigation and scrolling play these roles, but at this head only four roles have a consumer (the three progress.* durations in ProgressBar and progress.activityIndicator.cycleMs in Spinner), so the springs.sheetPresent example below changes nothing. Document only roles a component reads, and reject role ids no table registers instead of accepting them silently.

@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 waits on an engineering owner's decision on draft spec:AST-094 for the iOS indeterminate bar, including its iOS engine gate, which spec:AST-013 FR6 does not allow today, and its theme-retunable timing, which the draft's own FR4 rules out. An engineering owner records that decision by commenting /approve-spec b7432c040517a75d25a5827fb880aecf97c2425b on this pull request, and the author may do so as an engineering owner.

  • Restore the indeterminate track, which the null default at ProgressBar.tsx:281-290 removes with a mouse and on Android too.
  • Rebase onto the stacked predecessors so this pull request carries only its own commits, drop when.platform or amend spec:AST-012 FR2, fix the halved iOS reversal, and keep the motion docs to roles that have a reader.

[Automated review]

// with Reduce Motion the track's own color steps aside for the pulse.
trackIndeterminate: {
backgroundColor: {
default: null,

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 null default replaces the track's --color-background-muted on every indeterminate bar, so the track renders transparent with a mouse, on Android and on iOS without Reduce Motion (the base paints rgb(212, 212, 212)). Give both defaults the track colour and keep transparent only under the gated Reduce Motion branch, then assert the track colour on the mouse and Android paths.

default: durationVars['--duration-medium'],
'@media (pointer: coarse)': {
'@supports (-webkit-touch-callout: none)':
'calc(var(--_progress-bar-change, 0) * var(--motion-progress-progress-view-whole-track-ms, 1000ms))',

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

A move reversed halfway (10 to 90, back to 10 when drawn at 50%) is specified as 0.8 s here, but CSS Transitions shortens a transition that reverses to its start, so Chromium runs it in 400 ms, half what progressView.wholeTrackMs documents. Start the reversal as a fresh transition or restate the table, and add the case to the touch spec.

* is everything else. Pair `ios` with `pointer: 'coarse'` for a touch
* profile, such as touch motion tuned per platform.
*/
platform?: 'ios' | '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.

spec:AST-012 FR2 closes the when vocabulary to width, pointer, contrast and motion and says future fields need explicit additions to it; no theme in the repository sets platform, only the motion doc's example. Remove it, or amend AST-012 FR2 in its own pull request.

* The engine is WebKit on iOS or iPadOS: the only one that implements
* `-webkit-touch-callout`. With TOUCH_QUERY, the iOS idiom.
*/
export const IOS_SUPPORTS_QUERY = '(-webkit-touch-callout: none)';

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 query identifies the engine to choose a presentation; spec:AST-013 FR6 allows an engine test only as a workaround for a reproduced defect with a regression fixture and a removal condition, so it needs an owner decision: amend FR6, or key the presentation on a capability.

Comment thread packages/cli/assets/docs/motion.doc.mjs Outdated
content: [
{
type: 'prose',
text: 'On a touch screen, sheets, alerts, menus, navigation and scrolling play the springs, durations and curves measured from iOS. Each value is a motion role named by its table path, such as `springs.sheetPresent` or `timing.curves.easeInOut`. A theme retunes a role through `motion.springs`, `motion.durations` or `motion.curves`, at its root or in an adaptation rule; a role it leaves alone keeps its measured default.',

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 only the three ProgressBar durations and Spinner's cycle read a motion role, so sheets, alerts, menus, navigation and scrolling do not follow these roles yet, and the example's springs.sheetPresent and springs.navigationPush change nothing. Document only roles that have a reader, or mark the rest as not yet in use.

@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 waits on an engineering owner's decision about draft AST-094. That decision covers the iOS-only engine gate (current AST-013 FR6 does not allow it), the trackless Reduce Motion pulse, and theme-retunable touch roles, which AST-094 FR4 and DEC-1 forbid as written. An engineering owner records it by commenting /approve-spec <full head SHA> on this pull request, and you may do so yourself if you are an engineering owner. Whatever the decision, these still need changes:

  • With Reduce Motion on iOS the pulsing bar reaches only 1.52:1 to 2.42:1 against the page, below the 3:1 non-text contrast AST-020 requires.
  • Rebase onto the stacked predecessors so the pull request carries only its own four commits, about 640 of its 19,658 authored lines.
  • Drop when.platform or amend AST-012 FR2 first, derive the Spinner spoke delays from the cycle role, keep a reversed iOS move at its documented duration, document only motion roles something reads, and fix the citation of the missing AST-090.

[Automated review]

'@media (pointer: coarse)': {
'@supports (-webkit-touch-callout: none)': {
default: colorVars['--color-background-muted'],
'@media (prefers-reduced-motion: reduce)': 'transparent',

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

With Reduce Motion on iOS this removes the track, so the pulsing fill (accent at 0.3 to 0.6 opacity) reaches only 1.52:1 to 2.42:1 against the light page, below the 3:1 non-text contrast that AST-020 requires for a meaningful state graphic. Pulsing at 0.74 opacity or more, or keeping the track and the 3 s slide that a mouse and Android keep, would hold 3:1.

default: durationVars['--duration-medium'],
'@media (pointer: coarse)': {
'@supports (-webkit-touch-callout: none)':
'calc(var(--_progress-bar-change, 0) * var(--motion-progress-progress-view-whole-track-ms, 1000ms))',

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

A move sent back to its starting value runs at half this duration, because CSS shortens a transition that reverses to its start. With the iOS gate emulated, 10 → 90 takes 800 ms; sent back to 10 halfway, it takes 400 ms, while 90 → 30 from the same point takes the documented 600 ms. A reversal test, plus a path the reversing rule does not shorten (or a documented exception in progress.ts), would close this.

Comment thread packages/core/src/Spinner/Spinner.tsx Outdated
// spoke partway through its cycle, so every spoke is in step from the first
// frame.
const spokeStyles = stylex.create({
s0: {animationDelay: '-600ms'},

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

These delays are sized for the 800 ms default, but the duration reads --motion-progress-activity-indicator-cycle-ms, which themes are invited to retune. At 1600 ms the bright head sits on one spoke for 800 ms and then steps 0, 7, 0, 1…. Deriving each delay from the same variable, for example calc(var(--motion-progress-activity-indicator-cycle-ms, 800ms) * -6 / 8) here, keeps the order at any cycle.

* is everything else. Pair `ios` with `pointer: 'coarse'` for a touch
* profile, such as touch motion tuned per platform.
*/
platform?: 'ios' | '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.

AST-012 FR2 closes the condition vocabulary to width, pointer, contrast and motion, and says future environment fields need an explicit addition to it. This adds platform without amending FR2, and nothing in this pull request reads it. Dropping it here, or amending AST-012 first, resolves the contradiction.

* The engine is WebKit on iOS or iPadOS: the only one that implements
* `-webkit-touch-callout`. With TOUCH_QUERY, the iOS idiom.
*/
export const IOS_SUPPORTS_QUERY = '(-webkit-touch-callout: none)';

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

AST-013 FR6 asks components to detect the capability they use rather than infer the engine, and allows an engine workaround only for a reproduced defect with a regression fixture and a removal condition. Here the property identifies iOS WebKit in order to choose a presentation, so it needs an engineering owner's decision (AST-094 approved together with an FR6 amendment) before it can land.

);
}
const registered = motionRole(id);
if (registered !== undefined && registered.kind !== kind) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

A well-formed id that names no registered role passes this check and writes a variable nothing reads; the error text even suggests sheet.present, which no table registers. Rejecting unregistered ids would catch a theme author's typo.

Comment thread packages/cli/assets/docs/motion.doc.mjs Outdated
content: [
{
type: 'prose',
text: 'On a touch screen, sheets, alerts, menus, navigation and scrolling play the springs, durations and curves measured from iOS. Each value is a motion role named by its table path, such as `springs.sheetPresent` or `timing.curves.easeInOut`. A theme retunes a role through `motion.springs`, `motion.durations` or `motion.curves`, at its root or in an adaptation rule; a role it leaves alone keeps its measured default.',

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Only four of the 50 roles have a reader in this pull request: three in ProgressBar and one in Spinner. Sheets, alerts, menus, navigation and scrolling do not play these roles yet, so this sentence and the example's springs.sheetPresent and springs.navigationPush promise effects a theme will not see.

cixzhang added a commit that referenced this pull request Oct 24, 2026
…ers change at once (#8272)

## Exact decision needed

What a person who prefers reduced motion sees when a core component animates a change, on touch and on other pointers, and whether a component may keep its motion.

## Existing authority searched

- `design:motion` DR5 (draft): every animated transition has an immediate or minimally moving form. Draft, so not authority.
- `spec:AST-013` FR13: reduced-motion behavior is unchanged by server-rendered entrances; it defines none.
- `spec:AST-012` FR2: `motion: reduce` and `pointer: coarse/fine` are the adaptation conditions.
- No current web record defines component behavior under reduced motion.
- Search terms: reduced motion, prefers-reduced-motion, cross-fade, Reduce Motion, motion DR5, spring, travel. Overlapping open pull requests: #7367 (draft `design:motion` DEC-2, hover entrances; no reduced-motion rule), #8214 (layer fade under a finger with Reduce Motion; conforms once its fade is 200 ms or less), #8052 (AST-094 FR8 carries the matching touch rule), #8211, #8239, #8253 (touch indicator and placeholder work; continuous indicators are outside this record).

## Decision

New current record `design:reduced-motion`, matching iOS Reduce Motion with Prefer Cross-Fade:

- DR1: under `prefers-reduced-motion: reduce`, a core component's entrances, exits, presentation switches, and state changes never move, scale, resize, or spring.
- DR2: on a coarse pointer, they cross-fade: opacity only, 200 ms or less.
- DR3: on any other pointer, they complete at once.
- DR4: no core component, prop, or theme value keeps motion under reduced motion.

`spec:AST-057` FR5 names that a swiped row's slide out, spring back, and hold-out follow DR2 under reduced motion. The draft `design:motion` keeps its other rules unapproved; its DR5 points to the new record. A separate record keeps the promotion to this one rule, as `docs/design/README.md` advises for a different approval lifecycle.

## Rejected alternatives

- A per-component setting that keeps full motion under reduced motion.

## Claim scope and non-goals

Only transitions under reduced motion. Motion without the preference, durations and easing, tokens, continuous indicators such as a spinner, and content the person drags with their own finger are outside this record.

## Evidence and implementation relationship

Layers that open under a finger with Reduce Motion appear in one frame today, where iOS cross-fades. The open fix #8214 makes them fade under a coarse pointer and stay immediate under a mouse, which conforms to DR2 and DR3 once its fade is 200 ms or less. The open draft #7367 (`design:motion` DEC-2, hover-summoned layers) does not touch reduced motion. The AST-094 revision in #8052 carries the matching touch rule. No implementation is included.

## Writing rubric results

- R1 narration: none.
- R2 research in the record: none.
- R3 evidence naming a change: none; verification names browser states.
- R4 rejected alternatives over one line or outside a decision: one, inside DEC-1, one line.
- R5 pull request or issue references remaining: none.
- R6 ruled open questions still listing options: none.

## Scope

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

## Validation

- `pnpm check:knowledge` (CI lint)
- Prettier
- `git diff --check`
@cixzhang

Copy link
Copy Markdown
Contributor

Posted by Cindy's agent on her behalf.

Parts of this PR re-implement behavior that already exists, or is being specified, as a shared part of core. Converging keeps one implementation per behavior:

  • Touch gating. core/src/ProgressBar/ProgressBar.tsx:287 writes its own (pointer: coarse) check. This conflicts with AST-012 FR2 (touch is resolved once, through the closed adaptation vocabulary) and public-component-api INV16. Resolve through the shared touch resolver key so behavior matches the spec. If this behavior genuinely can't be expressed through the resolver, please raise it here as a spec question instead of shipping a separate check.
  • Reduced motion. core/src/ProgressBar/ProgressBar.tsx:290 handles reduced motion locally. Per design:reduced-motion (DR1–DR4), the animator is the one place that resolves it (touch cross-fades in 200 ms or less with no travel, a fine pointer changes at once), and core has no opt-out, so reducedMotion: 'keep' should be removed.

This is about where the behavior lives, not about the PR's intent.

@vjeux

vjeux commented Oct 24, 2026

Copy link
Copy Markdown
Contributor Author

Agreed: one implementation.

@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 needs an engineering owner to approve or identify the governing spec for the iOS Safari look of ProgressBar's indeterminate bar, which today rests only on draft spec:AST-094: a 25% linear sweep, a Reduce Motion pulse with no track, whether it applies on iOS only or on every touch screen, and whether themes may retune its timing. An engineering owner records that decision by commenting /approve-spec <full head SHA> on this pull request, and the author may do so as an engineering owner. Whatever that decision, please rebase onto the landed stack so this diff is the ProgressBar change alone, and fix these:

  • Use the shared compact definition from spec:AST-012 FR9 instead of 744 px, which opens adaptive menus as popovers on 744–767 px touch screens.
  • Key the touch styles on the shared touch resolver instead of -webkit-touch-callout, as spec:AST-013 FR6 and the owner comment ask.
  • Drop when.platform, the theme-settable reduced-motion fade and reducedMotion: 'keep', which spec:AST-012 FR2 and the reduced-motion design record rule out.
  • Make the motion docs describe only roles a component reads, reject unknown role ids, and fix AST-094's citation of the missing AST-090.

[Automated review]

export const TOUCH_QUERY = '(pointer: coarse)';

/** The viewport is compact: layout behaviours. */
export const COMPACT_QUERY = '(width < 744px), (height < 500px)';

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-012 FR9 makes a screen compact below the effective md point (768 px by default) and says no resolver keeps size numbers of its own; on a 750 px touch screen this opens adaptive menus as popovers. main exports compactDefinition() / DEFAULT_COMPACT_DEFINITION for this.

* The engine is WebKit on iOS or iPadOS: the only one that implements
* `-webkit-touch-callout`. With TOUCH_QUERY, the iOS idiom.
*/
export const IOS_SUPPORTS_QUERY = '(-webkit-touch-callout: none)';

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

-webkit-touch-callout stands in for "iOS" here, which spec:AST-013 FR6 allows only for a reproduced engine defect with a fixture and a removal condition; the engineering owner asked for the shared touch key instead.

* is everything else. Pair `ios` with `pointer: 'coarse'` for a touch
* profile, such as touch motion tuned per platform.
*/
platform?: 'ios' | '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.

platform is not in spec:AST-012 FR2's closed when vocabulary (width, height, pointer, contrast, motion), which requires an explicit addition to the record first.

* Cross-Fade Transitions on (opacity CABasicAnimation 0.5 s,
* (0.42, 0, 0.58, 1)), Oct 23 2026
*/
reducedMotionCrossFadeMs: 500,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

As a theme role this lets a theme lengthen the reduced-motion fade, and its 500 ms default exceeds the 200 ms design:reduced-motion DR2 allows on touch; DR4 says theme values offer no way to keep a longer animation under reduced motion.

`${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.

An id the registry does not know passes through and emits a variable nothing reads (the test expects 'sheet.present' to become --motion-sheet-present), so a typo in a theme fails silently.

Comment thread packages/cli/assets/docs/motion.doc.mjs Outdated
content: [
{
type: 'prose',
text: 'On a touch screen, sheets, alerts, menus, navigation and scrolling play the springs, durations and curves measured from iOS. Each value is a motion role named by its table path, such as `springs.sheetPresent` or `timing.curves.easeInOut`. A theme retunes a role through `motion.springs`, `motion.durations` or `motion.curves`, at its root or in an adaptation rule; a role it leaves alone keeps its measured default.',

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 only ProgressBar and Spinner read a --motion-* variable and no component calls the animator, so retuning springs.sheetPresent or springs.navigationPush as the example shows changes nothing.

follows.
- **FR7 — Exits animate as UIKit's do.** A touch presentation MUST play the
exit its UIKit counterpart plays. **Proposed; pending owner review:** this
departs from the draft `spec:AST-090` FR1 (exits off by default) and FR10

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-090 is not on main or in this pull request, so this amendment cannot be checked.

vjeux added 11 commits October 26, 2026 23:57
…(spec:AST-094 FR18, DEC-13)

A screen-edge pan attached to a stack container, as wide as the screen,
with a touch 4 px from its edge: inside the 40.2 px band the pan decides at
UIKit's 15 px whatever the element's width, and then tracks the travel past
it. From several edges too. Both fail while a wider element decides at
FR19's 10 px.
…'s width (spec:AST-094 FR18, DEC-13)

FR18: inside the 40.2 px band a screen-edge pan decides at UIKit's 15 px
screen-edge hysteresis; outside it the author's scrolling is untouched and
FR19's 10 px governs every other claim. The pan decided at 10 px on any
element wider than the band, so NavigationStack's edge pan, attached to the
stack container, began 5 px early. The edge zone still decides only the
touch-action; a touch outside every band still fails at once. The two
cases that pinned the 10 px decision go.
… 15 px whatever the element's width) into the experimental touch exports
…er's move keeps that finger until it lifts) into the experimental touch exports
…than the band (spec:AST-094 FR18, DEC-22)

FR18 as amended in meta#8631 (DEC-22): the band's pan-x claim and 15 px start
hold only where the screen-edge pan is attached to an element within the
band; on an element that reaches beyond it, whose touch-action cannot
claim per touch, a touch in the band decides at FR19's 10 px like every
other claim. This restores the 10 px decision 3d85c30 removed and keeps
15 px on an element within the band. The cases pin DEC-22 both ways (a wide
element with a touch at x=4 begins at 10, an in-band strip at 15, and from
several edges at 10 on a wide element); the 10 px cases fail on 3d85c30.
…an the edge band decides at 10 px again, DEC-22) into the experimental touch exports
…xperimental touch exports

The motion README's Files table is the union: main's rows, with meta#8663's
resolveSnapPoint.ts and selectPage.ts, plus this branch's, in main's
order.

# Conflicts:
#	packages/core/src/motion/README.md
The base: the determinate fill on the compositor (meta#8196 de43cd2) and the touch foundation (meta#8076, with main through meta#8663).
The gesture README promises that usePressTracker rebuilds its tracker
when trackingExpansion, highlightDelay, cancelTravel or quickTapFlash
change, from the next touch. That fix (c248459) was lost in a later
merge of the press-tracking line: the tracker was built once, so a
changed option never applied. It is memoized on those four again; the
node attachment, keyed on the tracker, moves to the new one and disposes
the old. The callbacks keep being read through the ref on every touch.

The new test fails before this change (the same tracker after each
option change) and passes after it.

@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 until an engineering owner settles the governing spec for ProgressBar's indeterminate bar under a finger: no current record states Safari's 25% linear sweep, the trackless 0.74 to 1.0 Reduce Motion pulse, or whether spec:AST-094 FR4 needs motion roles for their timing. An engineering owner records that decision by commenting /approve-spec <full head SHA> on this pull request, and the author may do so as an engineering owner. Whatever that decision, these changes are still needed:

  • Land #8225 and its parents first, or retarget onto #8225, so this diff is its own 699 lines rather than 18,847.
  • Key the touch sweep on :dir(rtl) (ProgressBar.tsx:438), because a left-to-right bar inside a right-to-left page is empty for two thirds of each cycle.
  • Attach real-device iOS Safari and Android Chrome recordings for the touch claims.

[Automated review]

':is([dir="rtl"] *)': indeterminateSlideRtl,
'@media (pointer: coarse)': {
default: touchSweep,
':is([dir="rtl"] *)': touchSweepRtl,

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 key matches any right-to-left ancestor, while the 25% segment sits at the start of its own region, so a left-to-right bar inside a right-to-left page sweeps left off the track: on a 370 px track 92.5, 57.8, 23.1 and 0 px are visible at 0, 125, 250 and 500 ms, and the bar is empty from 333 to 1,667 ms of each 2 s cycle. design:bidirectionality DR3 asks ordered progression to follow the containing region's direction. Keying the sweep on ':dir(rtl)', as the determinate fill does at lines 356 and 369, fixes it; a left-to-right-inside-right-to-left case like the one in ProgressBarMotion.a11y.chromium.spec.ts would keep it fixed.

Comment on lines +32 to +36
sweep: '1000ms',
/** Under Reduce Motion, each direction of Safari's pulse instead. */
reducedMotionPulse: '1000ms',
/** The curve of all three: linear. */
easing: 'linear',

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

These are literals played in place of motion roles. spec:AST-094 FR4 says every duration and curve a touch presentation plays MUST be a motion role named for its use and that a component MUST NOT play a literal, and registerProgressBarTable() registers progress with no roles. Unless an engineering owner rules otherwise on the open question, name the sweep, the pulse and the linear curve as roles in the generated table (DEC-5) and read them here.

vjeux and others added 14 commits October 27, 2026 05:30
… s of wall clock

The two proofs that a Reduce Motion dip held before its first frame
still lands (after the hold is finished, and after it is resumed) polled
the box for 2 s of wall clock. On a loaded machine the page's main
thread ran the dip's next step late and the poll ran out first: one
WebKit run read {opacity: 0, translate: 'none'} with four WebKit
containers on the box, then passed in 530 ms on a rerun. They now wait
until the box runs no animation, on the animations' own finished
promises (including the fade back in the dip starts when its fade out
ends), and then assert the landed state. The test's own timeout still
bounds them.

With a 2.5 s main-thread block induced right after the release, both
proofs failed before this change (Timeout 2000ms exceeded while waiting
on the predicate) and pass after, 3 runs each in Chromium.
Main merged meta#8045 and meta#8046. The one conflict is utils/pressFeedback.ts:
it takes main's merged meta#8046 text (the finger and button slots, the mouse
and key press) and keeps this pull request's own additions, the one
writer of the press attribute (writePress, paintPress, readPressPaint)
and the header line naming them.
# Conflicts:
#	internal/interaction-harness/README.md
#	internal/interaction-harness/src/busy.test.ts
#	internal/interaction-harness/src/busy.ts
#	internal/interaction-harness/src/frames.ts
#	internal/interaction-harness/src/harness.interaction.browser.spec.ts
Main's button slot, merged with meta#8046, ends a press's release at once
when the next press starts, by clearing the press attribute directly. On
a row another painter holds, that clear erased the hold, which this pull
request's one writer refuses. A mouse press on a held row, released, then
a press elsewhere must leave the row held. Fails until the early end goes
through the one writer.
finish() writes none through writePress, as this pull request's
controller paints, so a held row stays its owner's when the next press
ends a release early, under the finger and the button slots alike.
Both upstream heads are folded onto main 70ea859 (her meta#8045 frame
evidence and meta#8046 coarse press): meta#8446 takes main's harness files, and
meta#8073 carries the press controller onto meta#8046's slots. meta#8076 has no change
of its own in either set of files, so the merge takes theirs with no hand
resolution, and meta#8076 is clean against main again.
…bute

From the review of meta#8076: the README said paintPress in
utils/pressFeedback.ts is the one writer of data-astryx-press, but the
action-group tracker on main (meta#8346) writes its groups' presses itself.
The sentence now says what holds at this head.
jsdom serves no cross-origin sheet, so the catch around the touch tests' rule reader could only hide a real error.
…cknowledgement

Each frame's acknowledgement settles before the session detaches, and a failure fails the test instead of being swallowed.
jsdom serves no cross-origin sheet, so the catch could only hide a real error.
…t screencast acknowledgement

Each frame's acknowledgement settles before the session detaches, and a failure fails the test instead of being swallowed.

@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 until an engineering owner settles the governing spec for ProgressBar's indeterminate bar under a finger: no current record states Safari's 25% linear sweep or the trackless 0.74 to 1.0 Reduce Motion pulse (spec:AST-094 leaves each component's touch presentation to its own record, and the ProgressBar record is still a draft), and the owners also need to say whether AST-094 FR4's motion roles apply to that timing. An engineering owner can record the decision by commenting /approve-spec <full head SHA> on this pull request (the author may do so as an engineering owner), name the record that should state it, or keep today's slide on touch. Whatever that decision, these changes are needed:

  • Land #8225 and its parents first, or retarget onto #8225, so this diff is its own 702 lines rather than 17,109.
  • Key the touch sweep on :dir(rtl) instead of :is([dir="rtl"] *) (ProgressBar.tsx:438), because a left-to-right bar inside a right-to-left page is empty for two thirds of every cycle.
  • Attach recordings from a real iPhone in Safari and a real Android phone in Chrome for the touch claims, as AST-094's platform support requires.

[Automated review]

':is([dir="rtl"] *)': indeterminateSlideRtl,
'@media (pointer: coarse)': {
default: touchSweep,
':is([dir="rtl"] *)': touchSweepRtl,

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 key matches any right-to-left ancestor, while the 25% segment sits at the start of its own region, so a left-to-right bar inside a right-to-left page plays the right-to-left sweep from the left end and leaves the track: on a 370 px track 92.5, 57.8, 23.1 and 0 px of it are visible at 0, 125, 250 and 500 ms, and the track is empty from 333 to 1,667 ms of every 2 s cycle. design:bidirectionality DR3 asks ordered progression to follow the containing region's direction. Keying the sweep on ':dir(rtl)', as the determinate fill does at lines 356 and 369, fixes it; a left-to-right-inside-right-to-left case in ProgressBarTouch.a11y.chromium.spec.ts would keep it fixed.

This branch was successfully deployed

1 active deployment
Preview — 9f354846 Deployed Oct 29, 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