Forms
Number Input
Number Input is direct numeric entry with optional steppers and min/max/step — not currency, quantity business logic, or a Slider.
State via Form Field (default/error/disabled/readOnly) — React-first (no Figma master yet)
betaReact AvailableFigma UnavailableDocs Partial
- React last updated
- 2026-09-14
- Documentation last updated
- 2026-09-14
- Accessibility target
- WCAG 2.2 AA (target)
- Version
- 0.1.0-beta
Known open questions
- Figma master not created yet — intentional React-first CE-2 sequence; design follow-up later.
- Locale/currency formatting explicitly out of scope for 0.1.0-beta.
Live preview
Text + spinbutton ARIA with optional steppers. Constraints apply on blur/step/arrows.
Interactive
Direct numeric entry — not currency. Prefer Slider for visual adjust.
Value: 1
Decimal step
Error
Disabled
Read-only
Without steppers
Purpose
Number Input is a direct numeric entry field with optional increment/decrement steppers, min/max/step constraints, and Form Field label/validation integration. It is not a currency field, quantity cart control, calculator, or Slider.
Anatomy
NumberInput = FormField + TextInputControl (type=text, role=spinbutton) + optional Increment/Decrement stepper buttons.
Variants and states
default · error · disabled · readOnly
When to use
When users must type or step a numeric value (counts, thresholds, settings). Prefer Slider when a bounded visual adjustment is a better interaction.
When not to use
Currency/money formatting, unit conversion, range selection (use two fields or a dedicated range control), or visual continuous adjustment (use Slider).
Accessibility
spinbutton semantics on a text input (not native type=number): aria-valuemin/max/now when applicable, labeled via Form Field, Increment/Decrement buttons with accessible names. Constraints commit on blur/step/arrows so intermediate drafts remain editable.
Keyboard behavior
Type digits/decimal/minus. Arrow Up/Down step by `step`. Enter commits. Blur clamps/snaps to min/max/step. Intermediate drafts (-, 1.) allowed while focused.
Common mistakes
Using native type=number for Skrewww chrome. Treating this as currency. Clamping on every keystroke. Confusing with Slider. Assuming a Figma master exists (React-first / Figma pending).
Properties
React: label, value/defaultValue/onValueChange (number | null), min, max, step, showSteppers, size, disabled, readOnly, required, error, supportingText, placeholder.
Number Input vs Slider?
Use Number Input for direct numeric entry and stepping. Use Slider when a bounded visual adjustment is the primary interaction.
Is this a currency field?
No. There is no locale, currency symbol, or money-precision API. Build currency on top of app logic, not this primitive.
Why not input type=number?
Native number inputs have inconsistent spinner chrome, awkward intermediate values, and weaker styling control. Skrewww uses text + spinbutton ARIA with optional steppers.
Tokens used
component/radius/controlsemantic/border/defaultsemantic/focus-ringsemantic/icon/mutedsemantic/action/danger
Known limitation
React-first CE-2B — no Figma component set yet (Figma pending). No locale/currency/precision APIs. /r deferred to CE-3. Empty value is null.
Component API
| Prop | Type | Default | Description |
|---|---|---|---|
| label | string | — | Visible Form Field label. |
| value | number | null | — | Controlled value. null means empty. |
| defaultValue | number | null | — | Uncontrolled initial value. null means empty. |
| onValueChange | (value: number | null) => void | — | Fires when the committed numeric value changes. |
| min | number | — | Minimum. Applied on blur/step/arrows, not every keystroke. |
| max | number | — | Maximum. Applied on blur/step/arrows, not every keystroke. |
| step | number | 1 | Step size for arrows and steppers; used for snap on commit. |
| showSteppers | boolean | true | Shows increment/decrement controls. |
| size | "sm" | "md" | "lg" | "md" | Control size matching Text Input. |
| disabled | boolean | false | Disables input and steppers. |
| readOnly | boolean | false | Read-only input; steppers disabled. |
| required | boolean | false | Marks the field required. |
| error | string | — | Error message via Form Field. |
| supportingText | string | — | Help text when no error is present. |
React example
Copy React example
import { useState } from "react";
import { NumberInput } from "@/components/ui/NumberInput";
export function Example() {
const [value, setValue] = useState<number | null>(1);
return (
<NumberInput
label="Quantity"
value={value}
onValueChange={setValue}
min={0}
max={99}
step={1}
supportingText="Direct numeric entry — not currency formatting."
/>
);
}