Skip to content

SegmentedButtons MD3 Spec - #5086

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

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',
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.

3 participants