Accordion
An accordion shows and hides sections of content. <cinq-accordion> wires up
header buttons, aria-expanded / aria-controls relationships, optional height
transitions, keyboard navigation between headers, and open/close events.
Inspired by @19h47/accordion.
Install
Section titled “Install”pnpm add @agencecinq/accordionImport once:
import "@agencecinq/accordion";Then write the WAI-ARIA markup explicitly — the component adds interactivity only.
HTML is the source of truth. The component will not auto-set
role, auto-migrate attributes, or warn about missing labels. Run an a11y linter (axe, Lighthouse) to catch invalid markup.
<cinq-accordion> <div data-accordion-panel data-accordion-open="true" data-accordion-deselect="true"> <button type="button" data-accordion-header id="scores-header" aria-expanded="true" aria-controls="scores-body" > Ability scores </button>
<div id="scores-body" role="region" aria-labelledby="scores-header"> <div data-accordion-inner>Roll 3d6 for each ability and assign the results.</div> </div> </div>
<div data-accordion-panel data-accordion-open="false"> <button type="button" data-accordion-header id="class-header" aria-expanded="false" aria-controls="class-body" > Class & kit </button>
<div id="class-body" role="region" aria-labelledby="class-header" hidden> <div data-accordion-inner>Check minimum ability scores, then pick a kit for starting gear.</div> </div> </div></cinq-accordion>Required markup
Section titled “Required markup”| Selector / attribute | Required | Role |
|---|---|---|
<cinq-accordion> |
Yes | Accordion container. |
[data-accordion-panel] |
Yes | Panel wrapper for each section. |
[data-accordion-header] ‖ button |
Yes | Focusable trigger inside each panel. |
aria-expanded |
Yes | Current panel state on the trigger. |
aria-controls |
Yes | ID of the controlled region. |
role="region" |
Yes | On each panel body element. |
aria-labelledby |
Yes | On each region, referencing the header id. |
hidden |
Yes | On collapsed regions in their initial state. |
[data-accordion-inner] |
Recommended | Inner wrapper used for height measurement. |
Options
Section titled “Options”| Attribute | Default | Description |
|---|---|---|
data-accordion-multiselectable |
false |
Allow multiple panels open at once. |
data-accordion-hash |
true |
Open the panel whose region id matches the URL hash. |
Per panel:
| Attribute | Default | Description |
|---|---|---|
data-accordion-deselect |
false |
Allow collapsing when already open. |
data-accordion-open |
— | Reflected open state for styling. |
Nested accordion
Section titled “Nested accordion”You can nest a <cinq-accordion> inside a panel body. Each instance only
controls its own direct panels — inner headers are excluded from the outer
keyboard group, and arrow keys stay scoped to the focused accordion.
Set data-accordion-hash="false" on nested hosts to avoid hash collisions.
<cinq-accordion data-accordion-hash="false"> <div data-accordion-panel data-accordion-open="true" data-accordion-deselect="true"> <button type="button" data-accordion-header id="character-header" aria-expanded="true" aria-controls="character-body" > Character creation </button>
<div id="character-body" role="region" aria-labelledby="character-header"> <div data-accordion-inner> <p>Roll ability scores, pick a class, then flesh out your adventurer.</p>
<cinq-accordion data-accordion-hash="false"> <div data-accordion-panel data-accordion-open="true" data-accordion-deselect="true"> <button type="button" data-accordion-header id="scores-header" aria-expanded="true" aria-controls="scores-body" > Ability scores </button>
<div id="scores-body" role="region" aria-labelledby="scores-header"> <div data-accordion-inner>Roll 3d6 for each ability…</div> </div> </div> </cinq-accordion> </div> </div> </div></cinq-accordion>Keyboard support
Section titled “Keyboard support”| Key | Function |
|---|---|
Tab / Shift + Tab |
Move focus through focusable elements. |
Space / Enter |
Toggle the focused header (native button). |
Arrow Up / Arrow Down |
Move focus between headers. |
Arrow Left / Arrow Right |
Move focus between headers. |
Home / End |
Focus first / last header. |
| Method | Description |
|---|---|
closeAll() |
Closes every panel without emitting events. |
destroy() |
Detaches listeners. |
| Property | Description |
|---|---|
panels |
Internal panel controller instances. |
options |
Resolved multiselectable and hash options. |
Events
Section titled “Events”| Event | Cancelable | Detail | Description |
|---|---|---|---|
accordion-panel:open |
Yes | { el, index } |
Fired before a panel opens. |
accordion-panel:close |
Yes | { el, index } |
Fired before a panel closes. |
import { EVENTS } from "@agencecinq/utils";
$accordion.addEventListener(EVENTS.ACCORDION_PANEL_OPEN, (event) => { console.log(event.detail.index);});Examples
Section titled “Examples”Basic accordion
One panel open at a time. Use arrow keys to move between headers,Space / Enter to toggle.
Roll 3d6 for each ability — Strength, Dexterity, Constitution, Intelligence, Wisdom, and Charisma — then assign the totals.
Check minimum ability scores for your chosen class. Elves, dwarves, and halflings adjust scores per the Player's Handbook.
Starting gold and gear come from your class and kit tables — weapons, armour, packs, and a handful of gold pieces.
Last event: —
Multiselectable
Set data-accordion-multiselectable="true" on the host to keep multiple panels open.
Extra weapon slots beyond your class list — long sword, bow, or staff.
Languages, riding, and other NWP slots from your class and Intelligence score.
Nested accordion
Place a second <cinq-accordion> inside a panel body. Each instance only binds its own panels — keyboard navigation stays scoped to the focused accordion. Disable hash sync on nested hosts withdata-accordion-hash="false".
Roll ability scores, pick a class, then flesh out your adventurer.
Roll 3d6 for each ability, assign to Strength, Dexterity, Constitution, Intelligence, Wisdom, and Charisma.
Check minimum ability scores for your class, then choose a kit for extra proficiencies and starting equipment.
Starting gear comes from your class and background tables — weapons, armour, packs, and a handful of gold pieces.
Deselect disabled
Without data-accordion-deselect="true", clicking an open header does not collapse it — typical accordion behaviour.
Try clicking this header again — it stays open until you select another class.
Opens when selected and closes the first panel — typical dual-class flow.