Accordion
A stack of collapsible disclosure panels; expand one to read it, collapse it to reclaim space.
Last updated
What is Apogee?
A copy-in Astro component library, styled with Tailwind and design tokens.
Does it ship JavaScript?
Only when a component genuinely needs it. Accordion is zero-JS — it’s a native <details>.
Show codeHide code
<Accordion>
<AccordionItem title="What is Apogee?">
A copy-in Astro component library, styled with Tailwind and design tokens.
</AccordionItem>
<AccordionItem title="Does it ship JavaScript?">
Only when a component genuinely needs it. Accordion is zero-JS — it's a native `<details>`.
</AccordionItem>
</Accordion>Add
npx @ava-ui/apogee add accordion
AccordionItem uses @lucide/astro (ChevronDown, CirclePlus, CircleMinus) for the disclosure icon.
Usage
---
import Accordion from '@/components/apogee/Accordion/Accordion.astro'
import AccordionItem from '@/components/apogee/Accordion/AccordionItem.astro'
---
<Accordion>
<AccordionItem title="Section one">Panel content.</AccordionItem>
<AccordionItem title="Section two">Panel content.</AccordionItem>
</Accordion>
Anatomy
Accordion— the bordered, divided container. Default slot holds one or moreAccordionItems.AccordionItem— a native<details>. Itstitleprop renders the<summary>row (label + chevron); its default slot is the panel body.
API
@composable — both exposed to Content Editors in AVA. Any other native attribute
(e.g. id, data-*) passes through on either component.
Accordion
| Prop | Type | Default | Description |
|---|---|---|---|
variant |
'outline' | 'plain' |
'outline' |
A bordered box, or dividers only. |
class |
string |
— | Extra classes, merged last. |
AccordionItem
| Prop | Type | Default | Description |
|---|---|---|---|
title |
string |
— | The <summary> label; the trigger slot replaces it with rich content. |
iconPosition |
'start' | 'end' |
'end' |
Icon side. |
icon |
'chevron' | 'plus' |
'chevron' |
A chevron that flips, or a circled plus that turns to a minus. |
variant |
'plain' | 'card' |
'plain' |
card sets the item on a rounded card, tinted while open. Stack cards in a flex flex-col gap-2 wrapper instead of Accordion. |
size |
'medium' | 'large' |
'medium' |
large for FAQ-sized questions and answers. |
group |
string |
— | Shared across items → single-open (exclusive): opening one closes the rest with the same group. Maps to <details name>. |
open |
boolean |
false |
Open on load. |
class |
string |
— | Extra classes, merged last. |
Trigger slot and icon position
The trigger slot replaces title with rich content; iconPosition moves the chevron.
Chevron first
Chevron last (default)
Billing Paid
A rich summary with a badge.
Show codeHide code
<Accordion>
<AccordionItem iconPosition="start" title="Chevron first">…</AccordionItem>
<AccordionItem>
<span slot="trigger" class="flex items-center gap-2">Billing <Badge color="positive">Paid</Badge></span>
…
</AccordionItem>
</Accordion>Variants
A plain list with plus icons at the large size, and a stack of cards:
Is there a free trial?
Can I change plans later?
Is there a free trial?
Can I change plans later?
Show codeHide code
<Accordion variant="plain">
<AccordionItem title="Is there a free trial?" icon="plus" size="large" group="plain" open>Yes, 30 days.</AccordionItem>
<AccordionItem title="Can I change plans later?" icon="plus" size="large" group="plain">Any time.</AccordionItem>
</Accordion>
<div class="flex flex-col gap-2">
<AccordionItem title="Is there a free trial?" icon="plus" variant="card" iconPosition="start" group="cards" open>Yes, 30 days.</AccordionItem>
<AccordionItem title="Can I change plans later?" icon="plus" variant="card" iconPosition="start" group="cards">Any time.</AccordionItem>
</div>States
Default closed, open on load (open), and exclusive group (shared group — opening one closes its siblings):
Closed by default
Open on load
One
Two
Three
group name.Show codeHide code
<Accordion>
<AccordionItem title="Closed by default">Collapsed until clicked.</AccordionItem>
<AccordionItem title="Open on load" open>Visible immediately.</AccordionItem>
</Accordion>
<Accordion>
<AccordionItem title="One" group="faq">Only one of these three stays open.</AccordionItem>
<AccordionItem title="Two" group="faq">Opening this closes "One" and "Three".</AccordionItem>
<AccordionItem title="Three" group="faq">Exclusive via the shared `group` name.</AccordionItem>
</Accordion>Examples
A single-panel FAQ, exclusive within itself, pre-opening the first item:
<Accordion>
<AccordionItem title="Shipping times" group="faq" open>
3–5 business days domestic.
</AccordionItem>
<AccordionItem title="Return policy" group="faq">
30 days, unworn, with tags.
</AccordionItem>
</Accordion>
Accessibility
| Key | Action |
|---|---|
Tab |
Moves focus to the next item header. |
Shift Tab |
Moves focus to the previous item header. |
Enter Space |
Opens or closes the focused item. |
- The chevron or plus icon is
aria-hidden; the visible label is the accessible name. - Exclusive grouping uses the native
<details name>attribute — no JS, no ARIA wiring needed. - Height animation respects
prefers-reduced-motion(transitions are removed).
Tokens
| Token | Used for |
|---|---|
--border-neutral |
Container border + divider between items |
--foreground-neutral |
Summary label text |
--foreground-neutral-faded |
Chevron icon + panel body text |
--outline-color-focus |
Focus ring on the summary |
--duration-medium / ease-standard |
Chevron rotation |
--duration-slow / ease-standard |
Panel height/reveal transition |
Do / Don’t
- Do use a shared
groupfor FAQ-style lists where only one answer should show at a time. - Do pre-open the most relevant item with
openrather than making users hunt. - Don’t nest interactive controls (another accordion, a form) inside a panel without checking focus order.
- Don’t rely on JS for toggling — there isn’t any; it’s native disclosure.
Content
- Titles are short questions or labels, sentence case, no ending period.
- Panel body writes complete sentences — it’s read as prose once opened, not a label.