Accordion
Toggle the visibility of content sections using native details/summary elements.
Compose AccordionDetails and AccordionSummary on the native <details> and <summary> elements.
Exterior
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse malesuada lacus ex, sit amet blandit leo lobortis eget.
Charging
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse malesuada lacus ex, sit amet blandit leo lobortis eget.
Climate
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse malesuada lacus ex, sit amet blandit leo lobortis eget.
Show code
import { AccordionDetails, AccordionSummary,} from '@volvo-cars/react-accordion';
export function BasicAccordion() { return ( <> {['Exterior', 'Charging', 'Climate'].map((item) => ( <AccordionDetails key={item}> <AccordionSummary>{item}</AccordionSummary> <p className="pb-24 text-secondary"> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse malesuada lacus ex, sit amet blandit leo lobortis eget. </p> </AccordionDetails> ))} </> );}Composition
Section titled “Composition”AccordionDetails wraps each section. Its first child should be an AccordionSummary: this becomes the toggle. Everything else is the collapsible content.
AccordionSummary renders a <summary> element with an implicit button role. It must not contain interactive children (links, buttons, checkboxes).
Icon placement
Section titled “Icon placement”Control where the chevron icon appears with iconPlacement. Use iconAlignment to adjust vertical alignment when labels wrap.
Icon at end (default)
The chevron appears after the label.
Icon at start
The chevron appears before the label.
Show code
import { AccordionDetails, AccordionSummary,} from '@volvo-cars/react-accordion';
export function IconPlacement() { return ( <> <AccordionDetails> <AccordionSummary iconPlacement="end"> Icon at end (default) </AccordionSummary> <p className="pb-24 text-secondary"> The chevron appears after the label. </p> </AccordionDetails> <AccordionDetails> <AccordionSummary iconPlacement="start">Icon at start</AccordionSummary> <p className="pb-24 text-secondary"> The chevron appears before the label. </p> </AccordionDetails> </> );}| Prop | Values | Default |
|---|---|---|
iconPlacement | 'start', 'end' | 'end' |
iconAlignment | 'top', 'center' | 'top' |
Controlled and uncontrolled
Section titled “Controlled and uncontrolled”Uncontrolled (default)
Section titled “Uncontrolled (default)”By default, the browser manages toggle state via native <details>. Use defaultOpen to start a section expanded:
<AccordionDetails defaultOpen> <AccordionSummary>Open by default</AccordionSummary> <p>Content</p></AccordionDetails>Controlled
Section titled “Controlled”Use open and onToggle for full control. Useful for wizard-style flows or syncing with external state. See the controlled scenario in Examples.
Exclusive mode
Section titled “Exclusive mode”Wrap items in AccordionController to allow only one open at a time. Set exclusive="until-lg" to make it exclusive on mobile/tablet but allow multiple on desktop.
Stepped Accordion
Section titled “Stepped Accordion”The Stepped Accordion organizes content into collapsible sections displayed vertically with a connecting left border. Only one step can be open at a time, guiding users through sequential information.
The component uses three parts:
SteppedAccordion– Container that provides the vertical layout and left borderSteppedAccordionDetails– Wraps each step, controls open state via theopenpropSteppedAccordionSummary– The clickable header for each step
Controlled state
Section titled “Controlled state”The stepped accordion is always controlled. Track the current step with state and update it via the onToggle callback. An open step cannot be closed directly. Opening another step closes the current one.
const [currentStep, setCurrentStep] = useState(0);
<SteppedAccordionDetails open={currentStep === 0} onToggle={() => setCurrentStep(0)}>Rich content
Section titled “Rich content”Add content inside each step after the summary. Use the standard padding classes for consistent spacing.
Accessibility
Section titled “Accessibility”Key consumer responsibilities from the WCAG audit:
- Accessible name: provide clear accordion header text. If using icons or non-text indicators, add
aria-labeloraria-labelledby. - No interactive children in summary:
<summary>has an implicitbuttonrole. Nesting links, buttons, or other interactive elements insideAccordionSummaryis invalid. - Expand/collapse via keyboard: handled natively (Space / Enter on
<summary>). Ensure users can tab in and out without getting trapped. - Consistent labeling: use the same naming pattern for accordion headers across your interface.
AccordionDetails
Section titled “AccordionDetails”| Prop | Type | Required | Default |
|---|---|---|---|
hidden | boolean | - | - |
id | string | - | - |
title | string | - | - |
dir | string | - | - |
lang | string | - | - |
slot | string | - | - |
translate | "yes" | "no" | - | - |
style | CSSProperties | - | - |
onClick | MouseEventHandler<HTMLDetailsElement> | - | - |
onPointerEnter | PointerEventHandler<HTMLDetailsElement> | - | - |
onPointerMove | PointerEventHandler<HTMLDetailsElement> | - | - |
onPointerDown | PointerEventHandler<HTMLDetailsElement> | - | - |
onPointerUp | PointerEventHandler<HTMLDetailsElement> | - | - |
onPointerLeave | PointerEventHandler<HTMLDetailsElement> | - | - |
children | ReactNode | ✓ | - |
| The content of the accordion. The first child should be the `AccordionSummary` component. | |||
className | string | - | - |
| Custom class name, merged with existing classes. | |||
onToggle | ((open: boolean) => void) | - | - |
| Called when the accordion is toggled. | |||
onAnimationFinish | ((open: boolean) => void) | - | - |
| Called when the toggle animation is finished. | |||
open | boolean | - | - |
| If set, the accordion will be controlled by the value of this prop. Must be used together with the `onToggle` prop. | |||
defaultOpen | boolean | - | - |
| If true, the accordion will be open by default but can then be toggled freely by the user. | |||
AccordionSummary
Section titled “AccordionSummary”| Prop | Type | Required | Default |
|---|---|---|---|
hidden | boolean | - | - |
id | string | - | - |
title | string | - | - |
dir | string | - | - |
lang | string | - | - |
slot | string | - | - |
translate | "yes" | "no" | - | - |
style | CSSProperties | - | - |
onFocus | FocusEventHandler<HTMLElement> | - | - |
onBlur | FocusEventHandler<HTMLElement> | - | - |
onClick | MouseEventHandler<HTMLElement> | - | - |
onKeyDown | KeyboardEventHandler<HTMLElement> | - | - |
onKeyUp | KeyboardEventHandler<HTMLElement> | - | - |
onPointerEnter | PointerEventHandler<HTMLElement> | - | - |
onPointerMove | PointerEventHandler<HTMLElement> | - | - |
onPointerDown | PointerEventHandler<HTMLElement> | - | - |
onPointerUp | PointerEventHandler<HTMLElement> | - | - |
onPointerLeave | PointerEventHandler<HTMLElement> | - | - |
children | ReactNode | ✓ | - |
| The label of the summary. Should not contain interactive elements. | |||
icon | ReactNode | - | - |
| The icon to display. | |||
iconPlacement | "start" | "end" | - | end |
| The placement of the icon - before or after the label. | |||
iconAlignment | "top" | "center" | - | top |
| The vertical alignment of the icon. | |||
className | string | - | - |
| Custom class name, merged with existing classes. | |||
AccordionController
Section titled “AccordionController”| Prop | Type | Required | Default |
|---|---|---|---|
exclusive | boolean | "until-lg" | - | true |
SteppedAccordion
Section titled “SteppedAccordion”| Prop | Type | Required | Default |
|---|---|---|---|
className | string | - | - |
onClick | MouseEventHandler<HTMLDivElement> | - | - |
onMouseOver | MouseEventHandler<HTMLDivElement> | - | - |
onMouseOut | MouseEventHandler<HTMLDivElement> | - | - |
style | CSSProperties | - | - |
SteppedAccordionDetails
Section titled “SteppedAccordionDetails”| Prop | Type | Required | Default |
|---|---|---|---|
open | boolean | ✓ | - |
| Whether the details are open. | |||
hidden | boolean | - | - |
id | string | - | - |
title | string | - | - |
dir | string | - | - |
lang | string | - | - |
slot | string | - | - |
translate | "yes" | "no" | - | - |
style | CSSProperties | - | - |
onClick | MouseEventHandler<HTMLDetailsElement> | - | - |
onPointerEnter | PointerEventHandler<HTMLDetailsElement> | - | - |
onPointerMove | PointerEventHandler<HTMLDetailsElement> | - | - |
onPointerDown | PointerEventHandler<HTMLDetailsElement> | - | - |
onPointerUp | PointerEventHandler<HTMLDetailsElement> | - | - |
onPointerLeave | PointerEventHandler<HTMLDetailsElement> | - | - |
children | ReactNode | ✓ | - |
| The content of the accordion. The first child should be the `AccordionSummary` component. | |||
className | string | - | - |
| Custom class name, merged with existing classes. | |||
onToggle | ((open: boolean) => void) | - | - |
| Called when the accordion is toggled. | |||
onAnimationFinish | ((open: boolean) => void) | - | - |
| Called when the toggle animation is finished. | |||
SteppedAccordionSummary
Section titled “SteppedAccordionSummary”| Prop | Type | Required | Default |
|---|---|---|---|
hidden | boolean | - | - |
id | string | - | - |
title | string | - | - |
dir | string | - | - |
lang | string | - | - |
slot | string | - | - |
translate | "yes" | "no" | - | - |
style | CSSProperties | - | - |
onFocus | FocusEventHandler<HTMLElement> | - | - |
onBlur | FocusEventHandler<HTMLElement> | - | - |
onClick | MouseEventHandler<HTMLElement> | - | - |
onKeyDown | KeyboardEventHandler<HTMLElement> | - | - |
onKeyUp | KeyboardEventHandler<HTMLElement> | - | - |
onPointerEnter | PointerEventHandler<HTMLElement> | - | - |
onPointerMove | PointerEventHandler<HTMLElement> | - | - |
onPointerDown | PointerEventHandler<HTMLElement> | - | - |
onPointerUp | PointerEventHandler<HTMLElement> | - | - |
onPointerLeave | PointerEventHandler<HTMLElement> | - | - |
children | ReactNode | ✓ | - |
| The label of the summary. Should not contain interactive elements. | |||
icon | ReactNode | - | - |
| The icon to display. | |||
iconPlacement | "start" | "end" | - | - |
| The placement of the icon - before or after the label. | |||
iconAlignment | "top" | "center" | - | - |
| The vertical alignment of the icon. | |||
className | string | - | - |
| Custom class name, merged with existing classes. | |||