NumberField

Components

NumberField New · experimental

shadcn's NumberField with the Input's goo focus ring. Pressing + or - squishes a goo dollop under the button, and every step nudges toward the side you pressed.

Press + or - (hold to repeat), or focus the field and use the arrow keys.

Customize

Preset
Speed 1.00×
Colour
Squish 1.00×
Nudge 1.00×

GoopNumberField is shadcn-vue's NumberField on reka-ui's NumberFieldRoot: the same parts (GoopNumberFieldContent, GoopNumberFieldInput, GoopNumberFieldIncrement, GoopNumberFieldDecrement), the same classes, v-model, and class merge. min, max, step, formatOptions, locale, disabled, id, name, and the other reka props go straight to reka. The goo is drawn on aria-hidden layers inside GoopNumberFieldContent, behind the real input and buttons.

Installation

Install @goopui/nuxt or @goopui/vue as shown on the Button page. The Nuxt module auto-imports every part. They use the same shadcn tokens as the plain NumberField (--input, --ring, --accent, --destructive).

Usage

<script setup lang="ts">
// Nuxt auto-imports these. In plain Vue, import them:
import {
  GoopNumberField,
  GoopNumberFieldContent,
  GoopNumberFieldDecrement,
  GoopNumberFieldIncrement,
  GoopNumberFieldInput,
} from '@goopui/vue'

const qty = ref(3)
</script>

<template>
  <label for="qty" class="text-sm font-medium">Quantity</label>
  <GoopNumberField id="qty" v-model="qty" :min="0" :max="20">
    <GoopNumberFieldContent>
      <GoopNumberFieldDecrement />
      <GoopNumberFieldInput />
      <GoopNumberFieldIncrement />
    </GoopNumberFieldContent>
  </GoopNumberField>
</template>

What it does

Each part is an effect you can turn off:

  • squish. Pressing + or - swells a soft goo dollop out of the button, squished low and wide. Letting go springs it back with a small wobble as it shrinks away.
  • nudge. Every step (a press, a held repeat, or an arrow key) shoves the dollop toward the side you pressed and sends a ripple along the ring from that side. A typed value that commits on blur doesn't nudge.
  • The focus ring. The same ring as Input, with the same flow, close, ripple, wobble, drain, drip, and drips effects. A press on a button focuses the field, so the ring flows in from that button's side.

Formatting

formatOptions and locale work as in reka, so currency, percent, and unit fields need no extra code.

<GoopNumberField v-model="price" :step="0.5" :format-options="{ style: 'currency', currency: 'USD' }">
  <GoopNumberFieldContent>
    <GoopNumberFieldDecrement />
    <GoopNumberFieldInput />
    <GoopNumberFieldIncrement />
  </GoopNumberFieldContent>
</GoopNumberField>

Effects and presets

Every effect is a prop on GoopNumberField. false turns it off, true uses the preset's strength, and a number scales it.

<GoopNumberField preset="wild">...</GoopNumberField>
<GoopNumberField :squish="false" :nudge="false">...</GoopNumberField>

Disabled and goo off

A disabled field is shadcn's: dimmed, and its buttons don't press, so nothing squishes. :goo="false" renders a plain shadcn NumberField.

Colors

VariableDefaultWhat it colors
--goop-number-field-pressvar(--accent)The dollop under a pressed button.
--goop-number-field-press-opacity1Its opacity.
--goop-input-colorvar(--ring)The focus ring.
--goop-input-opacity0.5The focus ring's opacity.
--goop-input-invalid-colorvar(--destructive)The ring and drip of an invalid field.

Props

GoopNumberField props
PropTypeDefaultNotes
modelValuenumber | nullThe value (v-model).
defaultValuenumberThe initial value when uncontrolled.
classstring | array | objectMerged onto the root, like shadcn.
goobooleantrueMaster switch. false renders a plain shadcn NumberField.
squishboolean | numbertruePressing + or - swells a goo dollop under it, squished low and wide; letting go springs it back with a small wobble.
nudgeboolean | numbertrueEach step shoves the dollop toward the side you pressed and sends a ripple along the ring from that side.
flowboolean | numbertrueOn focus the ring flows around the border from the click point (or the button you pressed).
closeboolean | numbertrueThe wobble where the two flow fronts meet and close the ring.
rippleboolean | numbertrueTyping sends a ripple along the ring from the caret.
wobbleboolean | numbertrueThe closed ring wobbles gently while focused.
drainboolean | numbertrueOn blur the ring drains away, top first.
dripboolean | numbertrueWith aria-invalid on the input, a small drip hangs off the destructive ring.
dripsboolean | numberfalseResting drips under the ring while it shows.
speednumber1Animation speed multiplier.
preset'subtle' | 'playful' | 'wild'app config, then playfulThe feel: ring and dollop size, squish depth, nudge distance.
renderer'svg' | 'canvas''svg'How the goo is drawn.

GoopNumberFieldContent exposes handle (the live goo handle, null before mount or with goo off) and el. Before it mounts, during SSR, and with :goo="false", the field looks exactly like shadcn's, so first paint and hydration match.

Accessibility

It's reka's NumberField: the input is a role="spinbutton" with aria-valuenow, aria-valuemin, and aria-valuemax, the buttons are labelled "Increase" and "Decrease", and the arrow, Page Up/Down, Home, and End keys step the value. The goo layers are aria-hidden and can't be clicked. With the goo attached, shadcn's focus ring steps aside for the goo ring, which shows on every focus, keyboard included.

Under reduced motion the ring is a static closed band, a pressed button shows a still, round dollop, and steps don't nudge. See Accessibility & reduced motion.