Components/Layout
Accordion
Accordions are stacked rows that each hide a panel of content until the reader opens it. They let a long page offer many sections at once while showing only the one that was asked for. A row’s label is what tells the reader whether it holds the answer they came for, so it carries most of the weight.
import { Accordion } from '@growth-protocol-ai/alpha';Monthly, on the day you subscribed, to the card on file.
Yes. Your plan stays active until the end of the period you paid for.
Within fourteen days of a charge, for any reason.
When to use
Reach for an accordion when a surface holds several independent sections and any given reader wants one or two of them. It trades a screen of scrolling for a click per answer, which is a good trade for a list of questions and a poor one for anything the reader has to read in order.
When not to use
Do not hide behind an accordion anything the reader needs in order to get on — a form put in one costs them a click per field. Sections that are alternatives rather than a stack belong in tabs, and a lone section that opens is usually a section that should simply be on the page.
size2 values
How much room a trigger row takes, set on the group so every row in it matches. Both draw the same separator and the same glyph at the same size — only the padding and the label’s type change, and the row’s height is what those two add up to rather than a number of its own.
Monthly, on the day you subscribed, to the card on file.
Yes, and your plan runs to the end of the period you paid for.
Monthly, on the day you subscribed, to the card on file.
Yes, and your plan runs to the end of the period you paid for.
| size | When to use |
|---|---|
regular | The default. Tight padding and the button’s type size, for an accordion inside something else — a settings panel, a sidebar, a card. |
large | Generous padding and heading type, for an accordion that is the page rather than a part of it. An FAQ, a long-form disclosure list. |
Monthly, on the day you subscribed.
Yes, at any time.
Monthly, on the day you subscribed, to the card on file.
Yes, at any time.
Monthly, on the day you subscribed, to the card on file.
Yes, and your plan runs to the end of the period you paid for.
Editable, like any other row.
Locked while an invoice is being processed — still open, still readable.
| State | When to use |
|---|---|
All closed | The resting stack: separators and labels, each row carrying a plus. Nothing about a closed row is dimmed — it is a row with something to say, not an absence. |
One open | The trigger’s plus becomes a minus and the panel animates open on a grid row. aria-expanded on the trigger is the state, and every open and closed rule keys off that attribute rather than a class — so the first server-rendered paint is already correct. |
More than one open | With multiple set, opening a row leaves the others alone. Off by default, so the reader is never scrolling past two answers to reach a third. |
A locked row | disabled sits on an item, not the group. The row takes a --button-inactive fill under --text-disabled text, drops out of the keyboard’s reach and is announced as disabled — and it renders open or closed exactly as it was given, because a locked row still has something to say. |
The trigger is a real button whose accessible name is the label, and aria-expanded on it is the open state — there is no separate class or attribute to keep in step.
Each trigger points at its panel with aria-controls, and the panel back at its trigger, so a screen reader can move between the two. Tab reaches every enabled trigger in order; a disabled row is skipped entirely.
The glyph is decorative and hidden from assistive technology. Do not put the only copy of anything in it.
Nothing here traps focus or moves it on open, because the panel is part of the page rather than a layer over it.
An item always lives inside a group, even when there is only one of them. AccordionItem reads the open set and the mutex off its Accordion and throws without one — a lone row is a group with a single child, not a naked item.
Give every item a stable value. A value derived from an array index moves the open panel when the list reorders, and two items sharing one open and close together.
The glyph is a plus that becomes a minus, and never a rotating chevron. A chevron says navigation, and Alpha uses it for what drops down — a select, a menu.
Controlled open set
open with onOpenChange, when the state lives in yours. onOpenChange reports the whole new open set after a click, not the item that was clicked, so nothing has to branch on whether multiple is set.
Ships within two working days.
Thirty days, in the original packaging.
A panel that is not a paragraph
The component adds no padding to a panel, because a panel holding a paragraph and one holding a table want different amounts of it.
| Resource | Used | Included |
|---|---|---|
| API calls | 142,908 | 250,000 |
| Storage | 18.4 GB | 50 GB |
| Owner | Prop | Type | Default | Notes |
|---|---|---|---|---|
Accordion | size | "regular" | "large" | "regular" | On the group, so every row in it matches. There is no per-item size: rows of two heights in one stack read as two accordions. |
Accordion | multiple | boolean | false | Whether more than one panel may stand open. The group owns this, and it is the only thing that decides how many entries the open set can hold. |
Accordion | open | readonly string[] | — | Controlled open items, by item value. Pass it with onOpenChange when the state lives in yours; leave it off and the group keeps its own. |
Accordion | defaultOpen | readonly string[] | — | The items to start open when the group is uncontrolled. An array in both modes, so nothing has to branch on multiple. |
Accordion | onOpenChange | (open: readonly string[]) => void | — | The whole new open set after a click, not the item that was clicked. Fires whether the click opened or closed something. |
AccordionItem | value | string | — | Required: it is the identity onOpenChange reports and the name defaultOpen and open refer to. |
AccordionItem | label | ReactNode | — | The trigger row’s text, in a span of the component’s own — the glyph beside it is the component’s and is not yours to set. |
AccordionItem | disabled | boolean | false | On an item, not the group. Non-interactive, at whatever open state it was rendered in. |
AccordionItem | children | ReactNode | — | The panel’s contents. Nothing is imposed on them, including their spacing. |
AccordionItem | accordion-content | [data-slot] | — | The wrapper around your panel content. This is where padding of your own goes. |
AccordionItem | accordion-panel | [data-slot] | — | The grid the reveal animates. Padding here is kept while the panel is closed, which is why the content wrapper exists. |