Accordion

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.

shadcn-vue components with gooey, dripping motion. Same API, more slime.

Open another row. The old one drains while the new one floods.

Customize

Preset
Speed 1.00×
Flood 1.00×
Droplets 1.00×

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
nuxt.config.ts
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
main.css
@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-hidden layer 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.

3 starred repositories
goopui/core
The trigger floods while it's 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:

GoopAccordion and GoopCollapsible goo props
PropTypeDefaultNotes
goobooleantrueMaster switch. false renders a plain shadcn Accordion / Collapsible.
floodboolean | numbertrueThe trigger row floods with goo from the click point on open and drains back into it on close. Off, the fill snaps.
wobbleboolean | numbertrueThe crest of the flood front, and a jelly wobble of the row once it is full.
dropletsboolean | numbertrueA few droplets splash off the row's top edge as it floods.
jellyboolean | numbertrueThe content opens on a jelly spring: overshoot, then settle.
textFollowbooleantrueThe trigger's text color follows the goo's edge. false flips it all at once when the goo crosses the label.
speednumber1Animation speed multiplier.
preset'subtle' | 'playful' | 'wild'app config, then playfulThe 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.