<script setup>
import { NumberInput } from "@dicehub/kappa/components/number-input";
</script>
<template>
<NumberInput.Root
default-value="0.8"
:min="0"
:max="5"
:step="0.1"
:format-options="{ minimumFractionDigits: 1, maximumFractionDigits: 1 }"
>
<NumberInput.Label>Maximum Courant number</NumberInput.Label>
<NumberInput.Control>
<NumberInput.ScrubbableInput />
</NumberInput.Control>
</NumberInput.Root>
</template>Installation
Barrel
import {
NumberInput,
NumberInputControl,
NumberInputDecrementTrigger,
NumberInputIncrementTrigger,
NumberInputInput,
NumberInputLabel,
NumberInputRoot,
NumberInputScrubber,
NumberInputScrubbableInput,
NumberInputUnit,
NumberInputValueText,
} from "@dicehub/kappa";Granular
import {
NumberInput,
NumberInputControl,
NumberInputDecrementTrigger,
NumberInputIncrementTrigger,
NumberInputInput,
NumberInputLabel,
NumberInputRoot,
NumberInputScrubber,
NumberInputScrubbableInput,
NumberInputUnit,
NumberInputValueText,
} from "@dicehub/kappa/components/number-input";Usage
Keep controlled values as strings. This preserves intermediate input and locale-specific formatting. Use valueAsNumber from event details or context only at a numeric application boundary.
<script setup>
import { NumberInput } from "@dicehub/kappa/components/number-input";
</script>
<template>
<NumberInput.Root default-value="1" :min="0" :step="0.1" name="inlet-velocity">
<NumberInput.Label>Inlet velocity (m/s)</NumberInput.Label>
<NumberInput.Control>
<NumberInput.DecrementTrigger aria-label="Decrease inlet velocity" />
<NumberInput.Input />
<NumberInput.Unit aria-hidden="true">m/s</NumberInput.Unit>
<NumberInput.IncrementTrigger aria-label="Increase inlet velocity" />
</NumberInput.Control>
</NumberInput.Root>
</template>Composition
NumberInput.Root
├── NumberInput.Label
├── NumberInput.Control
│ ├── NumberInput.DecrementTrigger
│ ├── NumberInput.Input
│ ├── NumberInput.Unit
│ └── NumberInput.IncrementTrigger
├── NumberInput.ScrubbableInput (alternative)
└── NumberInput.ValueTextArk UI owns the number machine, spinbutton semantics, locale parsing, Pointer Lock behavior for the separate Scrubber, and press-and-hold triggers. Kappa owns the whole-field click, edit, and drag interaction in ScrubbableInput, plus the compound API, dense control geometry, states, and semantic tokens.
Rootowns the string value, range, steps, locale, and state.Labelnames the input. Include the physical unit in this text.Controlcontains the input, optional unit, and optional triggers.ScrubbableInputcombines a quiet value display, whole-field drag, and direct editing.Scrubberis an optional pointer target. Keep the input for keyboard access.
See the Ark UI Number Inputguide for the primitive behavior contract.
When you compose an external machine with RootProvider, repeatstep, smallStep, largeStep, andallowMouseWheel on the provider. These values configure Kappa's whole-field scrub and hover-wheel enhancements; the Ark machine still owns the value.
Examples
Basic
Direct entry, arrow keys, and explicit step triggers share one string value.
<script setup>
import { NumberInput } from "@dicehub/kappa/components/number-input";
</script>
<template>
<NumberInput.Root default-value="12" :min="1" :max="64">
<NumberInput.Label>Solver processes</NumberInput.Label>
<NumberInput.Control>
<NumberInput.DecrementTrigger aria-label="Decrease solver processes" />
<NumberInput.Input />
<NumberInput.IncrementTrigger aria-label="Increase solver processes" />
</NumberInput.Control>
</NumberInput.Root>
</template>Scrubbable Input
Use ScrubbableInput for the whole-field drag-and-edit pattern common in 3D applications. Its quiet display becomes a selected text editor on click. The first example uses only the scrubbable field. The second adds decrement and increment triggers on its left and right. It sets focusInputOnChange to false, so a step button keeps the center in display mode. Its editAlignment="center" option also keeps the number centered during direct editing. A horizontal drag starts after 10 pixels. Ctrl or Alt uses smallStep, Shift uses largeStep, and Escape restores the value from the start of the drag.
Drag after 10 px. Ctrl or Alt gives fine steps; Shift gives coarse steps. Escape restores.
<script setup>
import { NumberInput } from "@dicehub/kappa/components/number-input";
</script>
<template>
<NumberInput.Root
default-value="0.35"
:min="0"
:max="1"
:step="0.01"
:small-step="0.001"
:large-step="0.1"
>
<NumberInput.Label>Under-relaxation factor</NumberInput.Label>
<NumberInput.Control>
<NumberInput.ScrubbableInput :drag-threshold="10" />
</NumberInput.Control>
</NumberInput.Root>
<NumberInput.Root
default-value="12.5"
:min="-100"
:max="100"
:step="0.5"
:small-step="0.05"
:large-step="5"
:focus-input-on-change="false"
>
<NumberInput.Label>Clipping plane offset (mm)</NumberInput.Label>
<NumberInput.Control>
<NumberInput.DecrementTrigger aria-label="Decrease clipping plane offset" />
<NumberInput.ScrubbableInput edit-alignment="center" />
<NumberInput.IncrementTrigger aria-label="Increase clipping plane offset" />
</NumberInput.Control>
</NumberInput.Root>
</template>Compact Use
One 20px-high input field. Click to edit, drag to adjust, or hover to show the arrow buttons.
<script setup lang="ts">
import { ref } from "vue";
import NumberInputField from "./NumberInputField.vue";
const value = ref(2);
</script>
<template>
<div class="kappa-compact-number-demo" data-number-input-demo="compact-use">
<NumberInputField label="Value" :value="value" @change="value = $event" />
</div>
</template>
<style src="./number-input-compact-demo.css"></style>NumberInputField is a local docs helper, not a published Kappa component. This example keeps the compact field's existing edit and drag behavior, with no card or linked values.
Example behavior
- Drag after 10px. Ctrl ×100, Shift ×0.02, Alt ×10; the first active modifier wins. Escape cancels a drag.
- Arrow buttons step on release and repeat after a 400ms hold, through Ark's context API.
- Typed edits save on blur, Enter, or Escape. Empty or invalid text restores the previous value; decimal commas are accepted. The minimum is zero.
- Hover shows the complete value after 500ms. The reference font and compatibility rules apply only to this example, not the default Number Input.
Separate Scrubber Handle
Use Scrubber when the design needs a separate drag handle. Click or focus the input to type an exact value. This lower-level Ark interaction uses step.
<script setup>
import { NumberInput } from "@dicehub/kappa/components/number-input";
</script>
<template>
<NumberInput.Root default-value="0.35" :min="0" :max="1" :step="0.01">
<div class="label-line">
<NumberInput.Label>Under-relaxation factor</NumberInput.Label>
<NumberInput.Scrubber title="Drag horizontally to adjust" />
</div>
<NumberInput.Control>
<NumberInput.DecrementTrigger aria-label="Decrease under-relaxation factor" />
<NumberInput.Input />
<NumberInput.IncrementTrigger aria-label="Increase under-relaxation factor" />
</NumberInput.Control>
</NumberInput.Root>
</template>Min and Max
Set a valid range with min and max. Kappa restricts changes to the range by default. allowOverflow also disables the default blur clamp; set clampValueOnBlur explicitly when both behaviors are required.
<script setup>
import { NumberInput } from "@dicehub/kappa/components/number-input";
</script>
<template>
<NumberInput.Root default-value="300" :min="250" :max="400" :step="5">
<NumberInput.Label>Inlet temperature (K)</NumberInput.Label>
<NumberInput.Control>
<NumberInput.DecrementTrigger aria-label="Decrease inlet temperature" />
<NumberInput.Input />
<NumberInput.Unit aria-hidden="true">K</NumberInput.Unit>
<NumberInput.IncrementTrigger aria-label="Increase inlet temperature" />
</NumberInput.Control>
</NumberInput.Root>
</template>Precision
Pair a decimal step with Intl fraction-digit options for stable engineering precision.
<script setup>
import { NumberInput } from "@dicehub/kappa/components/number-input";
</script>
<template>
<NumberInput.Root
default-value="0.0025"
:min="0.0001"
:step="0.0001"
:format-options="{ minimumFractionDigits: 4, maximumFractionDigits: 4 }"
>
<NumberInput.Label>Time step (s)</NumberInput.Label>
<NumberInput.Control>
<NumberInput.Input />
<NumberInput.Unit aria-hidden="true">s</NumberInput.Unit>
</NumberInput.Control>
</NumberInput.Root>
</template>Units
Use Unit for a short visual suffix, or use Intl unit formatting when it fits. A suffix does not name the quantity. Put the unit in Label and mark a repeated suffix as decorative.
<script setup>
import { NumberInput } from "@dicehub/kappa/components/number-input";
</script>
<template>
<NumberInput.Root default-value="18.5" :step="0.5">
<NumberInput.Label>Cell size (mm)</NumberInput.Label>
<NumberInput.Control>
<NumberInput.Input />
<NumberInput.Unit aria-hidden="true">mm</NumberInput.Unit>
</NumberInput.Control>
</NumberInput.Root>
<NumberInput.Root
default-value="12.5"
locale="en-GB"
:format-options="{ style: 'unit', unit: 'liter-per-second' }"
>
<NumberInput.Label>Volume flow rate</NumberInput.Label>
<NumberInput.Control><NumberInput.Input /></NumberInput.Control>
</NumberInput.Root>
</template>Controlled and Commit
Use v-model for live state and valueCommit for work that must wait for blur or Enter, such as an expensive solver update.
<script setup>
import { ref } from "vue";
import { NumberInput } from "@dicehub/kappa/components/number-input";
const value = ref("0.005");
const committed = ref("0.005");
</script>
<template>
<NumberInput.Root
v-model="value"
:min="0.0001"
:max="0.1"
:step="0.001"
@value-commit="committed = $event.value"
>
<NumberInput.Label>Write interval (s)</NumberInput.Label>
<NumberInput.Control>
<NumberInput.DecrementTrigger aria-label="Decrease write interval" />
<NumberInput.Input />
<NumberInput.Unit aria-hidden="true">s</NumberInput.Unit>
<NumberInput.IncrementTrigger aria-label="Increase write interval" />
</NumberInput.Control>
</NumberInput.Root>
<output aria-live="polite">Committed: {{ committed }}</output>
</template>States
Disabled blocks focus and changes. Read-only permits focus and selection but blocks triggers and scrubbing. Invalid needs a clear text description.
Enter a value from 0 to 100.
<script setup>
import { NumberInput } from "@dicehub/kappa/components/number-input";
</script>
<template>
<NumberInput.Root default-value="8" disabled>
<NumberInput.Label>Disabled partitions</NumberInput.Label>
<NumberInput.Control><NumberInput.Input /></NumberInput.Control>
</NumberInput.Root>
<NumberInput.Root default-value="24" read-only>
<NumberInput.Label>Read-only partitions</NumberInput.Label>
<NumberInput.Control>
<NumberInput.Scrubber title="Read-only value" />
<NumberInput.Input />
</NumberInput.Control>
</NumberInput.Root>
<NumberInput.Root default-value="125" :max="100" invalid>
<NumberInput.Label>Invalid load balance (%)</NumberInput.Label>
<NumberInput.Control>
<NumberInput.Input aria-describedby="load-balance-error" />
</NumberInput.Control>
</NumberInput.Root>
<p id="load-balance-error">Enter a value from 0 to 100.</p>
</template>Sizes
Use xs for compact dicehub property fields: 20px high with 3px input padding and a 2px corner radius. The other sizes are 28px (sm), 32px (default), and 36px (lg) at a 16px root font size. All sizes also support NumberInput.ScrubbableInput.
<script setup>
import { NumberInput } from "@dicehub/kappa/components/number-input";
</script>
<template>
<NumberInput.Root v-for="size in ['xs', 'sm', 'default', 'lg']" :key="size" :size="size" default-value="32">
<NumberInput.Label>{{ size }}</NumberInput.Label>
<NumberInput.Control>
<NumberInput.DecrementTrigger :aria-label="'Decrease ' + size + ' value'" />
<NumberInput.Input />
<NumberInput.IncrementTrigger :aria-label="'Increase ' + size + ' value'" />
</NumberInput.Control>
</NumberInput.Root>
</template>Locale and Formatting
locale controls grouping and decimal separators. Intl options control display precision.
<script setup>
import { NumberInput } from "@dicehub/kappa/components/number-input";
</script>
<template>
<NumberInput.Root
default-value="1234,5"
locale="de-DE"
:step="0.5"
:format-options="{ minimumFractionDigits: 1, maximumFractionDigits: 2, useGrouping: true }"
>
<NumberInput.Label>Volumenstrom (m³/s)</NumberInput.Label>
<NumberInput.Control>
<NumberInput.Input />
<NumberInput.Unit aria-hidden="true">m³/s</NumberInput.Unit>
</NumberInput.Control>
</NumberInput.Root>
</template>Mouse Wheel
Wheel changes are opt-in. With allowMouseWheel, hover or focus the input, then scroll to apply one step per wheel event. The default remains off to prevent accidental changes during page scrolling. Kappa intentionally extends Ark UI's focus-only wheel behavior to the hovered input when this option is enabled.
<script setup>
import { NumberInput } from "@dicehub/kappa/components/number-input";
</script>
<template>
<NumberInput.Root default-value="50" :step="5" allow-mouse-wheel>
<NumberInput.Label>Mesh refinement (%)</NumberInput.Label>
<NumberInput.Control><NumberInput.Input /></NumberInput.Control>
</NumberInput.Root>
</template>Right-to-left
Direction controls layout, trigger order, and scrub direction. Numeric parsing stays with locale, so the two settings can be chosen independently.
<script setup>
import { NumberInput } from "@dicehub/kappa/components/number-input";
</script>
<template>
<div dir="rtl">
<NumberInput.Root dir="rtl" locale="ar-EG" default-value="24" :min="1" :max="128">
<NumberInput.Label>عدد عمليات الحل</NumberInput.Label>
<NumberInput.Control>
<NumberInput.DecrementTrigger aria-label="تقليل عدد العمليات" />
<NumberInput.Input />
<NumberInput.IncrementTrigger aria-label="زيادة عدد العمليات" />
</NumberInput.Control>
</NumberInput.Root>
<NumberInput.Root
dir="rtl"
locale="de-DE"
default-value="1234,5"
:step="0.5"
:format-options="{ minimumFractionDigits: 1, maximumFractionDigits: 1, useGrouping: true }"
>
<NumberInput.Label>RTL layout, German number format</NumberInput.Label>
<NumberInput.Control><NumberInput.Input /></NumberInput.Control>
</NumberInput.Root>
</div>
</template>Accessibility
- The input follows the WAI-ARIA spinbutton pattern and exposes its value and range.
- ArrowUp and ArrowDown use
step. Shift useslargeStep; Alt usessmallStep. - Home sets the minimum, End sets the maximum, and Enter commits the current value.
- Every visible field needs
Label. Connect help or error text witharia-describedby. - Give icon-only triggers concise names if custom trigger content changes the supplied translation.
- Whole-field scrubbing starts only after 10 pixels, so a click remains a reliable route to direct entry.
- Scrubbing needs a pointing device. Direct entry and spinbutton keyboard commands remain available.
- Disabled and read-only fields do not start a scrub. Read-only input can still receive focus for selection.
- Use
nameandformfor native form association. ReadvalueAsNumberfrom context when a numeric payload is required.
API Reference
NumberInput.Root
| Prop | Type | Default | Description |
|---|---|---|---|
modelValue / defaultValue | string | — | Controlled or initial string value. Strings preserve locale-specific text. |
asChild | boolean | false | Merges root behavior into the direct child element. |
id / ids | string / partial ID map | generated | Overrides the machine or part identifiers. |
dir | "ltr" | "rtl" | locale direction | Overrides layout and scrub direction without changing numeric formatting. |
min / max | number | safe integer limits | Allowed numeric range. |
step | number | 1 | Standard trigger, keyboard, wheel, and scrub increment. |
smallStep / largeStep | number | step / 10; step × 10 | Alt+Arrow and Shift+Arrow increments. |
size | "xs" | "sm" | "default" | "lg" | "default" | Control height and spacing: 20, 28, 32, or 36px at a 16px root font size. |
locale | string | "en-US" | BCP 47 locale for parsing and formatting. |
formatOptions | Intl.NumberFormatOptions | — | Intl formatting, precision, unit, and currency options. |
allowMouseWheel | boolean | false | Changes a hovered or focused input with the mouse wheel. |
allowOverflow | boolean | false | Lets an edited value temporarily pass the range when enabled. |
clampValueOnBlur | boolean | !allowOverflow | Clamps an out-of-range value when focus leaves. |
focusInputOnChange | boolean | true | Moves focus to the input after a trigger or scrub change. |
spinOnPress | boolean | true | Repeats changes while a trigger stays pressed. |
disabled / readOnly / invalid / required | boolean | false | Field state passed to all Ark parts. |
name / form | string | — | Native form integration. |
inputMode | "text" | "tel" | "numeric" | "decimal" | "decimal" | Virtual-keyboard hint. |
pattern | string | numeric pattern | Native input validation pattern. |
translations | IntlTranslations | Ark defaults | Accessible trigger and value announcements. |
NumberInput.ScrubbableInput
Replaces Input inside Control. It forwards native input attributes and listeners. Range, formatting, step, smallStep, and largeStep come from Root.
| Prop or slot | Type | Default | Description |
|---|---|---|---|
scrubSensitivity | { control?: number; shift?: number; alt?: number } | — | Opt-in pointer multipliers of Root.step. Priority: Control, Shift, Alt. Omitted keys use ×1. Without this object, the existing smallStep/largeStep behavior is unchanged. Keyboard steps are not affected. |
dragThreshold | number | 10 | Horizontal pixels required before a pointer gesture becomes a drag. |
editAlignment | "start" | "center" | "start" | Logical text alignment while the internal spinbutton is focused. |
native input attributes | InputHTMLAttributes | — | Attributes and listeners forwarded to the internal Ark spinbutton. |
default slot | { value, valueAsNumber, editing, dragging } | formatted value | Customizes only the blurred display content. |
Parts
| Part | Element | Description |
|---|---|---|
NumberInput.Label | label | Accessible label linked to the spinbutton. |
NumberInput.Control | div | Groups the input, unit, and optional triggers. |
NumberInput.Input | input | Locale-aware spinbutton and form control. |
NumberInput.ValueText | span | Formatted value for custom readouts. |
NumberInput.IncrementTrigger / DecrementTrigger | button | Step controls with press-and-hold behavior. |
NumberInput.Scrubber | div | Horizontal pointer-lock drag target. |
NumberInput.ScrubbableInput | div + input | Whole-field display, drag surface, and direct editor commonly used in 3D applications. |
NumberInput.Unit | span | Visual suffix; put its meaning in the label or Intl formatting. |
Events
| Event | Payload | Description |
|---|---|---|
update:modelValue | string | Updates v-model with the current string value. |
valueChange | NumberInputValueChangeDetails | Reports each value change and valueAsNumber. |
valueCommit | NumberInputValueCommitDetails | Reports a value committed by blur or Enter. |
valueInvalid | NumberInputValueInvalidDetails | Reports range overflow or underflow. |
focusChange | NumberInputFocusChangeDetails | Reports the focused state. |
Exports
| Export | Description |
|---|---|
NumberInput | Root component with all styled compound parts. |
NumberInputRoot / NumberInputRootProvider | Direct root and provider components. |
NumberInputLabel / NumberInputControl / NumberInputInput | Core field parts. |
NumberInputIncrementTrigger / NumberInputDecrementTrigger | Step trigger parts. |
NumberInputScrubber / NumberInputScrubbableInput | Separate-handle and whole-field scrub interactions. |
NumberInputValueText / NumberInputUnit | Formatted readout and visual suffix parts. |
NumberInputContext / useNumberInput | Renderless context and external machine composition APIs. |
NUMBER_INPUT_SIZES / NUMBER_INPUT_DEFAULT_SIZE | Readonly sizes and the public visual default. |
NUMBER_INPUT_EDIT_ALIGNMENTS / NUMBER_INPUT_DEFAULT_EDIT_ALIGNMENT | Readonly editor alignments and the public default. |
isNumberInputEditAlignment / resolveNumberInputEditAlignment | Runtime alignment guard and safe fallback resolver. |
NumberInput*Props / NumberInput*Slots / NumberInput*Emits | Public Vue contracts. |
NumberInputFocusChangeDetails / NumberInputValueChangeDetails / NumberInputValueCommitDetails / NumberInputValueInvalidDetails | Public event detail types. |