Accordion
A vertically stacked set of interactive headings that each reveal a section of content — one open at a time, or several.
- Status
- Since
0.1.0- Accessibility pattern
- APG accordion
Last updated
Install
Configure registry credentials first using the Quickstart. Before copying this item or any transitive registry dependency, verify the signed manifest and save the exact bytes for Accordion before installation. Retain the digest printed by this command.
pnpm exec vegastack-design verify accordionInstall with the pinned shadcn CLI. Bare shadcn does not verify VegaStack signature or integrity.
pnpm dlx shadcn@4.21.0 add @vegastack/accordionRun the exact offline --post-write command printed by the preflight, using its saved item path and independently retained --expected-integrity digest. Use the saved preflight for every transitive registry dependency too; stop on any mismatch before using the components.
Usage
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "@/components/ui/accordion";
<Accordion defaultValue={["item-1"]}>
<AccordionItem value="item-1">
<AccordionTrigger>Is it accessible?</AccordionTrigger>
<AccordionContent>
Yes. It adheres to the WAI-ARIA design pattern.
</AccordionContent>
</AccordionItem>
</Accordion>;value and defaultValue are arrays, even when only one section can be open — the single-open rule
is the default, and multiple lifts it.
Anatomy
Examples
Composition
An AccordionItem pairs a trigger with the panel it reveals, and the value on the item is what
defaultValue and value on the root name.
Accordion
├── AccordionItem
│ ├── AccordionTrigger
│ └── AccordionContent
└── AccordionItem
├── AccordionTrigger
└── AccordionContentBasic
One section at a time, with the first open. Closing the open section by clicking its own trigger is allowed — the accordion can rest with nothing expanded.
Multiple
multiple on the root lets several sections stay open at once. Everything else is unchanged: the
value is still the array of open items.
Disabled
disabled on an AccordionItem takes that section out of play. The trigger keeps its pointer
events (FRM-4), so a tooltip or a hover explanation can still say why it is unavailable.
Borders
The accordion ships with a hairline between items and nothing around them. Add border to the root
and border-b last:border-b-0 to each item to box the whole stack and keep the rules even.
Card
Dropped into CardContent, the accordion inherits the card's padding, so the triggers line up with
the card title above them.
RTL
Wrap the subtree in DirectionProvider direction="rtl". The trigger's text alignment and the
chevron's position are logical (text-start, ms-auto), so they follow the reading direction
without a per-component override.
API Reference
Accordion, AccordionItem, AccordionTrigger and AccordionContent add no props of their own —
each accepts everything the corresponding Base UI Accordion
part accepts (Root, Item, Trigger, Panel): value / defaultValue / onValueChange,
multiple, disabled, keepMounted and hiddenUntilFound on the root, value and disabled on
an item, plus className, ref and render everywhere.
Accessibility
- Renders Base UI's Accordion: each trigger is a button inside a heading, carrying
aria-expandedandaria-controls, and the panel it names is a labelled region. - Tab moves between triggers, Enter and Space toggle the focused section. Base UI follows the 2024 APG update that removed roving focus from the pattern, so every trigger is its own tab stop.
- A disabled item's trigger is
aria-disabled, not nativelydisabled, and keeps its pointer events (FRM-4) — it stays reachable and hoverable so the reason can be explained in place. - Focus is the global
:focus-visiblebackground tint frombase.css; there is no ring glow on a trigger, and forced colors retain their separate outline. - The chevrons are decoration: they are swapped by an
aria-expandedvariant and never carry the state on their own.
| Contract | States tested |
|---|---|
| Behaviour | default, open, closed, disabled |
| Accessibility | native-or-base-ui-semantics, browser-accessibility-test, keyboard-navigation |
| Visual | default, hover, open, disabled |
Do / Don't
Deviations
Upstream's file plus packages/ui/upstream/patches/accordion.patch. Every hunk:
- FOC-1, FOC-6 —
outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 focus-visible:after:border-ringgoes fromAccordionTrigger.base.cssowns the no-ring focus tint and a separate forced-colors outline. - FRM-4 —
aria-disabled:pointer-events-noneis dropped, so a disabled section header stays hoverable and a Tooltip can explain why it is unavailable. The opacity half stays. - DOC-2 —
import { cn } from "cn"becomes@vegastack/design; our npm layer ownscn. - DOC-1 — prettier reflow plus the
registry-stamp.mjsprovenance header the three-copy model needs; no decision of its own.