Expandable Cards
Expandable Cards renders a vertical list of collapsible items. Only one card is open at a time by default; enable multiple to allow several open simultaneously. Each header animates its chevron on toggle, and the body expands with height/opacity via AnimatePresence and motion-v.
Usage
Provide an array of items with title and content. You can also pass extra fields on each item and access them via the item slot. For multi-select, set multiple to true.
What is Nuxt?
What is Nuxt Motion Block?
Is it free to use?
<template>
<div class="w-full max-w-md">
<MExpandableCards
:items="items"
/>
</div>
</template>
<script setup lang="ts">
const items = [
{
title: 'What is Nuxt?',
content: 'Nuxt is an open source framework that makes web development intuitive and powerful. Build performant and production-grade full-stack web apps and websites with confidence.',
},
{
title: 'What is Nuxt Motion Block?',
content: 'A collection of ready-to-use animated components built with motion-v and Nuxt. Copy, paste, and customize to add motion to your Nuxt apps.',
},
{
title: 'Is it free to use?',
content: 'Yes, Nuxt Motion Block is open source and free to use in your personal and commercial projects.',
},
]
</script>
Customize
What is Nuxt?
What is Nuxt Motion Block?
Is it free to use?
Customize
Props
| Prop | Type | Default | Description |
|---|---|---|---|
itemsrequired | AccordionItem[] | - | Array of items to render. Each item must include title (string) and content (string). Additional arbitrary keys are allowed and exposed via the item slot. |
multiple | boolean | false | When true, opening one item does not close others; when false, only one item remains open at a time. |
class | string | - | Merged onto each UCard (:class="props.class"). |
ui | CardProps['ui'] | {} | UI configuration passed to each UCard. |
Events
No events are emitted by this component.
Slots
| Slot | Props | Description |
|---|---|---|
item | ({ item, index }: { item: AccordionItem, index: number }) => VNode | Per-item slot rendered inside the expanded body (after the content text). Useful for adding actions, links, or richer markup per item. |
UI
This component's theme exposes the slots below. Override any of them per instance with the ui prop, or app-wide under ui.expandableCards in app.config.ts.
| Prop | Type | Default | Description |
|---|---|---|---|
ui | object | - | Per-slot classes merged over the theme defaults. |
| Slot | Props | Description |
|---|---|---|
root | - | space-y-3 |
cardRoot | - | |
cardBody | - | |
header | - | flex items-start justify-between cursor-pointer |
headerContent | - | flex-1 min-w-0 |
title | - | text-lg font-semibold text-highlighted |
chevron | - | ml-2 flex-shrink-0 text-muted |
chevronIcon | - | w-5 h-5 |
collapsible | - | overflow-hidden |
content | - | pt-4 |
contentText | - | text-muted |
Accessibility
The header is a plain clickable div with cursor-pointer (no button role, aria-expanded, or aria-controls). The chevron uses an inline SVG with no accessible name. The expanded region uses AnimatePresence but has no ARIA attributes linking it to the trigger. Keyboard navigation (Enter/Space) is not handled. No prefers-reduced-motion awareness is present. Consumers should add their own accessible triggers if keyboard/screen-reader support is required.
Performance
- Each item enters with a spring (
stiffness: 300, damping: 30,y: 20, opacity: 0,delay: index*0.05). - Chevron rotation uses a spring (
stiffness: 300, damping: 25,rotate: isOpen ? 180 : 0). - Expanded body animates height/opacity with
AnimatePresenceand spring (stiffness: 300, damping: 30) usingheight: 'auto'inside the motion wrapper. - State is tracked in a
Set<number>for O(1) membership checks; toggling clears the set when notmultiple. - No timers, heavy computations, or WebGL. Rendering scales linearly with
items.length.
overflow-hidden and height: 'auto' during the spring; very long content still animates but may feel slower—consider truncating or splitting dense sections if needed.