# Number Stepper

> Minus, the number, plus. For small counts like seats, guests, or minutes, and for live scores.

- Name: `number-stepper`
- Status: beta
- Categories: forms
- File: the install writes `number-stepper.tsx` to your `ui` alias, `@/components/ui` by default.
- npm packages: `@base-ui/react`. The install adds them.
- Other registry items: `@arbokui/input`, `@arbokui/motion-timing`. The install adds them.
- Gallery: https://arbokui.com/components/number-stepper
- Registry item: https://arbokui.com/r/number-stepper.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/number-stepper
   ```

## Use it for

- Small counts people nudge up or down: seats offered, guests per table, minutes per meeting.
- Give it a name: aria-label, like "Seats offered", or an id that a visible label's htmlFor points at.
- Set min and max. The buttons stop there, and Home and End jump there.
- Put the unit in suffix, like "seats". For a unit that can change, pass a UnitSwitch as the suffix.
- Set score for a live scoreboard: no box, round buttons, and a big number.
- The number rolls when it changes, up when it grows and down when it shrinks. With motion off, it changes at once.

## Avoid it for

- Money and long numbers people type, like a price or a phone number. Use an input.
- Picking from a few named options. Use a radio group or a select.
- Ranges people drag across, like a volume. Use a slider.

## Props

- `value`: `number | null`. The number. Use it with onValueChange.
- `defaultValue`: `number`. The number at first render. Defaults to min, or 0.
- `onValueChange`: `(value: number | null) => void`. Called with each new number, or null when the text is cleared.
- `min`: `number`. The lowest number. The minus button stops there.
- `max`: `number`. The highest number. The plus button stops there.
- `step`: `number`. Default `1`. How much the buttons and the arrow keys move it.
- `largeStep`: `number`. Default `10`. How much Page Up and Page Down, or Shift with an arrow, move it.
- `size`: `"sm" | "md" | "lg"`. Default `"md"`. Height and text size. The same heights as Input.
- `score`: `boolean`. Default `false`. No box, round buttons, and a big number, for live scores.
- `format`: `Intl.NumberFormatOptions`. How the number shows, like { style: "percent" }.
- `suffix`: `ReactNode`. A muted unit after the number, like "seats", or a UnitSwitch.
- `disabled`: `boolean`. Default `false`. Blocks the buttons and typing.
- `name`: `string`. Form field name. The number is sent with the form.
- `aria-label`: `string`. Names the number. Required unless you pass aria-labelledby, or an id with a visible label.
- `className`: `string`. Classes for the box. Later classes win.

## Accessibility

- The number is a text field, so people can type it as well as step it.
- Its type needs a name: aria-label, aria-labelledby, or an id for a label's htmlFor.
- The arrow keys step it, Shift with an arrow and Page Up and Page Down move 10, and Home and End jump to min and max.
- The buttons are named "Decrease" and "Increase", and they are not Tab stops, since the keys do the same. Holding one repeats.
- The suffix describes the field, so screen readers hear the unit after the number.
- The roll reads --motion-duration-base. When reduced motion sets it to 0, the number changes with no movement.

## Examples

### Seats, with min, max, and a unit

```tsx
<NumberStepper aria-label="Seats offered" defaultValue={4} min={1} max={8} suffix="seats" />
```

### The big score look

```tsx
<NumberStepper aria-label="Design score" score defaultValue={0} min={0} />
```

### Minutes, with a visible label

```tsx
<label htmlFor="length">Meeting length</label>
<NumberStepper id="length" defaultValue={45} min={10} max={60} step={5} size="sm" />
```
