Skip to content

NumberStepper

A themed numeric input with +/- nudge buttons, replacing the native <input type="number"> spin buttons. No boxed/bordered control — minimal icon buttons only, consistent with the framework's other borderless inputs (e.g. MealReviewCard's name field, ClarificationPrompt's free-text field).

Playground

Click the chevrons — the value pulses on every nudge. With stepPercent on, the nudge amount is 10% of the value at the time you started clicking (frozen between clicks, so repeated clicks move a consistent distance instead of compounding) rather than a fixed absolute step.

vue
<script setup>
import { NumberStepper } from 'yemek'
</script>

<template>
  <NumberStepper
    v-model="confirmProtein"
    accent-color="var(--yemek-nutrient-protein)"
    :min="0"
    :max="300"
  />
</template>

Props

PropTypeDefaultDescription
modelValuenumber | string | nullCurrent value (v-model).
stepnumber1Fixed nudge amount, used when stepPercent is unset.
stepPercentnumberundefinedDynamic percent-based stepping — nudge amount is a fraction of a "base" value instead of a fixed step, frozen between nudges so N consecutive clicks move the same total distance rather than compounding, and re-armed whenever the value changes from outside the stepper.
minnumberundefinedLower bound — clamped on blur and nudge, not on every keystroke (so typing a value below min digit-by-digit still works).
maxnumberundefinedUpper bound, same clamping behavior as min.
accentColorstringundefinedCSS color for the value text (e.g. var(--yemek-nutrient-protein)) — lets each field pick whichever identity fits instead of the kit guessing one.
placeholderstringundefinedPlaceholder shown when the value is empty.

Events

EventPayloadFired when
update:modelValuenumber | ''Standard v-model — on typing, blur-clamp, or a nudge.

Behavior notes

  • Bounds-checking (min/max) is pure numeric validation, not domain logic, so it stays in the component — same precedent as MetricRing's ratio math. Portion/gram scaling, macro clamping to a plausible meal size, etc. all stay the consumer's own domain logic; this only clamps to whatever min/max it's given.
  • Typing is never clamped mid-keystroke — only on blur or a nudge-button click. Clamping every keystroke would make a value below min unreachable by typing (e.g. typing "1" of "1500" into a field with min=1000 would instantly snap to 1000).
  • Each nudge pulses the value with a quick scale animation — feedback that the click registered, not a silent number swap.
  • prefers-reduced-motion: reduce disables the pulse and the button's tap-scale feedback.