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.

A commercial React component library: polished, token-driven, and Radix-backed, built to ship design systems that feel finished on day one.

Installation#

# One-time setup (tokens + lib helpers)npx koalaui-cli@latest init# Add this component (its dependencies come along)npx koalaui-cli@latest add accordion

Usage#

usage.tsxTSX
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.

minimal

Yes, plans are month-to-month and you can cancel from billing in two clicks. Access lasts through the period you've paid for.

card

Yes, plans are month-to-month and you can cancel from billing in two clicks. Access lasts through the period you've paid for.

separated

Yes, plans are month-to-month and you can cancel from billing in two clicks. Access lasts through the period you've paid for.

pill

Yes, plans are month-to-month and you can cancel from billing in two clicks. Access lasts through the period you've paid for.

board

Yes, plans are month-to-month and you can cancel from billing in two clicks. Access lasts through the period you've paid for.

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.

Free worldwide shipping on orders over $50. Most orders arrive in 3–5 business days.

30-day no-questions returns. We email a prepaid label as soon as you start the return.

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.

comfortable

Manage your profile, email, and password.

compact

Manage your profile, email, and password.

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.

trailing

Open Settings, choose Security, then Reset password. We email a link that stays valid for one hour.

leading

Open Settings, choose Security, then Reset password. We email a link that stays valid for one hour.

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.

caret

Open Settings, choose Security, then Reset password. We email a link that stays valid for one hour.

plus

Open Settings, choose Security, then Reset password. We email a link that stays valid for one hour.

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.

md

One-time. Once it is purchased it is yours, updates included, with no subscription to cancel.

lg

One-time. Once it is purchased it is yours, updates included, with no subscription to cancel.

API reference#

Accordion

The root, over Radix Accordion.Root: forwards all its props (disabled, orientation, dir among them). Every part accepts className, merged last.

PropTypeDefaultDescription
type*"single" | "multiple"-Whether one row or any number of rows can be open.
collapsiblebooleanfalseWith type="single", lets the open row close again.
valuestring | string[]-The open row (or rows, an array with type="multiple"), controlled.
defaultValuestring | 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 attributeDescription
data-orientation"vertical" or "horizontal", from Radix.

AccordionItem

One row, over Radix Accordion.Item: forwards all its props.

PropTypeDefaultDescription
value*string-Identifies the row in value and defaultValue.
disabledbooleanfalseStops the row from opening or closing.
Data attributeDescription
data-state"open" or "closed".
data-disabledPresent 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 attributeDescription
data-state"open" or "closed". Drives the turn of the mark.
data-disabledPresent 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 attributeDescription
data-state"open" or "closed". Keys the open and close animations.
CSS variableDescription
--radix-accordion-content-heightThe measured height of the answer, read by the open and close keyframes.
--radix-accordion-content-widthThe measured width of the answer.

FAQ#

They are three points on one chrome axis. `minimal` (the default) is a borderless list of rows split by hairline dividers; `card` wraps the whole set in a single bordered, shadowed surface with shared dividers; `separated` gives every `AccordionItem` its own standalone card stacked with a gap.