Skip to content
Kappa
Number Input
@dicehub/kappav0.4.2

Number Input

Edits numeric values with direct entry, step controls, keyboard commands, and horizontal scrubbing.

Transient solver · Time controlsMaximum Courant number
Drag the value, or click it to type.
<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

Typical part hierarchy
NumberInput.Root
├── NumberInput.Label
├── NumberInput.Control
│   ├── NumberInput.DecrementTrigger
│   ├── NumberInput.Input
│   ├── NumberInput.Unit
│   └── NumberInput.IncrementTrigger
├── NumberInput.ScrubbableInput (alternative)
└── NumberInput.ValueText

Ark 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.

  • Root owns the string value, range, steps, locale, and state.
  • Label names the input. Include the physical unit in this text.
  • Control contains the input, optional unit, and optional triggers.
  • ScrubbableInput combines a quiet value display, whole-field drag, and direct editing.
  • Scrubber is 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.

Live: 0.005 · Committed: 0.005
<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.

Hover the input, then use the mouse wheel.
<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 uses largeStep; Alt uses smallStep.
  • Home sets the minimum, End sets the maximum, and Enter commits the current value.
  • Every visible field needs Label. Connect help or error text with aria-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 name and form for native form association. Read valueAsNumber from context when a numeric payload is required.

API Reference

NumberInput.Root

PropTypeDefaultDescription
modelValue / defaultValuestring—Controlled or initial string value. Strings preserve locale-specific text.
asChildbooleanfalseMerges root behavior into the direct child element.
id / idsstring / partial ID mapgeneratedOverrides the machine or part identifiers.
dir"ltr" | "rtl"locale directionOverrides layout and scrub direction without changing numeric formatting.
min / maxnumbersafe integer limitsAllowed numeric range.
stepnumber1Standard trigger, keyboard, wheel, and scrub increment.
smallStep / largeStepnumberstep / 10; step × 10Alt+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.
localestring"en-US"BCP 47 locale for parsing and formatting.
formatOptionsIntl.NumberFormatOptions—Intl formatting, precision, unit, and currency options.
allowMouseWheelbooleanfalseChanges a hovered or focused input with the mouse wheel.
allowOverflowbooleanfalseLets an edited value temporarily pass the range when enabled.
clampValueOnBlurboolean!allowOverflowClamps an out-of-range value when focus leaves.
focusInputOnChangebooleantrueMoves focus to the input after a trigger or scrub change.
spinOnPressbooleantrueRepeats changes while a trigger stays pressed.
disabled / readOnly / invalid / requiredbooleanfalseField state passed to all Ark parts.
name / formstring—Native form integration.
inputMode"text" | "tel" | "numeric" | "decimal""decimal"Virtual-keyboard hint.
patternstringnumeric patternNative input validation pattern.
translationsIntlTranslationsArk defaultsAccessible 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 slotTypeDefaultDescription
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.
dragThresholdnumber10Horizontal pixels required before a pointer gesture becomes a drag.
editAlignment"start" | "center""start"Logical text alignment while the internal spinbutton is focused.
native input attributesInputHTMLAttributes—Attributes and listeners forwarded to the internal Ark spinbutton.
default slot{ value, valueAsNumber, editing, dragging }formatted valueCustomizes only the blurred display content.

Parts

PartElementDescription
NumberInput.LabellabelAccessible label linked to the spinbutton.
NumberInput.ControldivGroups the input, unit, and optional triggers.
NumberInput.InputinputLocale-aware spinbutton and form control.
NumberInput.ValueTextspanFormatted value for custom readouts.
NumberInput.IncrementTrigger / DecrementTriggerbuttonStep controls with press-and-hold behavior.
NumberInput.ScrubberdivHorizontal pointer-lock drag target.
NumberInput.ScrubbableInputdiv + inputWhole-field display, drag surface, and direct editor commonly used in 3D applications.
NumberInput.UnitspanVisual suffix; put its meaning in the label or Intl formatting.

Events

EventPayloadDescription
update:modelValuestringUpdates v-model with the current string value.
valueChangeNumberInputValueChangeDetailsReports each value change and valueAsNumber.
valueCommitNumberInputValueCommitDetailsReports a value committed by blur or Enter.
valueInvalidNumberInputValueInvalidDetailsReports range overflow or underflow.
focusChangeNumberInputFocusChangeDetailsReports the focused state.

Exports

ExportDescription
NumberInputRoot component with all styled compound parts.
NumberInputRoot / NumberInputRootProviderDirect root and provider components.
NumberInputLabel / NumberInputControl / NumberInputInputCore field parts.
NumberInputIncrementTrigger / NumberInputDecrementTriggerStep trigger parts.
NumberInputScrubber / NumberInputScrubbableInputSeparate-handle and whole-field scrub interactions.
NumberInputValueText / NumberInputUnitFormatted readout and visual suffix parts.
NumberInputContext / useNumberInputRenderless context and external machine composition APIs.
NUMBER_INPUT_SIZES / NUMBER_INPUT_DEFAULT_SIZEReadonly sizes and the public visual default.
NUMBER_INPUT_EDIT_ALIGNMENTS / NUMBER_INPUT_DEFAULT_EDIT_ALIGNMENTReadonly editor alignments and the public default.
isNumberInputEditAlignment / resolveNumberInputEditAlignmentRuntime alignment guard and safe fallback resolver.
NumberInput*Props / NumberInput*Slots / NumberInput*EmitsPublic Vue contracts.
NumberInputFocusChangeDetails / NumberInputValueChangeDetails / NumberInputValueCommitDetails / NumberInputValueInvalidDetailsPublic event detail types.