Accordion
Sections that expand and collapse, one or several at a time, built on Base UI Accordion.
Preview
accordion · base base · style base-lyraOpen under the product themeInstallation
pnpm dlx shadcn@latest add @opsinjs/accordion
The @opsinjs namespace is declared in your components.json. Everything it installs is code you then own. There is no runtime package to keep in step.
Usage
import { Accordion } from "@/components/ui/accordion"<Accordion
items={[
{ value: "hours", title: "When can I reach the team?", content: "Weekday mornings." },
{ value: "records", title: "Where are my notes kept?", content: "On your device." },
]}
/>When to use it
Use it when
- An index of related sections a reader scans first and opens one at a time, such as questions and answers.
- Detail a reader wants on demand but not on arrival, such as small print under a summary.
- Several independent groups of settings on one small screen, opened together with multiple.
Do not use it when
- A safety message, an urgent instruction or anything a reader must not miss. A collapsed panel may never be opened.Use
calloutinstead. - Content that must always be visible, not behind a header a reader has to press.Use
cardinstead. - Choosing a header should swap the whole view for another in the same space, not reveal detail in place.Use
tabsinstead.
Anatomy
AccordionBase UI's Accordion.Root: one card surface with a hairline that manages open state. Neutral chrome, neither colour axis.Controlled byitemsAccordion.ItemOne section, a header with its panel. A top hairline divides it from the section above.Controlled byitemsAccordion.HeaderThe section's heading, rendered by Base UI as an h3 so it sits in the document outline.Accordion.TriggerThe full-width button that toggles the section, with aria-expanded, aria-controls, a 44pt target floor, the title and chevron.Controlled byvalueAccordion.PanelThe revealed content, unmounted while closed, so a shut panel costs nothing and leaves the tab order.Controlled bycontent
- Accordion 1
- Accordion.Item 1..n
- Accordion.Header 1
- Accordion.Trigger 1
- Accordion.Panel 1
- Accordion.Header 1
- Accordion.Item 1..n
Examples
A frequently asked list
Three headers form an index; the reader opens one. Fictional copy.
accordion-frequently-asked · base base · style base-lyraOpen under the product themeOne section open at a time
With multiple off (the default), opening one section closes the last.
accordion-one-open-at-a-time · base base · style base-lyraOpen under the product themeContent guidelines
Write headers as a scannable index: a question, a noun phrase or a setting name, in sentence case, without terminal punctuation. Never fold a warning or a required step into a panel that starts closed. It belongs in a Callout that stays open.
Do
"When can I reach the team?" The reader's own question, short enough to scan against its siblings.
Don’t
A "More" panel holding "Call your clinician if this reading keeps rising." An instruction the reader may never open.
Accessibility
Audited against WCAG 2.2 AA in a source pass and a rendered pass. It is author-run, not independent, and clinical review is pending. The rendered pass (axe, target size, focus visible, 320px reflow) was clean.
One defect was fixed. The trigger's focus ring was drawn outside the trigger,
where the root's overflow-hidden clipped it (2.4.7). It now renders inside,
whole on every straight edge, with a small notch still clipped at the first and
last sections' outer corners, a known residual.
- Base UI renders each header as an
h3with abutton, wiresaria-expandedandaria-controlsbetween trigger and panel, and hides the chevron witharia-hidden, so a reader relies onaria-expanded, not the glyph. - The chevron's turn uses
--opsin-duration-fast, collapsed underprefers-reduced-motion. A closed panel is unmounted, leaving the tab order.
Still unmeasured. The rendered pass does not compute contrast, so every pair against the card surface is unmeasured, not known-failing. That no must-see content may sit in a closed panel stays a docs contract the wrapper cannot enforce.
| Key | Action | Notes |
|---|---|---|
| Tab | Moves focus to the next header | An ordinary tab stop. A disabled header keeps its stop. |
| Shift+Tab | Moves focus to the previous header | The same in reverse. |
| Enter, Space | Opens or closes the focused section | A native button. With multiple off, the open section closes. |
| Arrow keys | Not bound | Base UI follows the updated APG guidance, which dropped roving focus. |
| Pair | Theme | APCA Lc | WCAG 2.2 | Floor |
|---|---|---|---|---|
| body text on the page | light | 101.6 | 17.18:1 | Pass |
| body text on the page | dark | -100.5 | 18.00:1 | Pass |
| body text on a card | light | 104.7 | 17.96:1 | Pass |
| body text on a card | dark | -99.6 | 16.32:1 | Pass |
| body text on the muted ground | light | 98.1 | 16.32:1 | Pass |
| body text on the muted ground | dark | -97.5 | 13.77:1 | Pass |
| secondary text on the page | light | 83.9 | 7.42:1 | Pass |
| secondary text on the page | dark | -80.7 | 13.44:1 | Pass |
| secondary text on a card | light | 87.0 | 7.76:1 | Pass |
| secondary text on a card | dark | -79.8 | 12.19:1 | Pass |
| secondary text on the muted ground | light | 80.4 | 7.05:1 | Pass |
| secondary text on the muted ground | dark | -77.8 | 10.28:1 | Pass |
| a hairline boundary on the page | light | 19.3 | 1.41:1 | Below floor |
| a hairline boundary on the page | dark | -8.2 | 1.90:1 | Below floor |
| a hairline boundary on a card | light | 22.4 | 1.47:1 | Below floor |
| a hairline boundary on a card | dark | -7.3 | 1.72:1 | Below floor |
| a hairline boundary on the muted ground | light | 15.8 | 1.34:1 | Below floor |
| a hairline boundary on the muted ground | dark | 0.0 | 1.45:1 | Below floor |
| a placeholder boundary on the page | light | 69.8 | 4.52:1 | Pass |
| a placeholder boundary on the page | dark | -51.0 | 7.62:1 | Pass |
| a placeholder boundary on a card | light | 72.8 | 4.72:1 | Pass |
| a placeholder boundary on a card | dark | -50.1 | 6.91:1 | Pass |
| a placeholder boundary on the muted ground | light | 66.2 | 4.29:1 | Pass |
| a placeholder boundary on the muted ground | dark | -48.1 | 5.83:1 | Pass |
| a placeholder fill on the page | light | 8.3 | 1.18:1 | Below floor |
| a placeholder fill on the page | dark | -8.2 | 1.90:1 | Below floor |
| a placeholder fill on a card | light | 11.4 | 1.23:1 | Below floor |
| a placeholder fill on a card | dark | -7.3 | 1.72:1 | Below floor |
| a placeholder fill on the muted ground | light | 0.0 | 1.12:1 | Below floor |
| a placeholder fill on the muted ground | dark | 0.0 | 1.45:1 | Below floor |
| a placeholder boundary on its own fill | light | 58.9 | 3.84:1 | Pass |
| a placeholder boundary on its own fill | dark | -41.5 | 4.01:1 | Below floor |
| the card hairline on the page | light | 69.8 | 4.52:1 | Pass |
| the card hairline on the page | dark | -28.7 | 4.19:1 | Below floor |
| the card hairline on a card | light | 72.8 | 4.72:1 | Pass |
| the card hairline on a card | dark | -27.9 | 3.80:1 | Below floor |
These are the measured token pairs this component draws colour from, not a measurement of the component itself.
Data attributes
| Attribute | Condition | Value |
|---|---|---|
| data-slot | On the root and each part | accordion, accordion-item, accordion-header, accordion-trigger, accordion-title, accordion-panel |
| data-open | On the item and the panel | Present while expanded |
| data-panel-open | On the trigger | Present while its section is open |
| data-disabled | On the item and its trigger | Present while disabled |
API reference
Prop
Type
Generated from AccordionProps in registry/bases/base/accordion.tsx.
multiple defaults to false. value without onValueChange fixes the open
sections as given. An empty items array renders nothing and warns in
development, as does a duplicated value.
Related
Tabs
A row of tabs that switches between panels sharing one region. The list is one tab stop with a roving focus, and choosing a tab swaps the panel.
Button
A control you press to make something happen. It is the least clinical component here, and its page sets the pattern every other component page follows.