How to Use Astryx SegmentedControl for Tab-Like Interfaces
The Astryx SegmentedControl component implements a radio-group interface that visually resembles a tab bar, providing accessible navigation through a container-item composition pattern with support for icons, disabled states, and responsive layouts.
The Astryx SegmentedControl component from the facebook/astryx repository offers a robust solution for creating tab-like navigation interfaces in React applications. This component implements the ARIA radio group pattern while providing visual styling that resembles traditional tab bars, making it ideal for view toggles, mode selectors, and segmented navigation patterns.
Component Architecture and Context API
The SegmentedControl system consists of three core files working together to provide state management and rendering:
- SegmentedControl.tsx – The container component that creates the
SegmentedControlContextand manages focus coordination - SegmentedControlItem.tsx – Individual segment buttons that consume context via
useSegmentedControlContext - SegmentedControlContext.ts – Context definition providing
value,onChange,size,layout,isDisabled, andhasDisabledMessage
In SegmentedControl.tsx, the container implements handleContainerFocus to synchronize focus changes with selection, ensuring the UI follows ARIA Authoring Practices for radio groups. The component hierarchy requires wrapping SegmentedControlItem components inside the SegmentedControl container:
<SegmentedControl value={view} onChange={setView} label="View mode">
<SegmentedControlItem value="grid" label="Grid" />
<SegmentedControlItem value="list" label="List" />
</SegmentedControl>
Layout and Sizing Configuration
The component supports two distinct layout modes through the layout prop defined in the container:
layout="hug" (default) – Each segment sizes to its content width, creating a compact tab bar that fits its contents.
layout="fill" – All segments share equal portions of the container width, useful for full-width toolbars. This applies the fill style from SegmentedControl.tsx to distribute space evenly.
Size variants apply to both container and items through the size prop, supporting sm, md, and lg tokens that affect padding, typography, and overall dimensions.
Accessibility and Keyboard Navigation
The implementation provides comprehensive accessibility features according to WAI-ARIA guidelines:
The container renders with role="radiogroup" and accepts an aria-label prop, while each item renders as <button role="radio"> with aria-checked attributes. When isLabelHidden is true on an item, the component automatically applies an aria-label to maintain screen reader compatibility.
Keyboard navigation leverages the useListFocus primitive for roving-tabindex behavior, supporting arrow key navigation between tabs, Home/End keys for jumping to first/last items, and a selection-follows-focus pattern that updates the controlled value as users navigate.
Usage Examples
Basic Tab-Like Control
Implement a standard three-tab view selector using controlled state:
import { SegmentedControl, SegmentedControlItem } from '@astryxdesign/core';
function ViewToggle() {
const [view, setView] = React.useState('grid');
return (
<SegmentedControl
value={view}
onChange={setView}
label="View mode"
>
<SegmentedControlItem value="grid" label="Grid" />
<SegmentedControlItem value="list" label="List" />
<SegmentedControlItem value="table" label="Table" />
</SegmentedControl>
);
}
Equal-Width Tabs with Fill Layout
Create a full-width control where tabs share equal space:
<SegmentedControl
value={mode}
onChange={setMode}
label="Mode"
layout="fill"
>
<SegmentedControlItem value="edit" label="Edit" />
<SegmentedControlItem value="preview" label="Preview" />
</SegmentedControl>
Icon-Based Tabs
Combine icons with labels or display icon-only tabs while preserving accessibility:
import { GridIcon, ListIcon } from './icons';
<SegmentedControl
value={display}
onChange={setDisplay}
label="Display"
>
<SegmentedControlItem
value="grid"
label="Grid"
icon={<GridIcon />}
/>
<SegmentedControlItem
value="list"
label="List"
icon={<ListIcon />}
isLabelHidden
/>
</SegmentedControl>
Disabled States
Disable the entire control with a tooltip explanation:
<SegmentedControl
value="grid"
onChange={setDisplay}
label="Display"
isDisabled
disabledMessage="Switching display is unavailable while loading."
>
<SegmentedControlItem value="grid" label="Grid" />
<SegmentedControlItem value="list" label="List" />
</SegmentedControl>
Or disable individual items:
<SegmentedControl value={view} onChange={setView} label="View">
<SegmentedControlItem value="grid" label="Grid" />
<SegmentedControlItem value="list" label="List" isDisabled />
</SegmentedControl>
Summary
- Astryx SegmentedControl uses a compound component pattern with
SegmentedControlas the container andSegmentedControlItemas children - File locations: Core logic resides in
SegmentedControl.tsx,SegmentedControlItem.tsx, andSegmentedControlContext.ts - Layout options: Choose
layout="hug"for content-sized tabs orlayout="fill"for equal-width distribution - Accessibility: Implements
role="radiogroup"with roving-tabindex keyboard navigation viauseListFocus - Styling: Built with StyleX (
styles.container,styles.fill,sizeStyles) using theme tokens for colors, spacing, and border radius - Disabled states: Support both global disabling with tooltips (
isDisabled+disabledMessage) and per-item disabling
Frequently Asked Questions
How do I implement keyboard navigation in Astryx SegmentedControl?
Keyboard navigation is automatic and requires no additional configuration. The component uses the useListFocus primitive to manage roving-tabindex behavior, allowing users to navigate between tabs using arrow keys, jump to the first tab with Home, and jump to the last tab with End. Focus changes automatically update the selected value following the selection-follows-focus pattern defined in SegmentedControl.tsx.
Can I use icons without text labels in SegmentedControl?
Yes, pass the icon prop to SegmentedControlItem along with isLabelHidden to display icon-only tabs. When isLabelHidden is true, the component automatically applies an aria-label to the button element, ensuring screen readers can still identify the tab's purpose. The icon renders before the label position when both are present.
What is the difference between hug and fill layouts?
The layout="hug" setting (default) allows each segment to size according to its content width, creating a compact tab bar. The layout="fill" setting forces all segments to share equal portions of the container width, implemented through the fill style in SegmentedControl.tsx. Use fill layouts for full-width toolbars or when you need visual balance between tabs of varying label lengths.
How does the disabled state with tooltip work?
When isDisabled is applied to the SegmentedControl container, the entire control dims visually. If you also provide a disabledMessage prop, the component uses the useTooltip hook to display an explanation when users hover over or focus the selected segment. The selected segment remains focusable while disabled to maintain keyboard navigation continuity, while unselected segments become non-interactive.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →