Components
Combobox New · experimental
shadcn's Combobox with goo. The list blooms open below the input, the highlight is a blob, the check mark plops in, and the input gets a goo focus ring.
Customize
GoopCombobox wraps the same reka-ui Combobox primitive as shadcn-vue's Combobox, with the same API: filtering, keyboard navigation, by, multiple, and form submission work the same. Swap the imports and you're done. Install it like Command.
Usage
<script setup lang="ts">
const frameworks = [
{ value: 'nuxt', label: 'Nuxt' },
{ value: 'sveltekit', label: 'SvelteKit' },
{ value: 'astro', label: 'Astro' },
]
const framework = ref()
</script>
<template>
<GoopCombobox v-model="framework" by="value">
<GoopComboboxAnchor class="relative w-56">
<GoopComboboxInput placeholder="Select framework..." :display-value="(v) => v?.label ?? ''" />
<GoopComboboxTrigger class="absolute inset-y-0 right-0 px-2" aria-label="Show frameworks">▾</GoopComboboxTrigger>
</GoopComboboxAnchor>
<GoopComboboxList class="w-(--reka-combobox-trigger-width)">
<GoopComboboxEmpty>No framework found.</GoopComboboxEmpty>
<GoopComboboxGroup>
<GoopComboboxItem v-for="f in frameworks" :key="f.value" :value="f">
{{ f.label }}
<GoopComboboxItemIndicator />
</GoopComboboxItem>
</GoopComboboxGroup>
</GoopComboboxList>
</GoopCombobox>
</template>
The parts are GoopCombobox, Anchor, Input, Trigger, Cancel, List, Empty, Group, Item, ItemIndicator, and Separator, each prefixed with GoopCombobox. The list's goo lives on GoopComboboxList; the input's ring on GoopComboboxInput.
Behavior
- Bloom. The list pools from its own center, spreads with a lobed edge, overshoots, and splashes all around, kept off the input. Close pulls it in and shrinks it back (
openSlosh,openDrips,closeSlosh). - Highlight. The active item's background is a goo blob that glides between items with the keys and the pointer (
highlight,momentum,wobble,followPointer). When typing hides the highlighted item, the blob shrinks out. - Choose. Picking an item squishes the blob (
pop), sets the value at once, and closes the list once the pop has played. Its check mark plops in (plop). - Input ring.
GoopComboboxInputgets GoopInput's goo focus ring (:goo="false"on it turns the ring off).
Turning parts off
<GoopComboboxList :plop="false">...</GoopComboboxList> <!-- the check mark just appears -->
<GoopComboboxList :highlight="false">...</GoopComboboxList> <!-- shadcn's flat highlight -->
<GoopComboboxList :open-drips="false">...</GoopComboboxList> <!-- no splash -->
<GoopComboboxInput :goo="false" /> <!-- no ring -->
<GoopComboboxList :goo="false">...</GoopComboboxList> <!-- plain shadcn list -->
Props
GoopComboboxList takes reka-ui's ComboboxContent props (defaults position="popper", align="start", sideOffset 4), class, and the same goo props as GoopSelectContent except bloomFrom: a combobox list always blooms from its own center. GoopComboboxInput takes reka-ui's ComboboxInput props, GoopInput's ring props, and goo. The other parts take shadcn-vue's props.
| Prop | Type | Default | Notes |
|---|---|---|---|
goo | boolean | true | Master switch. false renders a plain shadcn ComboboxList. |
flow | boolean | true | The list opens in flowStyle. false: it inflates from a bulb at the input edge. |
flowStyle | 'bloom' | 'neck' | 'bloom' | 'bloom' pools from the list's own center, spreads with a lobed edge, overshoots, then splashes. 'neck' runs out of the input through a thick neck. |
bloomStart | 'single' | 'merge' | 'single' | 'single': one droplet swells. 'merge': a few drops fuse. |
bridge | boolean | true | The bulb and lumpy neck between the input and the list. Not used by bloom. |
wobble | boolean | true | Jelly overshoot: the open overshoot and settle, the highlight overshoot, and the plop wobble. |
momentum | boolean | true | The panel's launch (not used by bloom), and the highlight's stretch and neck while it travels. |
fade | boolean | true | The items fade in once the goo settles. |
openSlosh | boolean | true | On open, every edge overshoots, rebounds, and wobbles. |
openDrips | boolean | true | On open, the edges splash: globs, drops, and specks fling off and fall away, kept off the input. |
openDripAmount | number | 1 | Splash drop count, 0 to 3. |
openDripSize | number | 1 | Splash drop size, 0.3 to 2.5. |
openDripFling | number | 1 | How far the drops are flung, 0 to 3. |
openDripVariety | number | 0.7 | Mix of globs and specks, 0 to 1. 0 makes every drop alike. |
sloshVariety | number | 1 | How differently each side bulges on open and close, 0 to 1. 0 is an even, centred bulge. |
closeSlosh | boolean | true | On close, the list pulls in and wobbles before it shrinks back. |
highlight | boolean | true | The item highlight is a goo blob that glides between items with the keys and the pointer, and shrinks out when typing hides its item. false gives shadcn's flat background. |
pop | boolean | true | The highlight squishes when an item is chosen, before the list closes. |
followPointer | boolean | true | The highlight's swell follows the pointer with a little lag. Off, or from the keyboard, it stays centred. |
plop | boolean | true | The chosen item's check mark plops in: falls in small, squashes as it lands, and jiggles back to round. |
surface | 'popover' | 'trigger' | string | 'popover' | The list's color. 'popover' uses shadcn's popover tokens. Any CSS color works. |
speed | number | 1 | Animation speed multiplier. |
preset | 'subtle' | 'playful' | 'wild' | app config, then playful | The overall feel. |
renderer | 'svg' | 'canvas' | 'svg' | How the goo is drawn. |
Accessibility
The combobox and listbox roles, aria-activedescendant, keyboard navigation, and focus handling come from reka-ui. The goo surface, highlight blob, and ring are aria-hidden and can't be clicked. Under reduced motion the list opens and closes plainly, the highlight snaps, there's no pop or plop, and the ring is static. See Accessibility & reduced motion.