shadcn.io is an independent project built on shadcn/ui
Two rows share one field and one direction, starting at Name, ascending. The first is Name, Date, and Size, then an arrow that flips direction. The second is Price, Rating, and Popular, and the selected field shows that arrow. Picking Price clears Name. The demo sorts nothing.
Use a separate arrow when every field can flip, and the in-button arrow when only the active field should show it. A long field list fits a Select. In Solid, Ark UI does not reorder the rows.
Installation
Anatomy

Usage
<div class="flex flex-col gap-4">
<ButtonGroup aria-label="Sort by">
{FIELDS.map(item => <Button aria-pressed={sortField() === item.value} onClick={handleField} size="sm" value={item.value} variant={sortField() === item.value ? "default" : "outline"}>
{item.label}
</Button>)}
<Button aria-label={sortOrder() === "asc" ? "Sort ascending" : "Sort descending"} onClick={toggleOrder} size="icon-sm" variant="outline">
{sortOrder() === "asc" ? <ArrowUpIcon aria-hidden="true" /> : <ArrowDownIcon aria-hidden="true" />}
</Button>
</ButtonGroup>
<ButtonGroup aria-label="Sort products by">
{PRODUCT_FIELDS.map(item => <Button aria-pressed={sortField() === item.value} onClick={handleField} size="sm" value={item.value} variant={sortField() === item.value ? "default" : "outline"}>
{item.label}
{sortField() === item.value && sortOrder() === "asc" && <ArrowUpIcon aria-hidden="true" data-icon="inline-end" />}
{sortField() === item.value && sortOrder() === "desc" && <ArrowDownIcon aria-hidden="true" data-icon="inline-end" />}
</Button>)}
</ButtonGroup>
</div>FAQ
What is tracked?
Two values: the field, starting at name, and the direction, asc or desc, starting at asc. Every field button stores its own value as the field.
How does the direction button work?
It flips between ascending and descending and shows an up or down arrow. Its aria-label says Sort ascending or Sort descending, so the icon-only button has a name.
How does the second row show the direction?
The selected field's button adds an arrow after its label. The other buttons show no arrow. The direction itself only changes from the first row's arrow button.
Do the two rows share the field?
Yes. Picking Price clears Name. Use separate values if both rows should stay set.
API Reference
Button Group
Sets aria-label="Sort by" and aria-label="Sort products by". The rest is on the Button Group page.
Button
Sets aria-pressed={sortField() === item.value}, onClick={handleField}, size="sm", value={item.value}, variant and aria-label. The rest is on the Button page.





