Skip to content

spec(SegmentedControl): touch presentation — slide, overlay on press, general pressed-thumb lift (D33) - #8596

Open
cixzhang wants to merge 3 commits into
mainfrom
spec/segmentedcontrol-touch-presentation
Open

cixzhang wants to merge 3 commits into
mainfrom
spec/segmentedcontrol-touch-presentation

Conversation

@cixzhang

@cixzhang cixzhang commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

Decided (Cindy, 2026-10-26: D33).

User impact

On a phone, a person can slide SegmentedControl's selection to another segment and see it travel, as on iOS. A held segment keeps today's pressed overlay. A theme can add a lift to the pressed thumb; shipped themes don't, so they look exactly as before. A mouse is unchanged.

Records

  • component:SegmentedControl (draft):
    • FR5/FR6: the selection slides as one surface (::before) on internal segmented.* motion roles, with a Reduce Motion cross-fade. This follows spec:AST-094.
    • FR7 (D33): a held unselected segment paints the pressed overlay (design:user-states DR1), with no theme role to dim it.
    • FR8 (D33): holding the selected segment lifts it by a general pressed-thumb-lift theme role shared by SegmentedControl, Switch, and Slider. The lift happens only while pressed, and the role is none by default.
    • FR9/FR10: the label follows the drag, and the lift commits the nearest segment. This is the control's own model: SegmentedControl is a named exception to family:action-groups (spec(family): action groups — press, slide across, act on lift (AST-094 FR26–FR28) #8622).
    • PR1 adds no element. DEC-1 records D33, and there are no open questions.
  • design:user-states DR3: a theme may own one press treatment on a selected thumb, the general pressed-thumb lift. It applies only while pressed, is none by default, and a Switch thumb stays circular. The anatomy row is updated to match.

Measured behavior comes from #8127. No component-specific theme token is added.

Writing rubric results

  • R1 present tense: pass. R2 research: none in the records.
  • R3 verification: rows for FR5–FR10 and PR1.
  • R4 rejected alternatives: one line in DEC-1.
  • R5 change references: none in the records. R6 open questions: none.

…pressed treatment and thumb lift roles, drag-to-select (FR5–FR10, OQ1–OQ2)
@vercel

vercel Bot commented Oct 26, 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 11:49pm UTC

Request Review

@astracat-bot

astracat-bot Bot commented Oct 26, 2026 •

Copy link
Copy Markdown

AI review status for this pull request.

Review status Updated
🟡 Reviewed (for maintainers only) Oct 27, 2026, 12:25 AM UTC

@github-actions github-actions Bot added the spec-auto-merge Auto-merge was enabled by the spec owner gate label Oct 26, 2026
@github-actions

github-actions Bot commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

PR Analysis Report

Current run: The current change has no visual or preview-producing scope. Visual checks are not applicable; preview links are not shown.

The exact-head preview is not available yet.


Generated by PR Enrichment workflow | View current CI run

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Meta Open Source bot. label Oct 26, 2026
@cixzhang

Copy link
Copy Markdown
Contributor Author

Posted by Cindy's agent on her behalf.

Cindy's decisions on SegmentedControl press feedback under a finger:

  1. Pressing the selected thumb: the lift is theme-owned. A general "pressed thumb lift" theme role, shared by draggable thumbs (SegmentedControl, Switch and Slider), defaults to none, so shipped themes look the same as today, and a theme can turn the lift on. The Switch thumb stays circular; the lift only applies while it's pressed.
  2. Pressing an unselected segment: keep today's pressed overlay. No title dim and no new role.

The SegmentedControl record states both, and this PR conforms: no thumb lift by default, and the overlay stays on press.

@cixzhang cixzhang changed the title spec(SegmentedControl): proposed — touch presentation (FR5–FR10; owner calls OQ1–OQ2) spec(SegmentedControl): touch presentation — slide, overlay on press, general pressed-thumb lift (D33) Oct 26, 2026
@cixzhang

Copy link
Copy Markdown
Contributor Author

/approve-spec 660cd7e

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

SegmentedControl's touch presentation is owner-approved for this head, but the two records still contradict themselves and a current claim in a few places, so this needs changes before it lands. Please make FR10 commit the nearest enabled segment (and say what a cancelled touch does), carry the DR3 lift into the design:user-states matrix rows, example and decision log, and remove the first-draft leftovers: the open OQ2 and the dim role in the FR7/FR8 verification row.

  • The compatibility section still says runtime and styling are unchanged, and nothing says how a theme's selected-segment styles (Neutral sets boxShadow: 'none') reach the sliding ::before surface.
  • FR6's Reduce Motion cross-fade needs a second surface that the FR5/FR6/PR1 row rules out, and FR10 cites family:action-groups, which doesn't exist on main yet.

[Automated review]

| FR7 | Under a finger, a held unselected segment paints the pressed overlay on its part, as under a mouse (`design:user-states` DR1); no theme role changes or dims it. | `design:user-states` DR1; owner decision (DEC-1) | Decided |
| FR8 | Under a finger, holding the selected segment lifts the selected surface by the theme's general pressed-thumb-lift role for draggable thumbs (SegmentedControl, Switch, Slider), only while it is pressed; the role is none by default, so shipped themes are unchanged. A theme that sets it grows the surface and casts its shadow, as a transform and shadow on the existing surface (`design:user-states` DR3). | `design:user-states` DR3; owner decision (DEC-1) | Decided |
| FR9 | During a drag of the selected surface, the selected label's colour follows the surface, with an internal hysteresis around each boundary; label weights change only when the drag starts. A tap selects through the browser's click (`spec:AST-094` FR11). | Measured iOS 26.2 behavior; `spec:AST-094` FR11 | Follows `spec:AST-094` |
| FR10 | Under a finger, a drag from the selected segment carries the selected surface with the finger, within an internal overshoot past the end segments, and the lift commits the segment nearest the finger through that segment's click; a drag that starts on another segment leaves the surface in place and commits where it lifts, as a menu's press-tracking slide commits where it lifts (`spec:AST-094` FR26–FR28). | Measured iOS 26.2 behavior; this record's own model (`family:action-groups` exception) | Governed here |

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

FR10 commits "the segment nearest the finger", but a segment can be disabled on its own and its click selects nothing, while design:user-states DR2 keeps a disabled control inoperable. Could this say "the nearest enabled segment", add a disabled segment to the FR9/FR10 row, and say where the surface goes on a cancelled touch? The basis cell also cites family:action-groups, which doesn't exist on main yet.

SegmentedControlItem children. This draft records the current consumer anatomy
and theming ownership without changing selection behavior, targets, or public
API.
and theming ownership, and proposes how a finger presses, drags, and selects

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

"proposes" narrates the change (rubric R1). The Compatibility section just below also still says runtime and styling are unchanged, although FR5–FR10 change both by default under a finger. Could both be restated, including how a theme's selected-segment styles (for example Neutral's selected: { boxShadow: 'none' }) reach the sliding surface?

| FR1, FR2 | `SegmentedControl.test.tsx` radiogroup and rendering suites | Control with multiple segment items | Removing the group or item semantics fails existing role and content assertions. | `audit:SegmentedControl/anatomy` |
| FR3 | `SegmentedControl.test.tsx` label and icon suites | Visible label, icon with label, icon-only item | Removing or changing current label/icon rendering fails existing DOM assertions. | `audit:SegmentedControl/anatomy` |
| FR4 | `SegmentedControl.test.tsx` selection and disabled suites | Selected, unselected, and disabled items | Breaking reflected item state fails current ARIA, class, or data-attribute assertions. | `audit:SegmentedControl/theming` |
| FR5, FR6, PR1 | Selection-motion unit tests and real-browser proofs in Chromium and WebKit | a tap, a retarget, keyboard selection, Reduce Motion, a mouse | a second surface or added element, a Layout or Paint per frame, a curve off its role, travel under Reduce Motion, or changed behavior under a mouse fails | `audit:SegmentedControl/theming` |

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

FR6's Reduce Motion cross-fade shows the old and new surfaces together, but this row fails "a second surface" with Reduce Motion among its states, and PR1 names only ::before. Could the record name the cross-fade's second paint surface (for example the radiogroup's ::after) and exempt it here?

| FR3 | `SegmentedControl.test.tsx` label and icon suites | Visible label, icon with label, icon-only item | Removing or changing current label/icon rendering fails existing DOM assertions. | `audit:SegmentedControl/anatomy` |
| FR4 | `SegmentedControl.test.tsx` selection and disabled suites | Selected, unselected, and disabled items | Breaking reflected item state fails current ARIA, class, or data-attribute assertions. | `audit:SegmentedControl/theming` |
| FR5, FR6, PR1 | Selection-motion unit tests and real-browser proofs in Chromium and WebKit | a tap, a retarget, keyboard selection, Reduce Motion, a mouse | a second surface or added element, a Layout or Paint per frame, a curve off its role, travel under Reduce Motion, or changed behavior under a mouse fails | `audit:SegmentedControl/theming` |
| FR7, FR8 | Theme tests and real-browser proofs under a finger | the default theme, a theme that sets a dim and a lift, a held unselected segment, a held selected segment | a look that ignores the theme roles, a lift or dim under the default theme, or a lift that adds an element fails | `audit:SegmentedControl/theming` |

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 tests "a theme that sets a dim and a lift" and fails "a lift or dim under the default theme", but FR7 says no theme role dims a held segment and DEC-1 rejects the dim role. Suggest dropping the dim and failing any theme role that changes or dims a held unselected segment.

Comment on lines +170 to +173
- **OQ2 — A general pressed-thumb lift for draggable thumbs.** (`human-design`)
Open. Proposed: one theme role shared by SegmentedControl, Switch, and Slider,
none by default (today's look), amending `design:user-states` DR3 for touch
presentations.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

OQ2 is still marked Open under "None.", though DEC-1 decides it and this PR already amends DR3, so the description's "R6 open questions: none" doesn't hold yet. Suggest deleting the bullet.

Comment on lines +60 to +63
receives the pressed overlay. A theme may own one press treatment on a
selected thumb: the general pressed-thumb lift shared by the draggable thumbs
of SegmentedControl, Switch, and Slider lifts a selected thumb only while it
is pressed, is none by default, and keeps a Switch thumb circular. Under a finger the item keeps that surface, and its press timing follows

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

DR3 now allows a theme lift, but the SegmentedControl matrix row ("keeps its raised surface unchanged", under "This table is the complete current claim"), the example, and DEC-1 still say the held selected segment never changes, and the Switch and Slider rows don't mention the lift. Switch and Slider thumbs also have no "selected" state. Could those lines carry the exception, with a decision entry for the lift in this record?

@vjeux

vjeux commented Oct 27, 2026

Copy link
Copy Markdown
Contributor

FR9 says label weights change only when the drag starts; the implementation and UIKit's measurement change them at the lift (#8127's proofs: no weight change mid-drag). Should it read 'only at the lift'?

The FR7/FR8 verification row lists "a theme that sets a dim and a lift" and "a look that ignores the theme roles", while FR7 says no theme role dims a segment; is "a dim" there a leftover?

vjeux added a commit to vjeux/astryx that referenced this pull request Oct 27, 2026
…ole, segmented.selectionSpring

The record owner's SegmentedControl record (meta#8596) has the slide play on
the internal `segmented.*` motion roles with UIKit's measured values
(`spec:AST-094` FR4, DEC-11). `registerSegmentedTable` registers the
slide's spring as `segmented.selectionSpring`, named in FR4's role table;
the selection motion registers it before its first animation. The value is
unchanged, and no theme retunes it while the role stays internal: the
resolver still plays the table's value. The registration moves here from
the touch-tracking pull request, where the slide's spring was registered
with the lift's and the pressed title's roles (since withdrawn).

The unit proof that the slide's spring is the role failed before the
change.
vjeux added a commit to vjeux/astryx that referenced this pull request Oct 27, 2026
…l request no longer edits it

The record owner's meta#8596 records SegmentedControl's touch presentation
(FR5–FR10, DEC-1, PR1). Its FR5 and FR6 are this pull request's slide, so
the record rows, verification row, Intent and compatibility lines this
pull request had added are dropped in favor of hers, and the record stays
as the base has it.

This branch was successfully deployed

1 active deployment
Preview — 660cd7e6 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. spec-auto-merge Auto-merge was enabled by the spec owner gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants