# NumberInput
A number field with stepper buttons, keyboard steps, formatting, wheel and scrub.
```ehtml Example
import { NumberInput } from "@elements/ui/number-input";
```
## Parts
| Part | What it is |
|---|---|
| `.number-input` | The root. |
| `.number-input-scrub` | The scrub label. |
| `.number-input-group` | The bordered control holding the input and buttons. |
| `.number-input-input` | The text input, role=spinbutton. |
| `.number-input-button` | Decrement and increment buttons. |
| `.number-input-cursor` | The cursor drawn while a scrub holds pointer lock. |
## Attributes
| Attribute | Type | Default | Description |
|---|---|---|---|
| `value` | `number \| null` (two-way) | `null` | The number; null when the input is empty. |
| `min` | `number \| undefined` | `undefined` | Lowest value. Home goes here. |
| `max` | `number \| undefined` | `undefined` | Highest value. End goes here. |
| `step` | `number \| "any"` | `1` | Arrow and button step. "any" steps by 1. |
| `smallStep` | `number` | `0.1` | Alt + arrow step. |
| `largeStep` | `number` | `10` | Shift + arrow, PageUp and PageDown step. |
| `snapOnStep` | `boolean` | `false` | Steps land on multiples of the step. |
| `allowOutOfRange` | `boolean` | `false` | Typed values may stay outside min and max (marked invalid). |
| `allowWheelScrub` | `boolean` | `false` | The mouse wheel steps while the input is focused and hovered. |
| `format` | `Intl.NumberFormatOptions` | `{}` | Display format: currency, percent, unit, fraction digits, grouping. |
| `locale` | `string` | `""` | Locale for formatting and parsing; empty uses the browser's. |
| `buttons` | `"sides" \| "end" \| "none"` | `"sides"` | Minus and plus on each side, stacked chevrons at the end, or none. |
| `size` | `"sm" \| "md" \| "lg"` | `"md"` | Height and type size from the control tokens. |
| `scrubLabel` | `string` | `""` | A label above the input you drag to change the value. |
| `scrubDirection` | `"horizontal" \| "vertical"` | `"horizontal"` | Which drag direction scrubs. |
| `pixelSensitivity` | `number` | `2` | Pixels of drag per step. |
| `teleportDistance` | `number` | `0` | How far the scrub cursor travels before it loops back; 0 loops at the window's edge. |
| `placeholder` | `string` | `""` | Shown while empty. |
| `name` | `string` | `""` | Submits the raw number with the form. |
| `form` | `string` | `""` | The id of the form the value belongs to. |
| `disabled` | `boolean` | `false` | Not interactive. |
| `readonly` | `boolean` | `false` | Focusable and readable, not editable. |
| `required` | `boolean` | `false` | The form will not submit while empty. |
| `invalid` | `boolean` | `false` | Marks the field invalid. |
| `incrementLabel` | `string` | `"Increase"` | The increment button's accessible name. |
| `decrementLabel` | `string` | `"Decrease"` | The decrement button's accessible name. |
| `roleDescription` | `string` | `"Number field"` | aria-roledescription on the input. |
| `class` | `string` | `""` | Extra classes on the root. |
| `attrs` | `Record` | `{}` | Attributes for the input: id, aria-label, data-\*. |
| `onValueChange` | `(value: number \| null, details: NumberInputChangeDetails) => void` | `() => {}` | Every change, with details.reason, details.direction and details.cancel(). |
| `onValueCommit` | `(value: number \| null, details: NumberInputCommitDetails) => void` | `() => {}` | When a change is final: blur, Enter, a key step, a button release, wheel or scrub end. |
## Keyboard
| Keys | Action |
|---|---|
| `ArrowUp / ArrowDown` | Step up or down. |
| `Shift + ArrowUp / ArrowDown` | Large step. |
| `Alt + ArrowUp / ArrowDown` | Small step. |
| `PageUp / PageDown` | Large step. |
| `Home / End` | Go to min or max, when set. |
| `Enter` | Formats, clamps and commits the typed value. |
## Data attributes
| Attribute | Values | On |
|---|---|---|
| `data-disabled` | present when disabled | root, scrub, group, input, buttons, cursor |
| `data-readonly` | present when read-only | root, scrub, group, input, buttons, cursor |
| `data-required` | present when required | root, scrub, group, input, buttons, cursor |
| `data-step-mismatch` | present while the value is off step from an explicit min; the form will not submit | root |
| `data-invalid` | present when invalid or out of range | root, scrub, group, input, buttons, cursor |
| `data-focused, data-filled, data-dirty, data-touched, data-valid` | inside a Field, mirror the Field's state | root, scrub, group, input, buttons, cursor |
| `data-scrubbing` | present while scrubbing | root, scrub, group, input, buttons, cursor |
| `data-buttons` | `"sides" \| "end" \| "none"` | group |
| `data-direction` | `"up" \| "down"` | button |
## Accessibility
- The input is role=spinbutton with aria-valuenow, aria-valuemin, aria-valuemax and aria-valuetext for formatted values.
- The buttons are out of the tab order (the arrow keys do their job), named Increase and Decrease, and point at the input with aria-controls.
- A value outside min and max sets aria-invalid.
- The scrub label is the input's label, so it names the field too.
## See also
- [Slider](slider): Pick a value or a range by dragging thumbs along a track, with full keyboard support.
- [PasswordInput](password-input): A password field with a button that shows and hides what was typed.
- [InputOTP](input-otp): A one-time code in separate slots, with paste, SMS autofill and a pattern.
- [TagsInput](tags-input): Type a list of tags into one field: add, edit, delete and paste them from the keyboard.
- [ColorPicker](color-picker): Pick a color with an area, channel sliders, inputs, swatches and the eye dropper.
- [FileUpload](file-upload): Drop, paste or browse for files; see them listed with previews, sizes, errors and progress.