Segmented Control
Shared Astro style recipes for related radio choices, navigation links, and pressed buttons.
---
import { RadioGroup } from "@/components/ui/radio-group"
import { Toggle } from "@/components/ui/toggle"
import {
segmentedControlRootClassName,
segmentedControlItemVariants,
} from "@gnosticai/ui/lib/segmented-control"
---
<div class="grid gap-6">
{(["sm", "default", "lg"] as const).map((size) => (
<RadioGroup
name={`billing-${size}`}
defaultValue="monthly"
aria-label={`Billing period, ${size}`}
class={`${segmentedControlRootClassName} flex-row`}
>
<button
type="button"
data-slot="radio-group-item"
data-value="monthly"
class={segmentedControlItemVariants({ size, state: "checked" })}
>
Monthly
</button>
<button
type="button"
data-slot="radio-group-item"
data-value="yearly"
class={segmentedControlItemVariants({ size, state: "checked" })}
>
Yearly
</button>
</RadioGroup>
))}
<nav aria-label="Project guides" class={segmentedControlRootClassName}>
<a
class={segmentedControlItemVariants({ state: "current" })}
href="/components/segmented-control"
aria-current="page"
>
Choices
</a>
<a
class={segmentedControlItemVariants({ state: "current" })}
href="/components/tabs"
>
Panels
</a>
<a
class={segmentedControlItemVariants({ state: "current" })}
href="/components/group"
>
Actions
</a>
</nav>
<div
role="group"
aria-label="Independent view options"
class={segmentedControlRootClassName}
>
<Toggle
defaultPressed
class={segmentedControlItemVariants({ state: "pressed" })}
>
Guides
</Toggle>
<Toggle class={segmentedControlItemVariants({ state: "pressed" })}>
Grid
</Toggle>
</div>
</div>
Installation
bunx --bun shadcn@latest add @gnosticai/segmented-control
The recipe supplies classes. Add the control used by your composition:
bunx --bun shadcn@latest add @gnosticai/radio-group @gnosticai/toggle
Usage
import {
segmentedControlRootClassName,
segmentedControlItemVariants,
segmentedControlItemLayoutClassName,
} from "@/lib/segmented-control"
Apply the root class to a group or navigation element. Apply the item recipe to
each child. The radio example uses the existing RadioGroup controller with
button elements marked data-slot="radio-group-item" and stable data-value
attributes. Its flex-row class changes the group’s usual column layout.
API reference
size accepts sm, default, or lg. state accepts checked, current, or
pressed. These recipes read data-checked, aria-current="page", and
aria-pressed="true", respectively. Read the source
for all exported classes and types.
Choose the underlying control
Use Radio Group for one form value. Use links for destinations, with
aria-current="page" on the current route. Use Toggle for independent options.
Adjacent toggles do not become an exclusive Toggle Group. Use
Tabs when a choice shows a matching content panel; Tabs keeps
its own indicator and panel relationships.
Keyboard and form behavior
The recipe has no client script. Radio Group keeps its arrow-key selection and named form value. Links keep browser navigation and open-in-new-tab behavior. Toggle keeps its pressed state and Space activation. Give each group a name that explains the shared choice and test disabled items in the final page.
Theme and layout
The outer muted surface and selected item use shared theme tokens. Keep the labels short and choose one size for the group. A long row can need a different layout on a phone; a segmented appearance does not provide overflow controls. Use Group for joined action buttons with visible borders.