Skip to content
Components, switch product

Accordion

<gui-accordion> stacks sections that open and close under their headers. Each item is a native <details> you write, so it works with the keyboard and screen readers out of the box. The element styles the items and keeps one open at a time.

Shipping
Orders ship within two working days, from our warehouse in Leeds.
Returns
Send anything back within 30 days, at no cost.
Warranty
Every notebook has a two-year warranty.
Code of the example above
AccordionBasic.tsx
import { GuiAccordion, GuiAccordionItem } from '@golemui/gui-components/react';
import '@golemui/gui-components/index.css';
export function AccordionBasic() {
return (
<GuiAccordion>
<GuiAccordionItem>
<details open>
<summary id="faq-shipping">Shipping</summary>
<section role="region" aria-labelledby="faq-shipping">
Orders ship within two working days, from our warehouse in Leeds.
</section>
</details>
</GuiAccordionItem>
<GuiAccordionItem>
<details>
<summary id="faq-returns">Returns</summary>
<section role="region" aria-labelledby="faq-returns">
Send anything back within 30 days, at no cost.
</section>
</details>
</GuiAccordionItem>
<GuiAccordionItem>
<details>
<summary id="faq-warranty">Warranty</summary>
<section role="region" aria-labelledby="faq-warranty">
Every notebook has a two-year warranty.
</section>
</details>
</GuiAccordionItem>
</GuiAccordion>
);
}

The element’s entry point is @golemui/gui-components/accordion, which registers <gui-accordion> and its part, <gui-accordion-item>, and nothing else. To install the package and load the stylesheet, see Installation.

The @golemui/gui-components/react components have typed props and an on* prop for each event. React 18 or later.

AccordionExample.tsx
import { GuiAccordion, GuiAccordionItem } from '@golemui/gui-components/react';
import '@golemui/gui-components/index.css';
export function AccordionExample() {
return (
<GuiAccordion
multiple
>
<GuiAccordionItem>
<details open>
<summary id="faq-shipping">Shipping</summary>
<section role="region" aria-labelledby="faq-shipping">…</section>
</details>
</GuiAccordionItem>
</GuiAccordion>
);
}

Put an item inside it for each section, each one a <details> with its <summary> as the header. Put the content in a <section role="region"> named by its summary: the stylesheet draws the content’s box on it, and screen readers can find the open section by its header. The element never adds or removes your content, so your framework renders the items like any other markup.

Properties

Property Type Default Description
multiple boolean false Lets several items be open at once.

Events

Every event bubbles and is composed, so you can listen on any ancestor.

Event Description
gui-toggle An item opened or closed: the event's target is the item, and detail.open its new state. The items an opening closes fire theirs after the opened item's.

By default, opening an item closes the one that was open. With multiple, items open and close on their own.

Account
Notifications
Code of the example above
AccordionMultiple.tsx
import {
GuiAccordion,
GuiAccordionItem,
GuiCheckbox,
GuiTextinput,
} from '@golemui/gui-components/react';
import '@golemui/gui-components/index.css';
export function AccordionMultiple() {
return (
<GuiAccordion multiple>
<GuiAccordionItem>
<details open>
<summary id="settings-account">Account</summary>
<section role="region" aria-labelledby="settings-account">
<GuiTextinput label="Display name" value="Grace Hopper" />
</section>
</details>
</GuiAccordionItem>
<GuiAccordionItem>
<details open>
<summary id="settings-notifications">Notifications</summary>
<section role="region" aria-labelledby="settings-notifications">
<GuiCheckbox label="Email me when someone mentions me" value />
</section>
</details>
</GuiAccordionItem>
</GuiAccordion>
);
}

Each item fires gui-toggle when the user opens or closes it, with the new state in detail.open. The event bubbles, so listen on the accordion and read the item from event.target. When an opening closes another item, the closed item fires its gui-toggle after the opened one’s. Open each question below in turn.

How do seats work?
You pay for each member of your workspace. Guests are free.
Can I cancel at any time?
Yes. Your plan stays active until the end of the billing period.

Code of the example above
AccordionEvents.tsx
import { useState } from 'react';
import { GuiAccordion, GuiAccordionItem } from '@golemui/gui-components/react';
import type { GuiToggleEventDetail } from '@golemui/gui-components';
import '@golemui/gui-components/index.css';
export function AccordionEvents() {
const [log, setLog] = useState('');
const onToggle =
(question: string) => (event: CustomEvent<GuiToggleEventDetail>) =>
setLog(
(log) =>
`${log}${event.detail.open ? 'Opened' : 'Closed'} "${question}". `,
);
return (
<>
<GuiAccordion id="plan-faq">
<GuiAccordionItem onGuiToggle={onToggle('How do seats work?')}>
<details open>
<summary id="plan-faq-seats">How do seats work?</summary>
<section role="region" aria-labelledby="plan-faq-seats">
You pay for each member of your workspace. Guests are free.
</section>
</details>
</GuiAccordionItem>
<GuiAccordionItem onGuiToggle={onToggle('Can I cancel at any time?')}>
<details>
<summary id="plan-faq-cancel">Can I cancel at any time?</summary>
<section role="region" aria-labelledby="plan-faq-cancel">
Yes. Your plan stays active until the end of the billing period.
</section>
</details>
</GuiAccordionItem>
</GuiAccordion>
<p id="plan-faq-log" role="status">
{log}
</p>
</>
);
}

Set open on an item to open or close it yourself. Setting it fires no gui-toggle, and it doesn’t close the other items, even without multiple.

Version 2.1
Dark mode and a new calendar.
Version 2.0
Shared projects and comments.
Code of the example above
AccordionControlled.tsx
import { useState } from 'react';
import {
GuiAccordion,
GuiAccordionItem,
GuiButton,
} from '@golemui/gui-components/react';
import '@golemui/gui-components/index.css';
const releases = [
{
id: 'release-2-1',
title: 'Version 2.1',
notes: 'Dark mode and a new calendar.',
},
{
id: 'release-2-0',
title: 'Version 2.0',
notes: 'Shared projects and comments.',
},
];
export function AccordionControlled() {
const [open, setOpen] = useState(releases.map(() => false));
return (
<>
<GuiButton
id="release-expand"
variant="outlined"
size="sm"
label="Expand all"
onClick={() => setOpen(releases.map(() => true))}
/>
<GuiAccordion id="release-history" multiple>
{releases.map((release, index) => (
<GuiAccordionItem
key={release.id}
open={open[index]}
onGuiToggle={(event) =>
setOpen((current) =>
current.map((value, i) =>
i === index ? event.detail.open : value,
),
)
}
>
<details>
<summary id={release.id}>{release.title}</summary>
<section role="region" aria-labelledby={release.id}>
{release.notes}
</section>
</details>
</GuiAccordionItem>
))}
</GuiAccordion>
</>
);
}

<gui-accordion-item> is an item of an accordion: it holds one <details>, and keeps its open property in step with the details, both ways. Without open set, it takes the details’ own state, so <details open> is all you need to start an item open. An item outside an accordion works on its own, with the same look, as a single section that opens and closes.

Properties

Property Type Default Description
open boolean – Whether the item is open. Without a value it takes the details' own open. Setting it fires no gui-toggle.

Events

Every event bubbles and is composed, so you can listen on any ancestor.

Event Description
gui-toggle The user opened or closed the item, or its accordion closed it because the user opened another. detail.open is the new state.

<gui-accordion> and its items render nothing of their own: the anatomy is your markup. The item reflects its state in the open attribute, and the stylesheet draws the arrow at the end of each summary with CSS:

gui-accordion
<gui-accordion>
<gui-accordion-item open>
<details open>
<summary id="faq-shipping">Shipping</summary>
<section role="region" aria-labelledby="faq-shipping">
Orders ship within two working days, from our warehouse in Leeds.
</section>
</details>
</gui-accordion-item>
<gui-accordion-item>
<details>
<summary id="faq-returns">Returns</summary>
<section role="region" aria-labelledby="faq-returns">…</section>
</details>
</gui-accordion-item>
<!-- …one item per section -->
</gui-accordion>

The parts have no classes. Style them by tag:

SelectorPart
gui-accordionThe stack of items.
gui-accordion[multiple]An accordion that lets several items open.
gui-accordion-itemAn item.
gui-accordion-item[open]An open item.
gui-accordion-item > details > summaryThe header.
gui-accordion-item > details > summary::afterThe arrow. It turns over while the item is open.
gui-accordion-item section[role='region']The content’s box.

The accordion reads these design tokens. Set them on :root to change every accordion, or on any element to change the accordions inside it:

TokenWhat it styles
--gui-bg-defaultThe background of the headers and the content.
--gui-border-defaultThe borders of the headers and the content.
--gui-text-defaultThe text, the arrow, and the border and ring of the focused header.
--gui-space-3The padding of the headers and the content, and the space around the accordion.
--gui-font-familyThe font.
--gui-font-smThe text size.
--gui-caret-shape, --gui-caret-imageThe shape of the arrow. See Carets.
styles.css
/* Left-aligned, roomier headers. */
gui-accordion-item > details > summary {
text-align: start;
}
.faq {
--gui-space-3: 1rem;
}

To restyle beyond the tokens, see Overriding styles; for dark mode and the Clay theme, see Themes and dark mode.

  • Native disclosure. Each header is a native <summary>: browsers expose it as a button and announce whether its section is expanded. The element adds no ARIA of its own.
  • Regions. A <section role="region"> named by its summary with aria-labelledby lets screen reader users jump to the open section by its header. For a long list of items, leave out the role, so the page doesn’t fill with landmarks.
  • Arrow. The arrow is drawn with CSS, so screen readers don’t read it, and it keeps the text color in forced-colors modes.
  • Before the upgrade. The details work on their own before the element loads, such as in a page rendered on the server: only closing the other items waits for the element. See Server rendering.
KeyAction
TabMoves focus from header to header, and into the open sections.
EnterOpens or closes the focused item.
SpaceOpens or closes the focused item.

What every element shares is in Accessibility.