skrewww

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.

Surface
Shape

Interactive

Direct numeric entry — not currency. Prefer Slider for visual adjust.

Value: 1

Decimal step

Error

Must be 10 or less.

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

PropTypeDefaultDescription
labelstringVisible Form Field label.
valuenumber | nullControlled value. null means empty.
defaultValuenumber | nullUncontrolled initial value. null means empty.
onValueChange(value: number | null) => voidFires when the committed numeric value changes.
minnumberMinimum. Applied on blur/step/arrows, not every keystroke.
maxnumberMaximum. Applied on blur/step/arrows, not every keystroke.
stepnumber1Step size for arrows and steppers; used for snap on commit.
showSteppersbooleantrueShows increment/decrement controls.
size"sm" | "md" | "lg""md"Control size matching Text Input.
disabledbooleanfalseDisables input and steppers.
readOnlybooleanfalseRead-only input; steppers disabled.
requiredbooleanfalseMarks the field required.
errorstringError message via Form Field.
supportingTextstringHelp 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."
    />
  );
}