Skip to content

SegmentedButtons MD3 Spec - #5086

Closed
michalfedyna wants to merge 16 commits into
callstack:mainfrom
michalfedyna:refactor/segmented-buttons-md3
Closed

michalfedyna wants to merge 16 commits into
callstack:mainfrom
michalfedyna:refactor/segmented-buttons-md3

Conversation

@michalfedyna

@michalfedyna michalfedyna commented Sep 1, 2026 •

Copy link
Copy Markdown
Contributor

Waiting for #5080 to land on main

Motivation

Related issue

Test plan

Copilot AI 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.

🟡 Changes recommended

Unresolved issues affect disabled-state styling, user style overrides, unnecessary animations, and radio keyboard behavior.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Migrates SegmentedButtons to Material 3 styling, accessibility semantics, RTL support, focus behavior, and animated selection states.

Changes:

  • Adds MD3 tokens and color/geometry resolution.
  • Refactors layout, selection handling, accessibility, and RTL behavior.
  • Expands behavioral, theming, and accessibility test coverage.
File summaries
File Summary
src/components/SegmentedButtons/utils.ts Resolves colors, borders, radii, and MD3 states.
src/components/SegmentedButtons/tokens.ts Defines component design tokens.
src/components/SegmentedButtons/SegmentedButtons.tsx Handles group layout, semantics, RTL, and selection.
src/components/SegmentedButtons/SegmentedButtonItem.tsx Updates item rendering, focus, ripple, and accessibility.
src/components/SegmentedButtons/SegmentedButtonContent.tsx Renders labels, icons, and animated content.
src/components/SegmentedButtons/AnimatedOptionIcon.tsx Animates option icons.
src/components/SegmentedButtons/AnimatedCheckIcon.tsx Animates selected check icons.
src/components/__tests__/SegmentedButton.test.tsx Adds behavioral and accessibility coverage.
src/components/__tests__/__snapshots__/SegmentedButton.test.tsx.snap Updates rendered output snapshots.
Review details

Suppressed comments (3)

src/components/SegmentedButtons/SegmentedButtonContent.tsx:58

  • When showSelectedCheck is false, neither child consumes checkmarkScale, but this effect still creates a withSpring(0) animation for every segment on mount and when reduce-motion changes. The previous implementation skipped this work for static segments; guard the effect (or split the animated content) so non-animated buttons do not pay the animation cost.
  React.useEffect(() => {
    checkmarkScale.value = withSpring(showCheckIcon ? 1 : 0, {
      reduceMotion: reduceMotion ? ReduceMotion.Always : ReduceMotion.Never,
    });
  }, [checkmarkScale, reduceMotion, showCheckIcon]);

src/components/SegmentedButtons/SegmentedButtonItem.tsx:152

  • style now decorates the outer wrapper, while the nested TouchableRipple owns the actual fixed-height border and radii. Existing buttons[].style values such as height, borderColor, borderWidth, or borderRadius therefore no longer control the rendered button and can produce a second, mismatched border. Before this split the same style was merged with the view that painted the button; keep user styles on the visual layer or explicitly preserve their overrides across both layers.
    <View
      testID={testID && `${testID}-wrapper`}
      style={[
        styles.wrapper,
        borderRadius,
        { backgroundColor: colors.wrapper },
        style,
      ]}

src/components/SegmentedButtons/SegmentedButtonItem.tsx:162

  • The new radiogroup/radio semantics are not accompanied by the radio keyboard contract: focusable={!disabled} leaves every enabled item in the tab order, and there is no arrow-key handler to move focus and selection within the group. On web, keyboard users will tab through each option and arrow keys will do nothing; implement roving focus with arrow navigation (and the horizontal orientation) or use semantics that do not claim a radiogroup.
        role={role}
        disabled={disabled}
        focusable={!disabled}
  • Files reviewed: 9/9 changed files
  • Comments generated: 3
  • Review effort level: Lite (auto)

Note

Copilot is running an experiment and ran this review at Lite.


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +91 to +92
{ opacity: labelOpacity },
labelStyle,
Comment on lines +96 to 99
const applyOpacity = (value: ColorValue, opacity: number): ColorValue => {
if (opacity === 1 || typeof value !== 'string') {
return value;
}
Comment on lines +122 to +125
return {
wrapper: checked
? theme.colors[SegmentedButtonTokens.selectedContainerColor]
: 'transparent',
@Doberjohn

Copy link
Copy Markdown
Collaborator

Material 3 Expressive deprecates segmented buttons in favour of connected button groups, so we've decided to ship ConnectedButtonGroup (#5074, tracked in #4979) in 6.1 together with a deprecation of SegmentedButtons, and to remove SegmentedButtons in 7.0. A full SegmentedButtons spec update would be a breaking change for a component on its way out, and the shared parts are covered by other 6.0 PRs (#5108 for touch targets, #5107 for the focus ring, #5112 for motion), so I'm closing this one. The branch stays as a reference.

@Doberjohn Doberjohn closed this Oct 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants