shadcn.io is an independent project built on shadcn/ui
One setting, two rows, starting on Comfortable. The first names Compact, Comfortable, and Spacious. The second is XS, S, M, L, and XL. Picking M clears Comfortable. The S button stores sm. The demo does not change any spacing.
Use one row unless both really are the same choice. Arrow keys inside a single control need a Toggle Group. In React, Base UI does not change your row padding from this value.
Installation
Anatomy

Usage
<div className="flex flex-col gap-4">
<ButtonGroup aria-label="Density">
{DENSITIES.map((item) => (
<Button
aria-pressed={density === item.value}
key={item.value}
onClick={handleSelect}
size="sm"
value={item.value}
variant={density === item.value ? "default" : "outline"}
>
{item.label}
</Button>
))}
</ButtonGroup>
<ButtonGroup aria-label="Size">
{SIZES.map((item) => (
<Button
aria-pressed={density === item.value}
key={item.value}
onClick={handleSelect}
size="sm"
value={item.value}
variant={density === item.value ? "default" : "outline"}
>
{item.label}
</Button>
))}
</ButtonGroup>
</div>FAQ
How is the selected density tracked?
One value, starting at comfortable. Each button carries its own value, and pressing it stores that value. The matching button gets variant="default" and aria-pressed, the others outline.
Do the two rows share a selection?
Yes. They write the same value, so picking M clears Comfortable. Give each row its own value if they should be independent.
Why does the second row say S but store sm?
The labels and values are separate in the data: S, M and L are shown while sm, md and lg are stored. Change either list to suit your settings.
Does the demo change any spacing?
No. It only stores the choice. Read it where you render your rows, for example to pick padding classes.
What size are the buttons?
All use size="sm", so the picker stays small next to the content it controls.
How does the keyboard move through it?
Tab moves from button to button, and Enter or Space selects. For Arrow key movement inside one tab stop, use a Toggle Group.
API Reference
Button Group
Sets aria-label="Density" and aria-label="Size". The rest is on the Button Group page.
Button
Sets aria-pressed={density === item.value}, onClick={handleSelect}, size="sm", value={item.value} and variant={density === item.value ? "default" : "outline"}. The rest is on the Button page.





