Accordion
A stack of disclosure rows built on Radix Accordion: keyboard nav, ARIA, and single or multiple expansion. The variant axis spans a flush minimal list, contained cards, a pill list that hugs its own text, and a board grid.
Installation#
Usage#
import {
Accordion,
AccordionItem,
AccordionTrigger,
AccordionContent,
} from "@/components/ui/accordion"
export function Example() {
return (
<Accordion type="single" collapsible>
<AccordionItem value="item-1">
<AccordionTrigger>Section title</AccordionTrigger>
<AccordionContent>Section body.</AccordionContent>
</AccordionItem>
</Accordion>
)
}Anatomy#
import { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from "@/components/ui/accordion"
<Accordion>
<AccordionItem>
<AccordionTrigger />
<AccordionContent />
</AccordionItem>
</Accordion>Variants#
Three levels of chrome along one axis. minimal is a flush list of rows split by hairlines; card wraps the set in a single bordered box with dividers; separated gives every item its own. None of them paints a fill: they take the ground they sit on, so on a muted band the rows read as the band.
Off that axis sits pill, the one variant where a closed row does not span the list: each question hugs its own text as a chip, and the open one floods to full width and squares off into a filled card. It is a control, not a container, so it does paint a fill: the same ink tint a Tabs segment uses, which reads one step off whatever ground it lands on. Built for a short list beside something else, a photograph or a product shot, where a ragged column of labels beats a stack of full-width rules.
board takes separated's outline and lays it out as a grid: closed questions read as a wall you scan rather than a list you go down, and the one you open spans every column, so its answer gets a full measure instead of a third of one. The root is the grid, so its column count comes from a className like lg:grid-cols-3.
Single vs. multiple#
Radix drives expansion. type="single" keeps one row open at a time. Add collapsible to allow closing the open row. type="multiple" lets several stay open at once.
Density#
Like Card and Dialog, Accordion honors Koala's density axis. compact is the default, tight row height and inset for dense application UI; comfortable opens it up for marketing pages. Set it per-instance or once via a DensityProvider.
Icon position#
iconPosition controls where the caret sits. trailing (the default) pins it to the right edge of the row; leading moves it ahead of the label for a help-center / knowledge-base look. The caret still flips on open and keeps its ARIA wiring either way.
Marker#
marker picks the disclosure mark. caret (the default) is a chevron that flips a full 180°; plus is the FAQ / help-centre mark, a plus that turns 45° into a cross. It is the same glyph and the same turn GalleryAccordion uses, so the library has one disclosure mark rather than two idioms for the same gesture. Both ride the shared caret-turn beat, so glyph and height tween move together, and both stay neutral in every state: the motion is the feedback, not a color change.
Size#
size is the type scale, orthogonal to density (which only moves spacing). md (the default) leaves the row to density; lg is the landing-page FAQ, where each question reads as a heading: an 18px semibold question on a taller row, a 16px answer and a 20px mark. Faqs sets it for every list inside with its own size.
API reference#
Accordion
The root, over Radix Accordion.Root: forwards all its props (disabled, orientation, dir among them). Every part accepts className, merged last.
| Prop | Type | Default | Description |
|---|---|---|---|
| type* | "single" | "multiple" | - | Whether one row or any number of rows can be open. |
| collapsible | boolean | false | With type="single", lets the open row close again. |
| value | string | string[] | - | The open row (or rows, an array with type="multiple"), controlled. |
| defaultValue | string | string[] | - | The row or rows open on first render, uncontrolled. |
| onValueChange | (value: string | string[]) => void | - | Called when a row opens or closes. |
| variant | "minimal" | "card" | "separated" | "pill" | "board" | "minimal" | The chrome: a hairline list, one contour, one card per item, chips that flood open, or a grid of tiles. |
| density | "comfortable" | "compact" | "compact" | Row height and content inset. Falls back to the nearest DensityProvider, then compact. |
| size | "md" | "lg" | "md" | The type scale. lg is the landing-page FAQ: an 18px semibold question over a 16px answer. |
| marker | "caret" | "plus" | "caret" | The disclosure mark: a chevron that flips 180°, or a plus that turns 45° into an ✕. |
| iconPosition | "trailing" | "leading" | "trailing" | Puts the mark after or before the question, in DOM order, so focus and reading order follow. |
| Data attribute | Description |
|---|---|
| data-orientation | "vertical" or "horizontal", from Radix. |
AccordionItem
One row, over Radix Accordion.Item: forwards all its props.
| Prop | Type | Default | Description |
|---|---|---|---|
| value* | string | - | Identifies the row in value and defaultValue. |
| disabled | boolean | false | Stops the row from opening or closing. |
| Data attribute | Description |
|---|---|
| data-state | "open" or "closed". |
| data-disabled | Present when the row is disabled. |
AccordionTrigger
The question: a <button> inside a Radix Accordion.Header (<h3>). Forwards all Radix Accordion.Trigger props and draws the mark the root's marker picks.
| Data attribute | Description |
|---|---|
| data-state | "open" or "closed". Drives the turn of the mark. |
| data-disabled | Present when the row is disabled. |
AccordionContent
The answer, over Radix Accordion.Content: forwards all its props (forceMount among them). className lands on the padded inner <div>, not the animated wrapper, so the height tween stays smooth.
| Data attribute | Description |
|---|---|
| data-state | "open" or "closed". Keys the open and close animations. |
| CSS variable | Description |
|---|---|
| --radix-accordion-content-height | The measured height of the answer, read by the open and close keyframes. |
| --radix-accordion-content-width | The measured width of the answer. |