On this page
Accordion
Vertically collapsing accordions using <details> and
<summary> — no Collapse plugin.
| What runs it | JavaScript | |
|---|---|---|
| Bootstrap 5.3 | Collapse plugin. Accordion is that plugin; there is no accordion.js. | 5.6 KB gzip |
| This repo | details and summary, with name for an exclusive group | 0 KB |
| Optional | <pe-focus-group> · src/behaviors/focus-group.js . Arrow, Home, and End keys across the summaries. Tab works without it. |
|---|---|
| Avoid |
|
How it works
Each item is a details.accordion-item with a
summary.accordion-button. The same name on every
item makes the group exclusive (replaces data-bs-parent). Add
open to start expanded (replaces .show /
omitting .collapsed).
The browser fires a toggle event on <details>
when the open state changes.
The panel is ::details-content, and its height transitions
there instead of from JavaScript. Animating block-size from
0 to auto needs
interpolate-size: allow-keywords, which only Chromium has.
Firefox and Safari open and close the item without the transition.
Closed panels stay in the document, so Find in page can match them
(try quetzalcoatlus in the first example). Do not
put hidden or display: none on
.accordion-body. For a custom hidden region that is
not a <details> panel, use
hidden="until-found" and keep padding on an inner
wrapper. Bootstrap’s reboot would otherwise force
display: none and break that attribute.
Example
Click the accordions below to expand/collapse the accordion content.
To render an accordion that’s expanded by default, add the
open attribute on the <details> element.
Accordion Item #1
.accordion-body, though the transition does limit overflow.
Accordion Item #2
Accordion Item #3
.accordion-body, though the transition does limit overflow.
<div class="accordion">
<details class="accordion-item" name="accordionExample" open>
<summary class="accordion-button">Accordion Item #1</summary>
<div class="accordion-body">
<strong>This is the first item’s accordion body.</strong> It is shown by default. You can modify any of this with custom CSS or overriding our default variables. It’s also worth noting that just about any HTML can go within the <code>.accordion-body</code>, though the transition does limit overflow.
</div>
</details>
<details class="accordion-item" name="accordionExample">
<summary class="accordion-button">Accordion Item #2</summary>
<div class="accordion-body">
<strong>This is the second item’s accordion body.</strong> It is hidden by default. Find in page can still match this panel: the token is <strong>quetzalcoatlus</strong>.
</div>
</details>
<details class="accordion-item" name="accordionExample">
<summary class="accordion-button">Accordion Item #3</summary>
<div class="accordion-body">
<strong>This is the third item’s accordion body.</strong> It is hidden by default. You can modify any of this with custom CSS or overriding our default variables. It’s also worth noting that just about any HTML can go within the <code>.accordion-body</code>, though the transition does limit overflow.
</div>
</details>
</div> Flush
Add .accordion-flush to remove some borders and rounded corners to render accordions edge-to-edge with their parent container.
Accordion Item #1
.accordion-flush class. This is the first item’s accordion body.Accordion Item #2
.accordion-flush class. This is the second item’s accordion body. Let’s imagine this being filled with some actual content.Accordion Item #3
.accordion-flush class. This is the third item’s accordion body. Nothing more exciting happening here in terms of content, but just filling up the space to make it look, at least at first glance, a bit more representative of how this would look in a real-world application.<div class="accordion accordion-flush">
<details class="accordion-item" name="accordionFlushExample">
<summary class="accordion-button">Accordion Item #1</summary>
<div class="accordion-body">Placeholder content for this accordion, which is intended to demonstrate the <code>.accordion-flush</code> class. This is the first item’s accordion body.</div>
</details>
<details class="accordion-item" name="accordionFlushExample">
<summary class="accordion-button">Accordion Item #2</summary>
<div class="accordion-body">Placeholder content for this accordion, which is intended to demonstrate the <code>.accordion-flush</code> class. This is the second item’s accordion body. Let’s imagine this being filled with some actual content.</div>
</details>
<details class="accordion-item" name="accordionFlushExample">
<summary class="accordion-button">Accordion Item #3</summary>
<div class="accordion-body">Placeholder content for this accordion, which is intended to demonstrate the <code>.accordion-flush</code> class. This is the third item’s accordion body. Nothing more exciting happening here in terms of content, but just filling up the space to make it look, at least at first glance, a bit more representative of how this would look in a real-world application.</div>
</details>
</div> Always open
Omit the name attribute on each details element to make accordion items stay open when another item is opened.
Accordion Item #1
.accordion-body, though the transition does limit overflow.
Accordion Item #2
.accordion-body, though the transition does limit overflow.
Accordion Item #3
.accordion-body, though the transition does limit overflow.
<div class="accordion">
<details class="accordion-item" open>
<summary class="accordion-button">Accordion Item #1</summary>
<div class="accordion-body">
<strong>This is the first item’s accordion body.</strong> It is shown by default. You can modify any of this with custom CSS or overriding our default variables. It’s also worth noting that just about any HTML can go within the <code>.accordion-body</code>, though the transition does limit overflow.
</div>
</details>
<details class="accordion-item">
<summary class="accordion-button">Accordion Item #2</summary>
<div class="accordion-body">
<strong>This is the second item’s accordion body.</strong> It is hidden by default. You can modify any of this with custom CSS or overriding our default variables. It’s also worth noting that just about any HTML can go within the <code>.accordion-body</code>, though the transition does limit overflow.
</div>
</details>
<details class="accordion-item">
<summary class="accordion-button">Accordion Item #3</summary>
<div class="accordion-body">
<strong>This is the third item’s accordion body.</strong> It is hidden by default. You can modify any of this with custom CSS or overriding our default variables. It’s also worth noting that just about any HTML can go within the <code>.accordion-body</code>, though the transition does limit overflow.
</div>
</details>
</div> Find in page
hidden="until-found" hides a custom region until Find in page matches it. Accordion items themselves stay on details — do not put hidden on .accordion-body.
Search this page for thylacine.
<p>Search this page for <strong>thylacine</strong>.</p>
<div hidden="until-found">
<div class="accordion-body border rounded">
Find in page revealed this region. The token is <strong>thylacine</strong>.
</div>
</div> Accessibility
<details> / <summary> is a disclosure. The browser exposes expanded and collapsed; do not add aria-expanded on the summary.
Do not put a heading inside <summary>. Screen-reader heading lists skip that text.
Tab reaches every summary without script. Optional <pe-focus-group> adds Arrow, Home, and End. Enter and Space still open the item.
Closed panels stay in the document. Find in page can match them. Do not add display: none on the body.
Browser support
| No script | Polyfill | No fallback |
|---|---|---|
details / summary. Exclusive name: Chrome 120+, Firefox 130+, Safari 17.2+. | None | The height transition needs interpolate-size: allow-keywords to animate block-size from 0 to auto. That is Chromium-only (Chrome 129+), so Firefox and Safari snap the panel open. ::details-content itself is Chrome 131+, Firefox 143+, Safari 18.4+, and only styles the panel. |
Arrow keys
Tab still moves through each <summary> when no extra
script is loaded. Arrow keys, Home, and End are optional. Wrap the
accordion in <pe-focus-group> with
items="summary" and
orientation="vertical", and load
src/behaviors/focus-group.js. Without that module the
element is only a wrapper: the items still open, and Tab still reaches
every summary.
Up focuses the previous summary. Down focuses the next. Home and End
jump to the ends. Focusing a summary does not open it; Enter and Space
still do. The shared name keeps the group exclusive.
Keys inside the panel stay there: the text field, the link, and the
menu without its own focus group. The menu wrapped in a nested
<pe-focus-group> handles its own arrow keys.
Focus group
The open panel has a text field, a link, and two menus. Only the menu wrapped in pe-focus-group moves with arrow keys. The accordion group moves between summaries.
Accordion Item #1
Accordion Item #2
name still keeps only one item open.
Accordion Item #3
<script type="module" src="./node_modules/bootstrap-html/src/behaviors/focus-group.js"></script>
<pe-focus-group items="summary" orientation="vertical">
<div class="accordion">
<details class="accordion-item" name="accordionFocusGroup" open>
<summary class="accordion-button">Accordion Item #1</summary>
<div class="accordion-body">
<div class="mb-3">
<label class="form-label" for="accordionFocusNote">Note</label>
<input class="form-control" id="accordionFocusNote" type="text" value="Arrow keys stay in this field">
</div>
<p><a href="#focus-group">A link in the panel</a>. Arrow keys here do not move between summaries.</p>
<div class="d-flex flex-wrap gap-2">
<div class="dropdown">
<button class="btn btn-secondary dropdown-toggle" type="button" command="toggle-popover" commandfor="accordionMenuPlain">
Menu without focus group
</button>
<ul class="dropdown-menu" popover="auto" id="accordionMenuPlain">
<li><a class="dropdown-item" href="#focus-group">Action</a></li>
<li><a class="dropdown-item" href="#focus-group">Another action</a></li>
</ul>
</div>
<pe-focus-group items=".dropdown-item:not(.disabled, [aria-disabled='true'])">
<div class="dropdown">
<button class="btn btn-secondary dropdown-toggle" type="button" command="toggle-popover" commandfor="accordionMenuGrouped">
Menu with focus group
</button>
<ul class="dropdown-menu" popover="auto" id="accordionMenuGrouped">
<li><a class="dropdown-item" href="#focus-group">Action</a></li>
<li><a class="dropdown-item" href="#focus-group">Another action</a></li>
</ul>
</div>
</pe-focus-group>
</div>
</div>
</details>
<details class="accordion-item" name="accordionFocusGroup">
<summary class="accordion-button">Accordion Item #2</summary>
<div class="accordion-body">
<strong>This is the second item’s accordion body.</strong> The shared <code>name</code> still keeps only one item open.
</div>
</details>
<details class="accordion-item" name="accordionFocusGroup">
<summary class="accordion-button">Accordion Item #3</summary>
<div class="accordion-body">
<strong>This is the third item’s accordion body.</strong> Home and End jump to the first and last summary.
</div>
</details>
</div>
</pe-focus-group>