# NativeSelect

The platform’s own picker, restyled where it can be.

- Group: Forms
- Import: `import { NativeSelect } from '@misoto22/folio'`
- Page: https://ui.misoto22.com/components/native-select/
- Related: select, field

## When to reach for it

The escape hatch, not the default. Reach for it where the platform genuinely wins: a very long list on a phone, a form that must work without JavaScript, a page counting its last kilobyte.

## Anatomy

- **Wrapper** (required) — A relative <div> around the pair, and the element className lands on. It is the only control in this group where className does not go to the field itself, because the chevron is pinned to this box: a width set anywhere else strands the arrow at the far edge of the row.
- **Control box** (required) — The <select> on CONTROL_BASE, appearance-none so the platform’s own arrow is gone, with pe-9 of end padding so the longest option clears the drawn one. It fills the wrapper, so the wrapper’s width is the field’s width.
- **Chevron** (required) — A pointer-events-none icon pinned to the wrapper’s end edge. It is ours, not the platform’s, so it does not flip or move when the picker opens.
- **Option list** — children, drawn by the operating system on open. <option> and <optgroup> are the only things in it, and neither takes these tokens.

## Best practices

### Do

- Give it an explicit empty first option, or a defaultValue. A <select> nobody touches has its first option selected, so an untouched form submits the top of the list as though someone chose it.
- Group with <optgroup>: it is the one piece of structure the OS picker actually renders, and there is no styled equivalent to fall back on the way SelectLabel gives Select one.
- Set the width with className. It lands on the wrapper the chevron is pinned to and the select fills it, so the arrow travels with the edge of the field rather than staying where the row ends.

### Don’t

- multiple and size do not survive the styling: appearance-none plus a chevron pinned to the middle of the wrapper turns a list box into a scrolling column with an arrow drawn across it. Use checkboxes, or a multiple Combobox.
- Do not use the first option as the label. “Select a country” is announced as a choosable value and it is the value an untouched form submits — put the name in a Field and give that option value="" and disabled.
- Do not send the control’s own ink or border through className: it dresses the wrapper, and the <select> inside keeps CONTROL_BASE whatever the box around it says.

## Accessibility

- Typeahead and the mobile wheel come free, from the browser.
- What it cannot do is look like the rest of the system once open — the option list is drawn by the operating system and carries none of these tokens.

## Keyboard

- Space / ↓ — Opens the platform picker.
- a–z — Typeahead, from the browser’s own implementation.

## NativeSelect

A native `<select>`, restyled. The escape hatch, not the default — `Select` is the styled one. Reach for this where the platform's own picker is genuinely better: a very long list on a phone, a form that must work without JavaScript, a page where the last kilobyte matters. The browser gives typeahead and the mobile wheel for free, and those are real. What it cannot do is look like the rest of the system once open. The option list is drawn by the operating system, so it carries none of these tokens — which is exactly why it stopped being the default. `className` sizes the WRAPPER, not the `<select>` inside it. This is the one control here that renders two elements, and the chevron is pinned to the wrapper's end edge: a width on the select alone narrowed the box and left the arrow floating at the far side of the row. The select fills whatever the wrapper is.

### Props

- `invalid` — `boolean`. Paints the resting border with `--danger` and reflects `aria-invalid`.
- `ref` — `Ref<HTMLSelectElement>`.

Also accepts: `SelectHTMLAttributes<HTMLSelectElement>`.

## Example — default

```tsx
import { Field, NativeSelect } from '@misoto22/folio'

<Field
  label="Density"
  hint="The platform's own picker — better on a phone, and it works without JavaScript."
  className="w-full max-w-xs"
>
  <NativeSelect defaultValue="mid">
    <option value="tight">Tight</option>
    <option value="mid">Comfortable</option>
    <option value="loose">Loose</option>
  </NativeSelect>
</Field>
```

## Example — a placeholder option

```tsx
import { Field, NativeSelect } from '@misoto22/folio'

<Field
  label="Country"
  required
  hint="Where the invoice is issued from."
  className="w-full max-w-xs"
>
  <NativeSelect defaultValue="" required>
    <option value="" disabled>
      Select a country
    </option>
    <option value="au">Australia</option>
    <option value="jp">Japan</option>
    <option value="nz">New Zealand</option>
    <option value="sg">Singapore</option>
  </NativeSelect>
</Field>
```

## Example — option groups

```tsx
import { Field, NativeSelect } from '@misoto22/folio'

<Field label="Time zone" className="w-full max-w-xs">
  <NativeSelect defaultValue="Australia/Sydney">
    <optgroup label="Australia">
      <option value="Australia/Perth">Perth</option>
      <option value="Australia/Adelaide">Adelaide</option>
      <option value="Australia/Sydney">Sydney</option>
    </optgroup>
    <optgroup label="Asia">
      <option value="Asia/Singapore">Singapore</option>
      <option value="Asia/Tokyo">Tokyo</option>
    </optgroup>
  </NativeSelect>
</Field>
```
