Accordion
The Accordion allows content to be hidden or displayed by clicking a trigger. Multiple items can be shown simultaneously, or restricted to a single item using the type prop.
Installation
import { Accordion } from '@harnessio/ui/components'
//...
return ( <Accordion.Root type="multiple" defaultValue={["item1"]} size="md"> <Accordion.Item value="item1"> <Accordion.Trigger>Trigger text</Accordion.Trigger> <Accordion.Content> <p>This is the content</p> </Accordion.Content> </Accordion.Item> </Accordion.Root>)Anatomy
All parts of the Accordion can be imported and composed as required:
<Accordion.Root> <Accordion.Item> <Accordion.Trigger /> <Accordion.Content /> </Accordion.Item></Accordion.Root>- Root — the container that wraps and controls the items. It sets whether one item (
type="single") or several (type="multiple") can be open, thesize(smdefault ormd), thevariant(defaultorcard), and theindicatorPosition(rightdefault orleft). It can be uncontrolled (defaultValue) or controlled (value+onValueChange). - Item — one collapsible panel, identified by a required, unique
value. Setdisabledto lock an individual item. - Trigger — the header row that toggles its item. It shows the label (
children), an optionalprefixfor an icon or logo, an optionalsuffixfor supporting text, and a chevron indicator that rotates on open. - Content — the region revealed when the item is expanded; it holds whatever you nest inside it.
The card variant wraps each item in a Card, using cardSize for its padding.
Best practices
Do
- Use an accordion to break a long page into scannable sections, or to tuck secondary detail out of the way until it’s needed.
- Give every item a short, descriptive trigger label so people can tell what’s inside without opening it.
- Use
type="single"when only one section is relevant at a time (e.g. a settings wizard), andtype="multiple"when people may want to compare open sections. - Set a sensible
defaultValueso the most useful section is already open on load, when there is one.
Don’t
- Don’t hide essential or time-sensitive content behind an accordion — anything people always need should stay visible.
- Don’t nest accordions more than one level deep; the disclosure pattern gets hard to follow.
- Don’t put a single item in an accordion when a plain heading and body would read more directly.
- Don’t place primary page actions inside a collapsed panel where people won’t find them.
Content
- Write trigger labels as short noun phrases that name the section — “Billing details”, “Advanced options”.
- Use sentence case; capitalize only the first word, along with any proper nouns or product names.
- Keep labels parallel across items so the set reads as a consistent list.
- Use the
suffixfor a brief status or count (“3 items”), not for a second sentence.
Accessibility
Accordion is built on Radix UI’s accordion, so it follows the WAI-ARIA disclosure pattern: each trigger is a native <button> inside a heading element, wired to its content region with aria-expanded and aria-controls.
Keyboard
- Tab moves focus to a trigger; Enter or Space toggles that item open or closed.
- With focus on a trigger, Arrow Down / Arrow Up move between triggers, and Home / End jump to the first and last.
- A
disableditem (or a disabledRoot) is skipped and can’t be toggled.
Labeling
- Each trigger sits inside a Radix heading element, so assistive tech announces the items as a list of headings you can jump between.
- Give each item a clear text label in the
Triggerso its button has a meaningful name (see the limitation below about label placement). - The chevron indicator is decorative (
pointer-events-none) and adds no announced text; open/closed state is conveyed byaria-expanded.
Focus & states
- Triggers show the standard keyboard focus ring (
:focus-visible) via the--cn-focustoken. - Expanded/collapsed state is exposed through
data-stateandaria-expanded, so it’s available to both styling and assistive tech.
Known limitations
- The label sits outside the trigger button. The trigger is rendered as an empty button stretched across the whole header (
position: absolute; inset: 0), while the visible label, prefix, and suffix sit in a sibling element on top of it. This makes the entire header clickable, but it means the button’s accessible name doesn’t come from the visible text — for anything important, verify the announced name and add an explicitaria-labelon theTriggerif it’s missing. clickable={false}removes the trigger entirely. Turning offclickable(used so interactive children like a Copy Button in the header stay usable) renders no trigger button at all, so that item can’t be expanded or collapsed by keyboard or pointer. Only use it on items you don’t need to toggle from the header.- Motion isn’t reduced automatically. The expand/collapse height animation doesn’t currently respect
prefers-reduced-motion.
Properties
Root
The Accordion.Root component wraps and controls the Accordion.Items. The type prop controls whether the accordion
allows the display of multiple items or a single item. The accordion can be either controlled or uncontrolled.
By default, the accordion is uncontrolled and will manage its own internal state. When the value and onValueChange
props are supplied, the accordion becomes controlled and state is handled externally.
Prop | Required | Default | Type |
|---|---|---|---|
| type | true | single | 'single' | 'multiple' |
| collapsible | false | false | boolean |
| defaultValue | false | string | string[] | |
| disabled | false | false | boolean |
| onValueChange | false | (newValue: string | string[]) => void | |
| value | false | string | string[] | |
| size | false | 'sm' | 'sm' | 'md' |
| indicatorPosition | false | right | 'left' | 'right' |
Item
The Accordion.Item component represents an individual collapsible panel consisting of a trigger and content to display.
The value prop is required and can be thought of as the identifier of the individual Accordion.Item.
Prop | Required | Default | Type |
|---|---|---|---|
| value | true | string | |
| asChild | false | false | boolean |
| disabled | false | false | boolean |
Trigger
The Accordion.Trigger component displays the item with a chevron disclosure icon.
Prop | Required | Default | Type |
|---|---|---|---|
| children | true | ReactNode | |
| prefix | false | ReactNode | |
| suffix | false | ReactNode | |
| clickable | false | true | boolean |
Content
The Accordion.Content component displays the item contents when triggered using the Accordion.Trigger.
Prop | Required | Default | Type |
|---|---|---|---|
| children | true | ReactNode | |
| asChild | false | false | boolean |