Number Input
A numeric field with stepper buttons, bounds, and step snapping, driven by pointer or keyboard.
Live demo
Number Input edits a single number. A role="spinbutton" text field sits between decrease and increase buttons; the buttons, the up/down arrow keys, and direct typing all change the value, which snaps to step and clamps to [min, max] on commit.
It is form-associated; give it a name and its value submits with the form. It accepts decimals (e.g. a 0.01 step for currency). A second granularity rides alongside step: holding the modifier (Shift by default) on a click or arrow applies altStep, ten times step out of the box, while PageUp/PageDown always jump by it, and altDefault flips which one is primary. Out-of-range typing reverts on commit, and the stepper buttons disable at the bounds. Three sizes: sm, the default md, and lg.
When to use
How this component composes with the rest of the set.
Props
13 props, straight from the manifest.
| Prop | Type | Default | Bindings | Description |
|---|---|---|---|---|
Appearance
Sizes
sm
Compact.
md
Default.
lg
Large.
States
focus-within
The input is focused. The control border takes the accent and a token ring appears.
step-hover
Pointer over a stepper button: the hover tint.
step-disabled
A stepper button at its bound: muted and non-interactive.
disabled
The whole field disabled: muted control, no typing or stepping.
Anatomy
The named parts that make up the component, with their selectors.
number
The wrapper carrying the size and disabled classes and stacking the optional label over the control.
control
The bordered row grouping the stepper buttons and the input; shows the focus ring when the input is focused.
input
The role="spinbutton" text field, centered, accepting typed numbers.
step
The decrease and increase buttons; out of the tab order (the input is the focus stop), disabled at the bounds.
label
The optional visible label, referenced as the field's accessible name.
Tokens & coverage
What the component consumes, checked live against what the algorithm produces.
Live coverage check against the xoji-default register
(derive(xojiDefault, { anchors }) →
coverComponent(manifest, register)). Every token this component
consumes must be a key the algorithm produces.
--accent
--bg-0
--border-thick
--border-thin
--duration-fast
--ease-standard
--fg-0
--fg-1
--fg-disabled
--font-sans
--line-2
--neutral-bg
--radius-md
--ring
--space-1
--space-2
--space-3
--space-5
--space-6
--space-7
--state-disabled
--state-hover
--state-press
--text-body
--text-sm
Accessibility
Code
Bounds, steps, and form association
A bounded quantity, a stepped range, a currency field, and a disabled one.
<xoji-number-input label="Quantity" value="1" min="0" max="99"></xoji-number-input>
<xoji-number-input label="Step by 5" value="20" min="0" max="100" step="5"></xoji-number-input>
<xoji-number-input label="Price" value="9.99" min="0" step="0.01"></xoji-number-input>
<xoji-number-input label="Locked" value="42" disabled></xoji-number-input>
<script lang="ts">
import { NumberInput } from "@xoji/svelte";
let qty = $state(1);
</script>
<NumberInput label="Quantity" bind:value={qty} min={0} max={99} />
<NumberInput label="Step by 5" value={20} min={0} max={100} step={5} />
<NumberInput label="Price" value={9.99} min={0} step={0.01} />
<NumberInput label="Locked" value={42} disabled />
---
import { NumberInput } from "@xoji/astro";
---
<NumberInput label="Quantity" value={1} min={0} max={99} />
<NumberInput label="Step by 5" value={20} min={0} max={100} step={5} />
<NumberInput label="Price" value={9.99} min={0} step={0.01} />
<NumberInput label="Locked" value={42} disabled />
Alternate step and modifier
A volume that jumps by ten on Shift (or PageUp/PageDown), and a fine-tuner that steps by 0.1 plainly with Shift falling back to whole units via altDefault.
<xoji-number-input label="Volume" value="50" min="0" max="100" alt-step="10"></xoji-number-input>
<xoji-number-input label="Fine tune" value="1" step="1" alt-step="0.1" alt-default></xoji-number-input>
<NumberInput label="Volume" value={50} min={0} max={100} altStep={10} />
<NumberInput label="Fine tune" value={1} step={1} altStep={0.1} altDefault />
<NumberInput label="Volume" value={50} min={0} max={100} altStep={10} />
<NumberInput label="Fine tune" value={1} step={1} altStep={0.1} altDefault />