Radio
Pick one value from a few options. Arrow keys move between them. It is a field of its own; do not put it in a Field.
Retry policy
import { RadioGroup, RadioItem } from "@etched/reticle/radio";
export function Preview() {
return (
<RadioGroup legend="Retry policy" defaultValue="once">
<RadioItem value="never" label="Never" />
<RadioItem value="once" label="Once" />
<RadioItem value="always" label="Until it passes" />
</RadioGroup>
);
}Installation
import { RadioGroup, RadioItem, RadioLegend } from "@etched/reticle/radio";Examples
Groups
Retry policy
Applies to runs that fail on a bench error.
Employment
Rows per page
Label first
Would you bet on them?
Choose an answer.
Region (locked)
Reviewer (optional)
import { useState } from "react";
import { RadioGroup, RadioItem } from "@etched/reticle/radio";
export function Groups() {
const [size, setSize] = useState(25);
return (
<div className="grid">
<RadioGroup legend="Retry policy" description="Applies to runs that fail on a bench error." defaultValue="once">
<RadioItem value="never" label="Never" />
<RadioItem value="once" label="Once" />
<RadioItem value="always" label="Until it passes" />
<RadioItem value="custom" label="Custom (admins only)" disabled />
</RadioGroup>
<RadioGroup legend="Employment" orientation="horizontal" name="employment">
<RadioItem value="full-time" label="Full-time" />
<RadioItem value="internship" label="Internship" />
</RadioGroup>
<RadioGroup<number> legend="Rows per page" orientation="horizontal" value={size} onValueChange={setSize}>
{[10, 25, 50].map((n) => (
<RadioItem<number> key={n} value={n} label={String(n)} />
))}
</RadioGroup>
<RadioGroup legend="Label first" controlPosition="end" defaultValue="a">
<RadioItem value="a" label="Option A" />
<RadioItem value="b" label="Option B" />
</RadioGroup>
<RadioGroup legend="Would you bet on them?" required error="Choose an answer.">
<RadioItem value="yes" label="Yes" />
<RadioItem value="no" label="No" />
<RadioItem value="complicated" label="It’s complicated" />
</RadioGroup>
<RadioGroup legend="Region (locked)" disabled defaultValue="us">
<RadioItem value="us" label="United States" />
<RadioItem value="tw" label="Taiwan" />
</RadioGroup>
<RadioGroup legend="Reviewer" required={false}>
<RadioItem value="me" label="Me" />
<RadioItem value="team" label="Anyone on the team" />
</RadioGroup>
</div>
);
}Cards
The cards of a card group join into one card, with the circle at the end.
Plan
Domain strength
Bench (circle first)
Report format
Choose a format.
import { RadioGroup, RadioItem } from "@etched/reticle/radio";
const percentiles = [
{ value: "top-1", label: "Top 1%" },
{ value: "top-5", label: "Top 5%" },
{ value: "top-10", label: "Top 10%" },
{ value: "top-25", label: "Top 25%" },
{ value: "below", label: "Below top 25%" },
{ value: "unsure", label: "Not sure" },
];
export function Cards() {
return (
<div className="grid">
<RadioGroup legend="Plan" appearance="card" defaultValue="standard">
<RadioItem value="standard" label="Standard" description="Runs on shared benches, results in a day." />
<RadioItem value="priority" label="Priority" description="Runs first on the next free bench." />
<RadioItem value="dedicated" label="Dedicated" description="Needs a reserved bench." disabled />
</RadioGroup>
<RadioGroup legend="Domain strength" appearance="card" orientation="horizontal" defaultValue="top-5">
{percentiles.map((p) => (
<RadioItem key={p.value} value={p.value} label={p.label} />
))}
</RadioGroup>
<RadioGroup legend="Bench (circle first)" appearance="card" controlPosition="start" defaultValue="shared">
<RadioItem value="shared" label="Shared" description="Any free bench in the lab." />
<RadioItem value="reserved" label="Reserved" description="The bench booked for this run." />
</RadioGroup>
<RadioGroup legend="Report format" appearance="card" required error="Choose a format.">
<RadioItem value="summary" label="Summary" description="One page per run." />
<RadioItem value="full" label="Full" description="Every log line and chart." />
</RadioGroup>
</div>
);
}Segmented
Window
Window (disabled)
import { RadioGroup, RadioItem } from "@etched/reticle/radio";
export function Segmented() {
return (
<div className="grid">
<RadioGroup legend="Window" appearance="segmented" defaultValue="12h">
<RadioItem value="1h" label="1h" />
<RadioItem value="12h" label="12h" />
<RadioItem value="24h" label="24h" />
<RadioItem value="7d" label="7d" disabled />
</RadioGroup>
<RadioGroup legend="Window (disabled)" appearance="segmented" defaultValue="1h" disabled>
<RadioItem value="1h" label="1h" />
<RadioItem value="12h" label="12h" />
</RadioGroup>
</div>
);
}A description that follows the value
The meaning of the chosen score shows under the group, and screen readers read it with the group. It works on touch, where a tooltip would not.
Conviction
Additional backchannel or tribal knowledge about this person
import { useState } from "react";
import { RadioGroup, RadioItem } from "@etched/reticle/radio";
const meanings = new Map([
["1", "Profile looks good, unsure"],
["2", "Profile looks extra good, unsure"],
["3", "Additional backchannel or tribal knowledge about this person"],
["4", "Certified superstar has been deeply referenced"],
]);
export function ADescriptionThatFollowsTheValue() {
const [score, setScore] = useState("3");
return (
<RadioGroup legend="Conviction" appearance="segmented" value={score} onValueChange={(value) => setScore(String(value))} description={meanings.get(score)}>
{[...meanings.keys()].map((value) => (
<RadioItem key={value} value={value} label={value} />
))}
</RadioGroup>
);
}API reference
RadioGroup
| Prop | Type | Default | Description |
|---|---|---|---|
| legend | ReactNode | – | Title of the group. Without it, add a RadioLegend child or an aria-label. |
| children * | ReactNode | – | RadioItem children, and optionally a RadioLegend. |
| orientation | "vertical" | "horizontal" | "vertical" | |
| appearance | "default" | "card" | "segmented" | "default" | Card items join into one card. |
| description | ReactNode | – | Help text below the group. Hidden while an error shows. |
| error | FieldError | – | Error below the group. It marks every radio invalid. |
| controlPosition | "start" | "end" | "start", or "end" for cards | |
| required | boolean | – | false adds the optional marker after the legend. true makes a choice required in a form. |
| className | string | – | Class on the outer element, for layout. |
RadioItem
| Prop | Type | Default | Description |
|---|---|---|---|
| variant | "default" | "error" | "default" | |
| appearance | RadioItemAppearance | – | Overrides the group's default or card appearance. A segmented group ignores it. |
| label * | ReactNode | – | |
| description | ReactNode | – | Secondary text under the label. It describes the option; it is not part of its name. Not shown when segmented. |
| value * | T | – | |
| className | string | – | Class on the outer element, for layout. |
RadioLegend
| Prop | Type | Default | Description |
|---|---|---|---|
| children * | ReactNode | – | |
| className | string | – |