Repository navigation
SegmentedButtons MD3 Spec - #5086
michalfedyna wants to merge 16 commits into
Conversation
There was a problem hiding this comment.
🟡 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
showSelectedCheckis false, neither child consumescheckmarkScale, but this effect still creates awithSpring(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
stylenow decorates the outer wrapper, while the nestedTouchableRippleowns the actual fixed-height border and radii. Existingbuttons[].stylevalues such asheight,borderColor,borderWidth, orborderRadiustherefore 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/radiosemantics 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.
| { opacity: labelOpacity }, | ||
| labelStyle, |
| const applyOpacity = (value: ColorValue, opacity: number): ColorValue => { | ||
| if (opacity === 1 || typeof value !== 'string') { | ||
| return value; | ||
| } |
| return { | ||
| wrapper: checked | ||
| ? theme.colors[SegmentedButtonTokens.selectedContainerColor] | ||
| : 'transparent', |
|
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. |
Waiting for #5080 to land on main
Motivation
Related issue
Test plan