# Select Aligned (/docs/select-aligned)

A select whose panel opens on the trigger, with the selected row landing on the value it replaces.

## Install

```bash
npx shadcn@latest add https://4af.selfsimilar.dev/r/select-aligned.json
```

## Usage

```tsx
import {
  AlignedSelect,
  AlignedSelectContent,
  AlignedSelectItem,
  AlignedSelectTrigger,
} from "@/components/ui/select-aligned";

<AlignedSelect size="xs" value={city} onValueChange={setCity}>
  <AlignedSelectTrigger variant="ghost" label="City" placeholder="Pick a city" />
  <AlignedSelectContent>
    <AlignedSelectItem value="Lisbon">Lisbon</AlignedSelectItem>
    <AlignedSelectItem value="Oslo">Oslo</AlignedSelectItem>
  </AlignedSelectContent>
</AlignedSelect>
```

## API

**Main exports:** `AlignedSelect`, `AlignedSelectTrigger`, `AlignedSelectContent`,
`AlignedSelectItem`, `AlignedSelectGroup`, `AlignedSelectGroupLabel`

A single-select whose panel opens over the trigger, with the selected row on top of the value it
replaces, and scales out from that row. The trigger shows a `ChevronsUpDown` glyph, since the
list can extend in both directions.

Both `value` / `defaultValue` and `open` / `defaultOpen` support controlled and uncontrolled use.
Calling `details.cancel()` in `onValueChange` retains the accepted selection, including the
row offered when reopening. Calling it in `onOpenChange` retains the previous open state.

## Trigger variants

`AlignedSelectTrigger` accepts `variant="default" | "ghost" | "plain"`. `default` is the
full-width field. `ghost` uses Button's muted ghost colours with the default spacing, so the
value and glyph stay aligned. `plain` removes the background, border, and horizontal padding,
leaving the glyph directly beside the label. The focus ring remains for keyboard users.
`outline={false}` is still accepted as an alias for `variant="plain"`.

It also accepts an optional `label`, rendered as a dimmed prefix before the value. For example,
`label="Fruit"` displays `Fruit: Banana`.

By default, default and ghost panels match the trigger width; plain panels fit their content.
Set `width="trigger"` or `width="content"` on `AlignedSelectContent` to override.

```tsx
<AlignedSelectTrigger variant="plain" label="City" placeholder="Pick a city" />
```

## Overlapping the trigger

The overlap applies to pointer input only. Opened from the keyboard, or when there is no room,
such as near a viewport edge or with a list too long to centre on the selected row, the panel
opens as an ordinary dropdown below the trigger.

Row labels line up with the trigger's value because the panel's padding plus a row's padding
equals the trigger's padding. If you restyle one, restyle the others to match. The selected
check sits in a reserved slot at the end of the row, so it doesn't indent the labels.

## Sections

Wrap rows in `AlignedSelectGroup` with an `AlignedSelectGroupLabel` for a dimmed heading. The
heading is tied to its options, and a group hides itself when a search filters out all of its
rows.

```tsx
<AlignedSelectGroup>
  <AlignedSelectGroupLabel>Northern</AlignedSelectGroupLabel>
  <AlignedSelectItem value="Oslo">Oslo</AlignedSelectItem>
</AlignedSelectGroup>
```

## Search

Add `searchable` to `AlignedSelectContent` for a search field pinned above the list. It takes
focus when the panel opens, with a row already active: the selected row, or the first row when
nothing is chosen, so Enter takes it without an arrow press. Typing moves the active row to the
top match. The active row carries `data-panel-active`. The arrow keys move focus into
the list, and Base UI's own highlight takes over from there. The query clears when the panel
closes. `autoFocusFirstItem={false}` opens the panel with no active row.

Filtered rows leave the list rather than hiding, so the panel resizes to what is left. Rows
match on their text, which only works when the children are a plain string — give a row built
from elements an explicit `searchValue`. `emptyMessage` replaces the list when a query
matches nothing, and `searchPlaceholder` and `searchLabel` name the field.

Because the panel is positioned by the selected row, the search field sits that far above the
trigger. With a selection deep in a long list there may be no room for the overlap, and the panel
opens as an ordinary dropdown.

## Sizing

Use `size="xs"` for a compact 24px trigger and popup rows, `size="sm"` for the 30px compact
control, or `size="md"` for the 34px form-row control. The `xs` and `sm` trigger sizes use a 6px
button radius; `md` uses 8px.