Accordion
Stacked disclosure rows. One section open at a time, or many.
Accordion is a stack of rows that each show a heading and hide a block of content until the
row is opened. It is the group version of Collapsible,
so it manages which rows are open for you, and it can keep one open at a time or let several
stay open. A caret trails each label and turns when the row opens, and a rule in
border-border separates the rows.
Usage
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from '@/components/ui/accordion';
<Accordion type="single" collapsible defaultValue="one">
<AccordionItem value="one">
<AccordionTrigger>What is a look?</AccordionTrigger>
<AccordionContent>A retune of brand color, type, and radius.</AccordionContent>
</AccordionItem>
<AccordionItem value="two">
<AccordionTrigger>What stays in system?</AccordionTrigger>
<AccordionContent>Status colors and the chrome ladder.</AccordionContent>
</AccordionItem>
</Accordion>Every AccordionItem needs a value, and type is required on the root.
One open or several
type="single" keeps at most one row open, and opening another closes the first. Without
collapsible, the open row cannot be closed by pressing it again, so one row stays open once
any has been. Add collapsible so the open row can close too. Control it with value as a
string and onValueChange, or start it with defaultValue.
type="multiple" lets every row open and close on its own, with value and defaultValue
as arrays of item values. It is always collapsible.
<Accordion type="multiple" defaultValue={['shipping', 'returns']}>
{/* items */}
</Accordion>Rows and motion
AccordionTrigger is a full-width button inside an h3, with the label at the start and a
16px CaretDown at the end that turns 180 degrees when the row opens. The label is
text-fg in medium weight with py-4, and it underlines on hover. AccordionContent is
text-sm text-fg-muted, and its className goes to an inner block that has pb-4, so put
spacing there and not on the outer element that animates.
Each item draws a rule on its bottom edge, so the last row has one too. Opening and closing
animate the height of the content over --duration-base with --ease-out, and a row that
starts open does not animate on mount. A closed row is not rendered at all, so its content
is not in the page until it opens. Under reduced motion the caret turns instantly and the
content appears without sliding.
Props
Accordion takes every prop of the Radix Accordion, such as type, collapsible, value,
defaultValue, onValueChange, disabled, orientation, and dir on the root, and
value and disabled on an item. It adds no props of its own.
Accessibility
- Each trigger is a
buttonwitharia-expandedinside a heading, and each panel is aregionlabelled by its trigger. - Every trigger is a tab stop, and Space or Enter toggles the row. Down and Up move focus to
the next and previous trigger and wrap at the ends, and Home and End jump to the first and
last. With
orientation="horizontal"the keys are Right and Left instead. Disabled triggers are skipped. - In
type="single"withoutcollapsible, the open row's trigger getsaria-disabledwhile it is open, because pressing it does nothing. - Focus shows a 2px ring on
:focus-visible, and in forced colors the focus outline still shows. The caret isaria-hidden, sincearia-expandedalready says whether a row is open. The trigger target is the label row atpy-4and is not enlarged on touch. - Under reduced motion the caret does not turn and the panel does not animate.
When to use something else
- Collapsible for one block that opens on its own, with a trigger you style yourself. Accordion is for a stack of rows that share a look.
- Tabs when only one panel shows at a time and the reader should see every label at once beside it.
- Section when the blocks are always visible, with a title and actions on each. Reach for Accordion when the content is long enough that most of it should start closed.
Install
shadcn add @gesso/accordion