Accordion
Overview
A vertically (or horizontally) stacked set of interactive headings that each reveal a section of content, supporting single or multiple expansion. SAccordion combines an AccordionRoot/AccordionItem/AccordionHeader/AccordionTrigger/AccordionContent/AccordionDescription family of Aria primitives (zero styles) with the accordionVariants style recipe (8 slots, 6 sizes).
Use it for FAQs, settings groups, collapsible navigation, or any “expand to reveal” pattern where one (or several) sections should be open at a time. Prefer tabs when only one panel should be visible at once and each panel has a persistent label, and collapsible for a single standalone foldable section.
SAccordion aggregates the primitives through AccordionCompact and is items-driven with item/leading/title/trigger-icon/content slots. For fully custom compositions, fall back to the Aria AccordionRoot-family primitives.
Usage
Features
- 🧩 Aria/styled split —
AccordionCompactaggregates the 6 primitives and isitems-driven;SAccordiononly injects styles and forwards slots/events - 🎚️ Single / multiple —
multipletoggles one-open vs many-open (M extends booleangeneric);collapsibleallows all sections closed - ⌨️ Keyboard navigation — arrow keys move between triggers (
useArrowNavigation), respectingorientationanddir; triggers exposearia-expanded/aria-disabled/data-state - 🧭 Orientation —
vertical(default) orhorizontal - ⛔ Disabled items — per-item
disabledand rootdisabledpropagation - 🎨 6 sizes — xs–2xl
sizematchingThemeSize - 🔧 Fully customizable —
as/asChildon the trigger,leading/title/trigger-icon/content/itemslots, and per-slotuioverrides - ♿ Accessible by default — real
<button>triggers witharia-expanded, collapsible animation viadata-state, andaxe-corezero violations
Component family
SAccordion(styled) — the entry wrapper;accordionVariantsrecipe with dynamic slot forwardingAccordionRoot(Aria) — the state owner;useSelectionmanages single/multiplemodelValue, exposesdir/orientationAccordionItem(Aria) — one section; derives its open/disabled state from the root and wires arrow-key navigationAccordionHeader(Aria) — the header container for the triggerAccordionTrigger(Aria) — the<button>trigger;aria-expanded/aria-disabled/data-stateand click-to-toggleAccordionContent(Aria) — the animated collapsible content (CollapsibleContent)AccordionDescription(Aria) — the default description text inside contentAccordionCompact(Aria) — the aggregated composite; iteratesitemsinto items and exposes the customization slots
Demos
Basic
Custom Icon
Custom Styling
API
Aria Layer
8Accordion<T, M>
Props
| Prop | Type | Default | Description |
|---|---|---|---|
class | ClassValue | — | root class |
size | ThemeSize | — | Visual size of the component. |
ui | Partial<AccordionUi> | — | Per-slot class overrides for the component. |
items | T[] | — | Items rendered by the component. |
itemProps | AccordionItemProps | — | Properties forwarded to the item element. |
headerProps | AccordionHeaderProps | — | Properties forwarded to the header element. |
triggerProps | AccordionTriggerProps | — | Properties forwarded to the trigger element. |
contentProps | AccordionContentProps | — | Properties forwarded to the content element. |
descriptionProps | AccordionDescriptionProps | — | Properties forwarded to the description element. |
collapsible | boolean | false | When type is "single", allows closing content when clicking trigger for an open item.
When type is "multiple", this prop has no effect. |
dir | Direction | 'ltr' | The reading direction of the accordion when applicable. If omitted, assumes LTR (left-to-right) reading mode. |
disabled | boolean | false | When `true`, prevents the user from interacting with the accordion and all its items |
orientation | DataOrientation | 'vertical' | The orientation of the accordion. |
unmountOnHide | boolean | `true` | When `true`, the element will be unmounted on closed state. |
modelValue | (M extends true ? string[] : string) | — | The controlled value of the selected item(s).
Use this when you need to control the state of the items. Can be bound with `v-model` |
defaultValue | (M extends true ? string[] : string) | — | The default value of the selected item(s).
Use this when you need to set the initial state of the items. |
multiple | M | — | Determines whether a "single" or "multiple" items can be selected at a time.
This prop will overwrite the inferred type from `modelValue` and `defaultValue`. |
selectionBehavior | SelectionBehavior | 'toggle' | How multiple selection should behave in the collection. |
Emits
| Emit Name | Parameters | Description |
|---|---|---|
update:modelValue | M extends true ? string[] : string | — |
Slots
| Slot Name | Parameters | Description |
|---|---|---|
item | AccordionCompactSlotProps<T, M> | Custom content for the item slot. |
leading | AccordionCompactSlotProps<T, M> | Custom content for the leading slot. |
title | AccordionCompactSlotProps<T, M> | Custom content for the title slot. |
trigger-icon | AccordionCompactSlotProps<T, M> | Custom content for the trigger icon slot. |
content | AccordionCompactSlotProps<T, M> | Custom content for the content slot. |
AccordionCompact<T, M>
Props
| Prop | Type | Default | Description |
|---|---|---|---|
items | T[] | — | Items rendered by the component. |
itemProps | AccordionItemProps | — | Properties forwarded to the item element. |
headerProps | AccordionHeaderProps | — | Properties forwarded to the header element. |
triggerProps | AccordionTriggerProps | — | Properties forwarded to the trigger element. |
contentProps | AccordionContentProps | — | Properties forwarded to the content element. |
descriptionProps | AccordionDescriptionProps | — | Properties forwarded to the description element. |
collapsible | boolean | false | When type is "single", allows closing content when clicking trigger for an open item.
When type is "multiple", this prop has no effect. |
dir | Direction | 'ltr' | The reading direction of the accordion when applicable. If omitted, assumes LTR (left-to-right) reading mode. |
disabled | boolean | false | When `true`, prevents the user from interacting with the accordion and all its items |
orientation | DataOrientation | 'vertical' | The orientation of the accordion. |
unmountOnHide | boolean | `true` | When `true`, the element will be unmounted on closed state. |
modelValue | (M extends true ? string[] : string) | — | The controlled value of the selected item(s).
Use this when you need to control the state of the items. Can be bound with `v-model` |
defaultValue | (M extends true ? string[] : string) | — | The default value of the selected item(s).
Use this when you need to set the initial state of the items. |
multiple | M | — | Determines whether a "single" or "multiple" items can be selected at a time.
This prop will overwrite the inferred type from `modelValue` and `defaultValue`. |
selectionBehavior | SelectionBehavior | 'toggle' | How multiple selection should behave in the collection. |
Emits
| Emit Name | Parameters | Description |
|---|---|---|
update:modelValue | M extends true ? string[] : string | — |
Slots
| Slot Name | Parameters | Description |
|---|---|---|
item | AccordionCompactSlotProps<T, M> | Custom content for the item slot. |
leading | AccordionCompactSlotProps<T, M> | Custom content for the leading slot. |
title | AccordionCompactSlotProps<T, M> | Custom content for the title slot. |
trigger-icon | AccordionCompactSlotProps<T, M> | Custom content for the trigger icon slot. |
content | AccordionCompactSlotProps<T, M> | Custom content for the content slot. |
AccordionContent
Props
| Prop | Type | Default | Description |
|---|---|---|---|
forceMount | boolean | — | Used to force mounting when more control is needed.
Useful when controlling animation with Vue animation libraries. |
asChild | boolean | — | Change the default rendered element for the one passed as a child, merging their props and behavior. |
as | AsTag | Component | 'div' | The element or component this component should render as. Can be overwrite by `asChild` |
AccordionDescription
AccordionHeader
AccordionItem
Props
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | — | Value of the accordion item. All items within an accordion should use a unique value. |
disabled | boolean | — | When `true`, prevents the user from interacting with the collapsible. |
unmountOnHide | boolean | — | When `true`, the element will be unmounted on closed state. |
asChild | boolean | — | Change the default rendered element for the one passed as a child, merging their props and behavior. |
as | AsTag | Component | 'div' | The element or component this component should render as. Can be overwrite by `asChild` |
AccordionRoot<M>
Props
| Prop | Type | Default | Description |
|---|---|---|---|
collapsible | boolean | false | When type is "single", allows closing content when clicking trigger for an open item.
When type is "multiple", this prop has no effect. |
dir | Direction | 'ltr' | The reading direction of the accordion when applicable. If omitted, assumes LTR (left-to-right) reading mode. |
disabled | boolean | false | When `true`, prevents the user from interacting with the accordion and all its items |
orientation | DataOrientation | 'vertical' | The orientation of the accordion. |
unmountOnHide | boolean | `true` | When `true`, the element will be unmounted on closed state. |
modelValue | (M extends true ? string[] : string) | — | The controlled value of the selected item(s).
Use this when you need to control the state of the items. Can be bound with `v-model` |
defaultValue | (M extends true ? string[] : string) | — | The default value of the selected item(s).
Use this when you need to set the initial state of the items. |
multiple | M | — | Determines whether a "single" or "multiple" items can be selected at a time.
This prop will overwrite the inferred type from `modelValue` and `defaultValue`. |
selectionBehavior | SelectionBehavior | 'toggle' | How multiple selection should behave in the collection. |
Emits
| Emit Name | Parameters | Description |
|---|---|---|
update:modelValue | M extends true ? string[] : string | — |
AccordionTrigger
Props
| Prop | Type | Default | Description |
|---|---|---|---|
disabledCollapsible | boolean | — | When `true`, prevents the user from toggling the collapsible. |
type | ButtonType | 'button' | The type of the button element. Can be one of 'button', 'submit', or 'reset'. |
disabled | boolean | — | Whether the component is disabled. |
asChild | boolean | — | Change the default rendered element for the one passed as a child, merging their props and behavior. |
as | AsTag | Component | 'div' | The element or component this component should render as. Can be overwrite by `asChild` |
Notes
Architecture and benchmark differences
AccordionCompact owns the items iteration and default content assembly while every primitive stays style-free and only the UI wrapper injects the accordionVariants classes. This mirrors shadcn/ui’s headless split and Radix’s Accordion primitive, unlike Ant Design, Element Plus, Mantine and Naive UI which ship a config-driven collapse with defaultActiveKey/activeKey. Vean’s root uses useSelection so single/multiple modes share one state model, and arrow-key navigation is driven by the same useArrowNavigation composable used across the menu family for consistency.
| Capability | VeanUI | shadcn/ui | Ant Design Collapse | Element Plus Collapse | Mantine Accordion | Naive UI Collapse |
|---|---|---|---|---|---|---|
| Aria/styled split | ✅ | ✅ | — | — | — | — |
| Single / multiple | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Collapsible (all closed) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Keyboard arrow navigation | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Custom trigger icon | ✅ | — | ✅ | ✅ | ✅ | ✅ |
Data-driven items (Compact) | ✅ | — | ✅ | ✅ | ✅ | ✅ |
| Disabled item | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Orientation (v/h) | ✅ | — | ✅ | ✅ | — | — |
as/asChild trigger | ✅ | ✅ | — | — | — | — |
— = unsupported or a different interaction model.
Cautions
- In single mode (
multiple: false) only one section stays open; withcollapsible(defaulttrue) clicking the open trigger closes it, leaving all sections closed. - Arrow-key navigation requires a trigger to be focused; the keys follow
orientation(ArrowUp/ArrowDownvertically,ArrowLeft/ArrowRighthorizontally) and swap under RTL viadir. unmountOnHide(defaulttrue) unmounts closed content; set it tofalseto keep it in the DOM (e.g. to preserve inner form state).- The default trigger chevron rotates with CSS based on
data-state; replace it via thetrigger-iconslot if you need a different indicator. - Disabled items ignore clicks and are skipped by arrow navigation but remain reachable in the DOM order for assistive tech.
Roadmap
No blocking gaps identified for the core accordion API. A bordered variant set and a left-icon trigger layout are evaluated as enhancements tracked in docs/roadmap/README.md.
FAQ
How do I allow only one section open at a time?
Single mode is the default — bind modelValue to a single value:
<SAccordion v-model="open" :items="items" />How do I allow multiple sections open?
Set multiple and bind an array:
<SAccordion v-model="openList" multiple :items="items" />How do I let all sections be closed?
collapsible is true by default, so clicking the open trigger closes it:
<SAccordion collapsible :items="items" />How do I customize the trigger icon?
Use the trigger-icon slot (or leading for a left icon):
<SAccordion :items="items">
<template #trigger-icon="{ open }">
<SIcon :icon="open ? 'lucide:minus' : 'lucide:plus'" />
</template>
</SAccordion>How do I build a fully custom item?
Use the item slot to compose the Aria parts yourself:
<SAccordion :items="items">
<template #item="{ item, open }">
<div class="border-b">
<button type="button" :aria-expanded="open">{{ item.title }}</button>
<div v-if="open">{{ item.description }}</div>
</div>
</template>
</SAccordion>