# Stepper

> Slug: `stepper` · Category: Form Controls

## When to use

- Adjusting a small integer quantity (cart line item, ticket count, party size).
- When the user typically increments by 1 and the range is small.

## When NOT to use

- A multi-step wizard / progress indicator — use `StepIndicator`.
- Free numeric entry over a wide range — use `TextField type="number"`.
- A slider over a continuous range — Arrow does not currently include one; use a custom control.

## Variants & decision tree

| Need | Prop |
|---|---|
| Standard `−` button | `leftIcon="minus"` (default) |
| Show trash icon when count is 1 (cart "remove") | `leftIcon="delete"` + `onRemove` |

## States

At minimum (decrease disabled), at `maxCount` (increase disabled), normal, inverse, with-delete (count of 1 shows trash).

## Props

| Prop | Type | Default | Description |
|---|---|---|---|
| `currentCount` | `number` | — | Required. Controlled current value. |
| `maxCount` | `number` | — | Optional upper bound (disables `+` at max). |
| `leftIcon` | `'minus' \| 'delete'` | `'minus'` | When `'delete'`, renders trash at count 1. |
| `onIncrease` | `(nextCount) => void` | — | Fired on `+` click. |
| `onDecrease` | `(nextCount) => void` | — | Fired on `−` click. |
| `onRemove` | `() => void` | — | Fired when trash icon clicked (only with `leftIcon="delete"`). |
| `a11yLabelIncrease` | `string` | — | Override "Increase" label. |
| `a11yLabelDecrease` | `string` | — | Override "Decrease" label. |
| `a11yLabelRemove` | `string` | — | Override "Remove" label. |
| `inverse` | `boolean` | `false` | Dark-background styling. |

## Usage examples

```tsx
// Basic counter
const [count, setCount] = useState(1);
<Stepper
  currentCount={count}
  maxCount={10}
  onIncrease={() => setCount((c) => Math.min(10, c + 1))}
  onDecrease={() => setCount((c) => Math.max(0, c - 1))}
/>

// Cart row with delete-on-1
<Stepper
  currentCount={qty}
  maxCount={5}
  leftIcon="delete"
  onIncrease={() => setQty((c) => c + 1)}
  onDecrease={() => setQty((c) => c - 1)}
  onRemove={() => removeFromCart(item.id)}
/>
```

## Composition

Use inside cart line items, configurators, and ticket selectors. Pair with a price/total label that updates as the count changes.

## Accessibility

Buttons get default English labels — override `a11yLabelIncrease`, `a11yLabelDecrease`, `a11yLabelRemove` for localization or context (e.g., "Increase ticket count"). The current count is announced when buttons are activated. The component manages enabled/disabled state of its buttons based on `currentCount` vs `maxCount`.

## Do / Don't

- DO clamp `currentCount` in your handlers — the component does not enforce bounds itself.
- DO use `leftIcon="delete"` + `onRemove` for cart items where 0 means "remove".
- DON'T allow a free-form number entry inside Stepper — use a `TextField` for that.
- DON'T forget to update the surrounding total/price when `currentCount` changes.
