Accordion
An accordion is a stack of headed panels that open one at a time (single)
or independently (multiple). Headless UI's Disclosure is uncontrolled,
so the open set lives in Accordion itself; each trigger is a Headless UI
Button inside a heading, wired to a role="region" panel.
import Accordion from '@zuilib/components/accordion'
Single, collapsible
type="single" (the default) keeps one item open. Without collapsible,
the open item cannot be closed by its own trigger, per the WAI-ARIA pattern;
with it, a second click collapses to none.
Variants
bordered is one card with dividers, separated a stack of cards, ghost
dividers only. The card variants read their inset from --card-padding
(falling back to --card-padding-md), the same property Card uses.
Multiple, controlled
type="multiple" types value as string[]. Pass value and
onValueChange to own the state.
Props
AccordionProps is a union of the single and multiple shapes; both
extend div props (minus defaultValue / onChange).
| Prop | Type | Default | Description |
|---|---|---|---|
type | 'single' | 'multiple' | 'single' | Decides whether value is a string or string[] |
value | string | string[] | — | Controlled open item(s); '' means none |
defaultValue | string | string[] | — | Uncontrolled initial open item(s) |
onValueChange | (value: string | string[]) => void | — | Receives the open item, or '' when a single accordion collapsed |
collapsible | boolean | false | single only: allow closing the open item. While false the open trigger is aria-disabled |
variant | 'bordered' | 'separated' | 'ghost' | 'bordered' | |
disabled | boolean | false | Disables every item |
headingLevel | 2 | 3 | 4 | 5 | 6 | 3 | Heading element wrapping each trigger |
className | string | — | Merged last onto the root |
Accordion.Item
| Prop | Type | Default | Description |
|---|---|---|---|
valuerequired | string | — | Identity in value / defaultValue / onValueChange |
disabled | boolean | false | |
className | string | — |
Accordion.Trigger
button props minus type, id, disabled and the aria-expanded /
aria-controls / aria-disabled wiring, which come from the item.
| Prop | Type | Default | Description |
|---|---|---|---|
hideChevron | boolean | false | Hide the trailing chevron |
className | string | — | Merged onto the button |
headerClassName | string | — | Merged onto the heading element wrapping the button |
Accordion.Content
div props minus id, role and aria-labelledby. The ref and every
prop except className land on the animated region wrapper.
| Prop | Type | Default | Description |
|---|---|---|---|
region | boolean | true | Expose the panel as a role="region" landmark labelled by its trigger; pass false on accordions with many panels |
unmount | boolean | false | Remove the content from the DOM while closed (after the collapse). Default keeps it mounted and hidden |
className | string | — | Merged onto the padded body |
regionClassName | string | — | Merged onto the region wrapper |
Slots
| Slot | Element | Notes |
|---|---|---|
[data-slot="accordion"] | div | Root. Also data-type, data-variant, data-disabled |
[data-slot="accordion-item"] | div | data-state="open" | "closed", data-disabled |
[data-slot="accordion-header"] | h2–h6 | Per headingLevel |
[data-slot="accordion-trigger"] | button | data-state; Headless UI data-hover / data-active / data-focus |
[data-slot="accordion-title"] | span | The trigger children |
[data-slot="accordion-chevron"] | svg | Rotates 180° while open |
[data-slot="accordion-content"] | div | The animated region; data-state |
[data-slot="accordion-content-clip"] | div | overflow-hidden during the collapse |
[data-slot="accordion-content-body"] | div | Padded body holding the children |
Tokens
| Token | Used for |
|---|---|
--card-padding (falls back to --card-padding-md) | Trigger and body inset in the card variants |
--card, --border | Card surface and dividers |
--muted | Trigger hover tint |
--ring | Inset keyboard focus ring |
--duration-normal, --ease-standard | Panel height and chevron transitions |
Accessibility
- Each trigger is a
<button>inside a heading (headingLevel, defaulth3), witharia-expandedandaria-controlspointing at its panel; the panel isrole="region"witharia-labelledbyback to the trigger. - ArrowDown / ArrowUp move focus between the triggers of one accordion (wrapping); Home / End jump to the first / last. Nested accordions own their own cycle.
- The open item of a non-collapsible
singleaccordion isaria-disabledbut stays focusable, so arrow keys still reach it. - The panel animates
grid-template-rowsfrom1frto0fr(height toauto);prefers-reduced-motionremoves the transition. - With
unmount, the trigger dropsaria-controlswhile closed since the id no longer exists.
Related
- Disclosure: a single, uncoordinated panel.
- Tabs: one visible panel, selected rather than expanded.
- Card: shares
--card-paddingwith the card variants.