Components
Accordion New · experimental
shadcn's Accordion and Collapsible with goo. The open row floods with goo from where you clicked, and the content springs open.
Customize
GoopAccordion wraps the same reka-ui Accordion primitive as shadcn-vue's Accordion, with the same API. Keyboard navigation, type="single" / "multiple", collapsible, and ARIA work the same. Swap the imports and you're done. GoopCollapsible is on this page too: it's the same goo on a single trigger.
Installation
Nuxt
pnpm add @goopui/nuxt
export default defineNuxtConfig({
modules: ['@goopui/nuxt'],
})
The module auto-imports every GoopAccordion* and GoopCollapsible* part, adds goop.css, and points Tailwind at the component classes. It needs Tailwind CSS v4 and the shadcn tokens (--primary, --primary-foreground, --muted-foreground, --ring).
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 {
GoopAccordion,
GoopAccordionContent,
GoopAccordionItem,
GoopAccordionTrigger,
} from '@goopui/vue'
</script>
<template>
<GoopAccordion type="single" collapsible default-value="what" class="w-full">
<GoopAccordionItem value="what">
<GoopAccordionTrigger>What is goopUI?</GoopAccordionTrigger>
<GoopAccordionContent>shadcn-vue components with gooey, dripping motion.</GoopAccordionContent>
</GoopAccordionItem>
<GoopAccordionItem value="motion">
<GoopAccordionTrigger>Does it respect reduced motion?</GoopAccordionTrigger>
<GoopAccordionContent>Yes.</GoopAccordionContent>
</GoopAccordionItem>
</GoopAccordion>
</template>
The parts are GoopAccordion, GoopAccordionItem, GoopAccordionTrigger, and GoopAccordionContent. The goo props go on GoopAccordion, and every item follows them.
Behavior
- Flood. Opening a row floods its trigger with goo from the click point (its center when you open it from the keyboard). The front crests against the walls, the row wobbles once it's full, and a few droplets splash off its top edge (
flood,wobble,droplets). It's the same fill as the Toggle's. - Jelly content. The content opens on a spring at the same time: it overshoots, then settles (
jelly). It closes on a short spring with no overshoot. - Drain. Closing a row drains the goo back into the click point. In a single accordion, the old row drains while the new one floods.
- At rest nothing animates. The open row just stays filled.
- Content stays sharp. The goo is one
aria-hiddenlayer inside the trigger. Once the row is half full, the trigger's text and chevron switch to the goo's foreground color. - Layout is shadcn's exactly. Closed rows look exactly like shadcn's.
Tuning
Every effect takes true, false, or a strength from 0 to 2:
<GoopAccordion type="multiple" preset="wild" :droplets="2">...</GoopAccordion>
<GoopAccordion type="single" :flood="false" :jelly="0.5">...</GoopAccordion>
The goo paints with --goop-accordion-fill (default --primary), and the flooded trigger's text uses --goop-accordion-text (default --primary-foreground). Set them on the accordion, or any parent, to recolor it.
Collapsible
GoopCollapsible wraps reka-ui's Collapsible, like shadcn-vue's. It takes the same goo props as GoopAccordion: its trigger floods while it's open, and the content springs open.
<script setup lang="ts">
import { ref } from 'vue'
import { GoopButton, GoopCollapsible, GoopCollapsibleContent, GoopCollapsibleTrigger } from '@goopui/vue'
const open = ref(false)
</script>
<template>
<GoopCollapsible v-model:open="open" class="flex flex-col gap-2">
<div class="flex items-center justify-between">
<span>3 starred repositories</span>
<GoopCollapsibleTrigger as-child>
<GoopButton variant="outline" size="sm">{{ open ? 'Hide' : 'Show' }}</GoopButton>
</GoopCollapsibleTrigger>
</div>
<div>goopui/core</div>
<GoopCollapsibleContent class="flex flex-col gap-2">
<div>goopui/vue</div>
<div>goopui/nuxt</div>
</GoopCollapsibleContent>
</GoopCollapsible>
</template>
The parts are GoopCollapsible, GoopCollapsibleTrigger, and GoopCollapsibleContent. Like shadcn, the trigger and content are unstyled.
Plain shadcn
:goo="false" renders exactly shadcn's Accordion (or Collapsible), with its own height animation:
<GoopAccordion type="single" :goo="false">...</GoopAccordion>
<GoopCollapsible :goo="false">...</GoopCollapsible>
Driving it from code
GoopAccordionItem and GoopCollapsible expose their engine handle as handle (null before they mount, or when goo is off).
Props
GoopAccordion takes reka-ui's AccordionRoot props (type, collapsible, defaultValue, modelValue, disabled, orientation, and the rest). GoopCollapsible takes reka-ui's CollapsibleRoot props (open, defaultOpen, disabled, unmountOnHide) and class. The item, trigger, and content parts take shadcn-vue's props and class. Both roots take these goo props:
| Prop | Type | Default | Notes |
|---|---|---|---|
goo | boolean | true | Master switch. false renders a plain shadcn Accordion / Collapsible. |
flood | boolean | number | true | The trigger row floods with goo from the click point on open and drains back into it on close. Off, the fill snaps. |
wobble | boolean | number | true | The crest of the flood front, and a jelly wobble of the row once it is full. |
droplets | boolean | number | true | A few droplets splash off the row's top edge as it floods. |
jelly | boolean | number | true | The content opens on a jelly spring: overshoot, then settle. |
textFollow | boolean | true | The trigger's text color follows the goo's edge. false flips it all at once when the goo crosses the label. |
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, focus handling, and ARIA attributes (aria-expanded, aria-controls, the region roles) come from reka-ui. The goo layer is aria-hidden and can't be clicked, and the text is never filtered. Under reduced motion, the open row is a still fill with no flood, wobble, or droplets, and the content opens with a plain height animation. See Accessibility & reduced motion.