Components
Select New · experimental
shadcn's Select with goo. The list blooms out of the value you picked, the highlight is a blob, and the check mark plops in.
Customize
GoopSelect wraps the same reka-ui Select primitive as shadcn-vue's Select, with the same API. Keyboard navigation, typeahead, focus handling, form submission, and positioning work the same. Swap the imports and you're done.
Installation
Nuxt
pnpm add @goopui/nuxt
export default defineNuxtConfig({
modules: ['@goopui/nuxt'],
})
The module auto-imports every GoopSelect* part, adds goop.css, and points Tailwind at the component classes. It needs Tailwind CSS v4 and the shadcn tokens (--popover, --popover-foreground, --accent, --input, --ring, --radius).
Vue
pnpm add @goopui/vue
@import "tailwindcss";
@import "@goopui/vue/goop.css";
Usage
<script setup lang="ts">
// Nuxt auto-imports these. In plain Vue, import them:
import { ref } from 'vue'
import {
GoopSelect,
GoopSelectContent,
GoopSelectGroup,
GoopSelectItem,
GoopSelectLabel,
GoopSelectTrigger,
GoopSelectValue,
} from '@goopui/vue'
const fruit = ref('Blueberry')
</script>
<template>
<GoopSelect v-model="fruit">
<GoopSelectTrigger class="w-44">
<GoopSelectValue placeholder="Pick a fruit" />
</GoopSelectTrigger>
<GoopSelectContent>
<GoopSelectGroup>
<GoopSelectLabel>Fruits</GoopSelectLabel>
<GoopSelectItem value="Apple">Apple</GoopSelectItem>
<GoopSelectItem value="Banana">Banana</GoopSelectItem>
<GoopSelectItem value="Blueberry">Blueberry</GoopSelectItem>
</GoopSelectGroup>
</GoopSelectContent>
</GoopSelect>
</template>
The parts are GoopSelect, Trigger, Value, Content, Group, Label, Item, ItemText, Separator, ScrollUpButton, and ScrollDownButton, each prefixed with GoopSelect. All the goo lives on GoopSelectContent.
Behavior
- Bloom. The list pools from a single droplet (or a few that fuse,
bloomStart="merge"), spreads out with a lobed edge, overshoots, and wobbles to rest. Then it splashes drops all around, kept off the trigger (openSlosh,openDrips). - Highlight. The highlighted item's background is a goo blob. It slides between items with a stretch and a neck, overshoots when it lands, and follows the keyboard as well as the pointer (
highlight,momentum,wobble). - Follow the pointer. As the pointer moves along an item, the blob's swell leans toward it with a little lag (
followPointer). - Choose. Picking an item squishes the blob (
pop), sets the value at once, and closes the list once the pop has played. The new item's check mark plops in: it falls in small, squashes flat as it lands, and jiggles back to round (plop). - Close. The list pulls in, wobbles, and shrinks back to where it bloomed from (
closeSlosh). - Content stays sharp. The list's background, ring, and shadow are drawn on an
aria-hiddensurface behind the items, so the text is never filtered.
Position and bloom origin
position is reka-ui's. With item-aligned (the default, like shadcn), the list is laid over the trigger with the selected item on top of it, and it blooms out of that item, so the list grows from the value you clicked. With popper, the list sits below the trigger like a menu and blooms from its own center:
<GoopSelectContent>...</GoopSelectContent> <!-- item-aligned: blooms from the selected item -->
<GoopSelectContent position="popper">...</GoopSelectContent> <!-- blooms from its center -->
bloomFrom overrides where the bloom starts: 'auto' (the default, as above), 'selected', or 'center'. Close shrinks back to the same spot.
<GoopSelectContent bloom-from="center">...</GoopSelectContent>
Surface color
A select trigger is an outlined field, so its color isn't the list's. Unlike Popover and Dropdown Menu, the list defaults to surface="popover": shadcn's popover tokens. 'trigger' takes the trigger's background and text color, and any CSS color works too:
<GoopSelectContent>...</GoopSelectContent> <!-- 'popover' (default) -->
<GoopSelectContent surface="trigger">...</GoopSelectContent>
<GoopSelectContent surface="oklch(0.62 0.19 150)">...</GoopSelectContent>
Turning parts off
Every effect is its own boolean, and they all default to on:
<GoopSelectContent :plop="false">...</GoopSelectContent> <!-- the check mark just appears -->
<GoopSelectContent :follow-pointer="false">...</GoopSelectContent> <!-- the blob stays centred -->
<GoopSelectContent :highlight="false">...</GoopSelectContent> <!-- shadcn's flat highlight -->
<GoopSelectContent :open-drips="false">...</GoopSelectContent> <!-- no splash -->
Plain shadcn
:goo="false" renders exactly shadcn's SelectContent, with its own enter and exit animation:
<GoopSelectContent :goo="false">...</GoopSelectContent>
Driving it from code
GoopSelectContent exposes its engine handle as handle (null before it mounts, or when goo is off). The list only exists while open.
Props
The root, trigger, value, group, label, item, separator, and scroll buttons take shadcn-vue's props. GoopSelectTrigger also takes size: 'default' | 'sm', like shadcn. GoopSelectContent takes reka-ui's SelectContent props (position, side, align, sideOffset, and the rest), class, and these goo props. Defaults are position="item-aligned" and align="center".
| Prop | Type | Default | Notes |
|---|---|---|---|
goo | boolean | true | Master switch. false renders a plain shadcn SelectContent. |
flow | boolean | true | The list opens in flowStyle. false: it inflates from a bulb at the trigger edge. |
flowStyle | 'bloom' | 'neck' | 'bloom' | 'bloom' pools from a point and spreads in every direction with a lobed edge, overshoots, then splashes. 'neck' runs out of the trigger through a thick neck. |
bloomStart | 'single' | 'merge' | 'single' | 'single': one droplet swells. 'merge': a few drops fuse. |
bloomFrom | 'auto' | 'selected' | 'center' | 'auto' | Where the bloom starts. 'auto': the selected item for an item-aligned list, the list's center for a popper. Close shrinks back to the same spot. |
bridge | boolean | true | The bulb and lumpy neck between the trigger 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, the edges (bloom: every edge) overshoot, rebound, and wobble. |
openDrips | boolean | true | On open, the edges splash: globs, drops, and specks fling off and fall away, kept off the trigger. |
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 (off-centre, one or two lumps, staggered), 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 slides between items and follows the keyboard. 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 (its neck while it slides, a soft bulge as the pointer moves along an item) 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. 'trigger' takes the trigger's background and text color. 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
Keyboard navigation, typeahead, focus handling, form submission, and ARIA attributes come from reka-ui. The goo surface and the highlight blob are aria-hidden and can't be clicked, and the items are never filtered. Under reduced motion the list opens and closes plainly: it keeps its goo shape and color, but the bloom, slosh, and splash are off, the highlight snaps between items, and there's no pop and no plop. See Accessibility & reduced motion.