gray
radius
Navigation

Accordion

Collapsible sections. Each trigger sits inside a heading element, which is what lets a screen reader user jump between sections rather than tab through every one.

One at a time

Use type="single" when only one section should be open.

A shadow works by darkening what is behind it, so how opaque it has to be depends on how dark that already is. Eight per cent black moves a white ground by 20/255 and a near-black one by 2, so the geometry can be shared but the ink cannot.
<Accordion.Root type="single" collapsible defaultValue="a">
<Accordion.Item value="a">
<Accordion.Trigger>Why…?</Accordion.Trigger>
<Accordion.Content></Accordion.Content>
</Accordion.Item>
</Accordion.Root>

Any number open

Use type="multiple" to let sections open independently.

A shadow works by darkening what is behind it, so how opaque it has to be depends on how dark that already is. Eight per cent black moves a white ground by 20/255 and a near-black one by 2, so the geometry can be shared but the ink cannot.
<Accordion.Root type="multiple" defaultValue={['a', 'b']}></Accordion.Root>
The height animates without anyone knowing the height. height: auto cannot be animated, and measuring the panel in JavaScript breaks the moment its contents reflow. The base layer publishes the measurement as a custom property, which is what the keyframes read.

Parts

Import it as a namespace, and the parts work on either side of the server boundary.

import * as Accordion from '@minuk-hwang-design-system/components-react/accordion';
PartWhat it is
Accordion.Roottype="single" or "multiple". Owns which are open.
Accordion.ItemOne section.
Accordion.TriggerThe heading button. Chevron included, rotated from data-state.
Accordion.ContentThe panel. Animates its own height.

Props

PropTypeDefaultNotes
type'single' | 'multiple'Required, on Root.
collapsiblebooleanfalseSingle only. Allows closing the open one.
defaultValuestring | string[]Matches the type.
onValueChange(value) => void