<bmx-accordion>
A set of bmx-accordion-items that know about each other: one open at a
time, or several; collapsible or always showing something; arrow keys moving
between the headings.
5 properties · 1 events · 4 methods · 1 parts
Example
Two to three working days to a UK address, and next-day if the order is placed before noon.
Anything unworn, within thirty days, in the packaging it arrived in.
To the EU and to the United States. Duties are shown at checkout rather than on arrival.
Not available yet.
Several at once, in the separated appearance:
Card, PayPal, or an invoice on an approved trade account.
Cardboard and paper tape. No plastic anywhere in the chain.
And one on its own, which is a disclosure rather than an accordion:
Show markup
<div class="row">
<bmx-accordion id="ex-acc" style="max-inline-size: 40rem">
<bmx-accordion-item heading="How long does delivery take?">
<p style="margin: 0">Two to three working days to a UK address, and next-day if the order is placed before noon.</p>
</bmx-accordion-item>
<bmx-accordion-item heading="Can I return something?" badge="30 days">
<p style="margin: 0">Anything unworn, within thirty days, in the packaging it arrived in.</p>
</bmx-accordion-item>
<bmx-accordion-item heading="Do you ship outside the UK?">
<p style="margin: 0">To the EU and to the United States. Duties are shown at checkout rather than on arrival.</p>
</bmx-accordion-item>
<bmx-accordion-item heading="Trade accounts" disabled>
<p style="margin: 0">Not available yet.</p>
</bmx-accordion-item>
</bmx-accordion>
</div>
<div class="row">
<span class="note" id="ex-acc-out">One section at a time. Arrow keys move between the headings; Tab moves through them.</span>
</div>
<div class="row">
<p style="margin: 0">Several at once, in the separated appearance:</p>
</div>
<div class="row">
<bmx-accordion multiple appearance="separated" style="max-inline-size: 40rem">
<bmx-accordion-item heading="Payment" open>
<p style="margin: 0">Card, PayPal, or an invoice on an approved trade account.</p>
</bmx-accordion-item>
<bmx-accordion-item heading="Packaging" open>
<p style="margin: 0">Cardboard and paper tape. No plastic anywhere in the chain.</p>
</bmx-accordion-item>
</bmx-accordion>
</div>
<div class="row">
<p style="margin: 0">And one on its own, which is a disclosure rather than an accordion:</p>
</div>
<div class="row">
<bmx-accordion-item heading="Advanced options" style="max-inline-size: 40rem; border: 1px solid var(--bmx-border); border-radius: var(--bmx-radius-lg)">
<bmx-switch label="Send anonymous usage statistics"></bmx-switch>
</bmx-accordion-item>
</div>
<script type="module">
await customElements.whenDefined('bmx-accordion');
const accordion = document.getElementById('ex-acc');
const out = document.getElementById('ex-acc-out');
// One event per press, carrying the whole open set - not one from the item
// that opened and another from the one that closed.
accordion.addEventListener('bmxChange', event => {
const { value, open, expanded } = event.detail;
out.textContent = `"${value}" ${open ? 'opened' : 'closed'} — showing: ${expanded.length > 0 ? expanded.join(', ') : 'nothing'}`;
});
</script>
WHAT IT OWNS, AND WHAT THE ITEMS OWN
Each item draws its own heading and its own panel, and keeps both ends of its
ARIA references inside its own shadow root - so this component never reaches
across a boundary to wire anything, which is the problem that shaped
bmx-tabs. What is left for the group is the part an item cannot know: that
opening one may have to close another.
So an item reports its press, this component decides what the open set
becomes, and it writes open back onto every item. That is also why the
item's own bmxToggle is stopped at this boundary: the items are
light-DOM children, so their events pass through this element on the way to
whoever is listening, and a consumer would otherwise see a toggle for the
item they pressed, a second for the one that closed, and no single event
saying what the accordion now shows. One bmxChange says all of it.
KEYBOARD
Every heading is a Tab stop, which is the part libraries most often get wrong: an accordion is a list of ordinary buttons, and a user who has reached one expects Tab to take them to the next thing rather than out of the accordion entirely. Up, Down, Home and End are a shortcut on top of that, not a replacement for it.
Properties
| Property | Attribute | Type | Default | Description |
|---|---|---|---|---|
appearance |
appearance |
BmxAccordionAppearance |
'bordered' |
How the accordion is drawn. |
collapsible |
collapsible |
boolean |
true |
Whether the open panel may be closed again, leaving none. Only asked when one panel may be open. False is the right shape for a wizard, where something must always be showing; true is the right shape for a list of questions, where closing the answer you have read is the obvious thing to try. |
expanded |
property only | string[] |
[] |
Which panels are open, by value. Mutable, so pressing a heading updates it. Seeded from whichever items were written with open when the accordion first reads its children, and made legal on the way in: values naming nothing are dropped, and several open items in a single-open accordion keep the first in document order. Accepts a comma-separated or JSON string as well, because an attribute is the only channel some templates have. See src/core/markup.ts. |
headingLevel |
heading-level |
number |
3 |
The aria-level every item's heading is given. |
multiple |
multiple |
boolean |
false |
Whether more than one panel may be open. The default is one at a time, which is what makes it an accordion rather than a list of disclosures - and what a long page of sections usually wants, because it keeps the headings within reach of each other. |
Events
| Event | Detail | Description |
|---|---|---|
bmxChange |
BmxAccordionChangeDetail |
Fired when a panel opens or closes. |
Methods
| Method | Signature | Description |
|---|---|---|
closeAll |
closeAll() => Promise<void> |
Close every panel. Ignored when one must always be showing. |
closePanel |
closePanel(value: string) => Promise<void> |
Close a panel by value. |
openAll |
openAll() => Promise<void> |
Open every panel. Ignored unless several may be open. |
openPanel |
openPanel(value: string) => Promise<void> |
Open a panel by value, closing another if only one may be open. |
Slots
| Slot | Description |
|---|---|
(default) |
bmx-accordion-item elements. |
CSS shadow parts
| Part | Description |
|---|---|
base |
The container. |
CSS custom properties
| Property | Description |
|---|---|
--bmx-accordion-background |
Background behind the sections. |
--bmx-accordion-border-color |
Colour of the rules between sections. |
--bmx-accordion-gap |
Space between sections in the separated appearance. |
--bmx-accordion-radius |
Corner radius of the container, or of each section when separated. |