# Unit Switch

> The unit beside a value, like £, min, or km, that you can change in place. Click it for a menu of units. On a change the unit crossfades to the new one, and a note can say what changed.

- Name: `unit-switch`
- Status: beta
- Categories: forms
- File: the install writes `unit-switch.tsx` to your `ui` alias, `@/components/ui` by default.
- Other registry items: `@arbokui/motion`, `@arbokui/field-frame`, `@arbokui/menu`. The install adds them.
- Gallery: https://arbokui.com/components/unit-switch
- Registry item: https://arbokui.com/r/unit-switch.json

## Install

1. Add the registry to `components.json`. Do this first, once:

   ```bash
   npx shadcn@latest registry add "@arbokui=https://arbokui.com/r/{name}.json"
   ```

   The command writes this entry. You can also add it by hand:

   ```json
   {
     "registries": {
       "@arbokui": "https://arbokui.com/r/{name}.json"
     }
   }
   ```

   Without this step, the shadcn CLI looks up `@arbokui` in a public list that Arbokui does not control.

2. Add the theme once per app. It replaces your color tokens with ones checked for contrast:

   ```bash
   npx shadcn@latest add @arbokui/theme
   ```

3. Add the component:

   ```bash
   npx shadcn@latest add @arbokui/unit-switch
   ```

## Use it for

- A value whose unit people may want to change: a fee in pounds or dollars, a length in minutes or hours, a distance in km or miles.
- Pass a set name as units: "currency", "duration", "distance", or "weight". Or pass your own list of ids and options.
- A currency's id is its code, like "GBP", so you can pass it straight to Intl.NumberFormat.
- Put it in a NumberStepper's suffix for a count with a unit. Beside plain text, put it before or after the value, as its place says.
- Give announce to say what changed, like { text: "Pounds changed to dollars", key: 3 }. Change the key to say it again.
- matchUnit reads a typed value, like "2 hours", and returns the unit in it. Use it to switch the unit when someone types one.

## Avoid it for

- A unit that never changes. Write it as text, or use NumberStepper's suffix.
- Picking anything that is not a unit. Use a select or a menu.

## Props

- `units`: `"currency" | "duration" | "distance" | "weight" | Array<string | UnitOption>`. Default `"currency"`. A built-in set, or your own list. A string in a list is a built-in id, like "mins", or a plain unit.
- `value`: `string`. The unit's id. Use it with onValueChange.
- `defaultValue`: `string`. The unit at first render. Defaults to the first one.
- `onValueChange`: `(id: string) => void`. Called with the new unit's id when someone picks one.
- `announce`: `{ text: string; key: number | string } | null`. Shows a note above the unit for a moment, and says it to screen readers.
- `size`: `"sm" | "md" | "lg"`. Default `"md"`. Height and text size, to match the field it sits in.
- `disabled`: `boolean`. Default `false`. Blocks the menu.
- `aria-label`: `string`. Names the button. Defaults to the unit's name, like "Pounds". A unit that shows letters puts them first, like "min, Minutes".
- `title`: `string`. The tooltip. Defaults to "Change currency", or "Change unit".
- `className`: `string`. Classes for the button. Later classes win.

## Accessibility

- The unit is a menu button: Enter, Space, or the down arrow opens the menu, and focus starts on the current unit.
- With a mouse it opens on the press. Let go on a unit to pick it, or click to leave the menu open.
- The button's name is the unit's name, like "Pounds", and its tooltip, "Change currency", describes it.
- A unit that shows letters puts them first in its name, like "min, Minutes", so people who speak commands can say what they see.
- Each unit is a radio item named by its name, so screen readers hear which one is checked.
- Picking a unit closes the menu and puts focus back on the button.
- The note is said once through a polite live region. The visible copy is hidden from screen readers, so it is not read twice.
- The label uses the shared MotionSwap fade and layout tokens. Motion off changes it immediately; reduced motion keeps only the fade.

## Examples

### Currency beside a price

```tsx
<p className="flex items-center gap-1">
  <UnitSwitch units="currency" defaultValue="GBP" />
  <span>6.00</span>
</p>
```

### Duration in a stepper

```tsx
<NumberStepper
  aria-label="Meeting length"
  defaultValue={90}
  suffix={<UnitSwitch units="duration" defaultValue="mins" />}
/>
```

### Switch it from typed text

```tsx
const unit = matchUnit("2 hours", "duration"); // { id: "hours", ... }
```
