RadioButton draws the selected or unselected state of one option in a mutually exclusive group: a circular track with a stroke ring, filled with an inner circle when selected. It is a visual building block, not an input. It has no change callback, takes no focus, and never modifies its own state — the parent that owns the selected value passes checked down.RadioButton yourself. Use ListItem with showRadioButton inside a VerticalList, so each option is one labelled focus target and the group has a single source of truth. ListItem also owns the radio role and state, so a row is announced as one widget instead of a widget containing another widget.import { useState } from 'react';
import { ListItem, VerticalList } from '@wearables-ui-toolkit/mrbd';
const QUALITY_OPTIONS = ['Standard', 'High', 'Maximum'];
export function CaptureQuality() {
const [selected, setSelected] = useState('High');
return (
<VerticalList>
{QUALITY_OPTIONS.map(option => (
<ListItem
key={option}
title={option}
showRadioButton
checked={option === selected}
onCheckedChange={() => setSelected(option)}
/>
))}
</VerticalList>
);
}
RadioButton directly only where some other, clearly labelled control already owns the choice and you just need the glyph:import { RadioButton, TextView, TextStyle } from '@wearables-ui-toolkit/mrbd';
export function SelectedQualitySummary({ quality }: { quality: string }) {
return (
<div className="quality-summary">
<RadioButton checked aria-label={quality} />
<TextView textStyle={TextStyle.BODY2}>{quality}</TextView>
</div>
);
}
RadioButtonProps.| Prop | Type | Default | Description |
|---|---|---|---|
checked | boolean | false | Whether the inner fill is drawn. Fully controlled; the component never changes it. |
RadioButtonProps also accepts the standard div attribute surface — className, style, aria-label, title, data-* (including data-uit-*), and DOM event handlers — which is spread onto the root element. There is no onChange prop (it is removed from the accepted attributes), no disabled prop, and no animation prop.role, aria-checked, aria-disabled, aria-hidden, tabIndex, onClick, and onKeyDown after the rest props, so none of those can be overridden: an onClick or onKeyDown you pass to RadioButton is never attached, and an aria-hidden or aria-disabled you pass is dropped. Other handlers, such as onFocus and onPointerEnter, do reach the root element.checked is the single source of truth. Clicking the glyph does nothing; wire activation on the parent.checked is true a third, inner filled circle is added. The background uses --uit-color-background-inset, and the ring and inner fill use --uit-color-control-active.inline-flex square written as inline styles, and it does not shrink in a flex row. Your style object is merged after those, so let the parent lay the glyph out rather than resizing it.ref forwards to the root div, which is all you need for measuring or anchoring something to the glyph.role="radio" with aria-checked reflecting checked.aria-hidden cannot be turned on from the outside, so a RadioButton nested inside another control is always announced as an extra widget.aria-label when nothing adjacent names the option.ListItem with showRadioButton instead.checked from it.ListItem.RadioButton in your own focusable element to make it selectable; that announces two nested widgets.