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.
Customize
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, anddripseffects. 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
| Variable | Default | What it colors |
|---|---|---|
--goop-number-field-press | var(--accent) | The dollop under a pressed button. |
--goop-number-field-press-opacity | 1 | Its opacity. |
--goop-input-color | var(--ring) | The focus ring. |
--goop-input-opacity | 0.5 | The focus ring's opacity. |
--goop-input-invalid-color | var(--destructive) | The ring and drip of an invalid field. |
Props
| Prop | Type | Default | Notes |
|---|---|---|---|
modelValue | number | null | The value (v-model). | |
defaultValue | number | The initial value when uncontrolled. | |
class | string | array | object | Merged onto the root, like shadcn. | |
goo | boolean | true | Master switch. false renders a plain shadcn NumberField. |
squish | boolean | number | true | Pressing + or - swells a goo dollop under it, squished low and wide; letting go springs it back with a small wobble. |
nudge | boolean | number | true | Each step shoves the dollop toward the side you pressed and sends a ripple along the ring from that side. |
flow | boolean | number | true | On focus the ring flows around the border from the click point (or the button you pressed). |
close | boolean | number | true | The wobble where the two flow fronts meet and close the ring. |
ripple | boolean | number | true | Typing sends a ripple along the ring from the caret. |
wobble | boolean | number | true | The closed ring wobbles gently while focused. |
drain | boolean | number | true | On blur the ring drains away, top first. |
drip | boolean | number | true | With aria-invalid on the input, a small drip hangs off the destructive ring. |
drips | boolean | number | false | Resting drips under the ring while it shows. |
speed | number | 1 | Animation speed multiplier. |
preset | 'subtle' | 'playful' | 'wild' | app config, then playful | The 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.