Accordion
Accordions built on `<details>` / `<summary>`, with an optional managed structure for measured height animation.
<pe-accordion class="Accordion">
<details class="Item" data-accordion-item data-accordion-value="shipping" name="faq">
<summary class="Trigger">How long does shipping take?</summary>
<p class="Content">Standard shipping arrives within three to five business days.</p>
</details>
<details class="Item" data-accordion-item data-accordion-value="returns" name="faq">
<summary class="Trigger">Can I return an item?</summary>
<p class="Content">Unused items can be returned within 30 days.</p>
</details>
</pe-accordion>.Accordion {
display: flex;
max-width: 20rem;
width: 100%;
flex-direction: column;
border: 1px solid oklch(14.5% 0 0);
color: oklch(14.5% 0 0);
@media (prefers-color-scheme: dark) {
border-color: white;
color: white;
}
}
.Accordion .Item + .Item {
border-top: 1px solid oklch(14.5% 0 0);
@media (prefers-color-scheme: dark) {
border-top-color: white;
}
}
.Accordion .Trigger {
padding: 0.5rem 0.75rem;
cursor: pointer;
}
.Accordion .Content {
margin: 0;
padding: 0 0.75rem 0.75rem;
}
Accordion groups multiple related disclosure items together. The standard structure uses <details> / <summary>. Give each <details> the same name for single-open behavior, or omit it to allow multiple items. Content in that structure can be animated with ::details-content where supported.
Managed alternative for required height animation
Section titled “Managed alternative for required height animation”Use managed items only when measured height animation or custom trigger/panel structure is required:
<pe-accordion class="Accordion" data-accordion-single data-accordion-hidden-until-found> <div class="Item" data-accordion-item data-accordion-value="shipping"> <h3 class="Header"> <button type="button" class="Trigger" data-accordion-trigger>Shipping</button> </h3> <div class="Panel" data-accordion-panel> <div class="Content">Shipping information.</div> </div> </div></pe-accordion>Do not mix native and managed items in one host. Add data-accordion-hidden-until-found when closed content must remain available to browser find-in-page.
<pe-accordion class="Accordion" data-accordion-single data-accordion-hidden-until-found>
<div class="Item" data-accordion-item data-accordion-value="shipping">
<h3 class="Header">
<button type="button" class="Trigger" data-accordion-trigger>Shipping</button>
</h3>
<div class="Panel" data-accordion-panel>
<div class="Content">Shipping information.</div>
</div>
</div>
</pe-accordion>.Accordion {
display: flex;
max-width: 20rem;
width: 100%;
flex-direction: column;
border: 1px solid oklch(14.5% 0 0);
color: oklch(14.5% 0 0);
@media (prefers-color-scheme: dark) {
border-color: white;
color: white;
}
}
.Accordion .Item + .Item {
border-top: 1px solid oklch(14.5% 0 0);
@media (prefers-color-scheme: dark) {
border-top-color: white;
}
}
.Accordion .Header {
margin: 0;
}
.Accordion .Trigger {
display: flex;
width: 100%;
align-items: center;
justify-content: space-between;
gap: 1rem;
padding: 0.5rem 0.75rem;
margin: 0;
border: none;
border-radius: 0;
background-color: transparent;
color: inherit;
font-family: inherit;
font-size: 0.875rem;
font-weight: 400;
line-height: 1.25rem;
text-align: left;
cursor: pointer;
@media (hover: hover) {
&:hover:not([data-disabled]) {
background-color: oklch(97% 0 0);
@media (prefers-color-scheme: dark) {
background-color: oklch(26.9% 0 0);
}
}
}
&::after {
content: "+";
}
&[data-state="open"]::after {
content: "−";
}
}
.Accordion .Panel {
height: var(--accordion-panel-height);
overflow: hidden;
font-size: 0.875rem;
line-height: 1.25rem;
transition: height 150ms ease-out;
&[data-starting-style],
&[data-ending-style] {
height: 0;
}
}
.Accordion .Content {
padding: 0 0.75rem 0.75rem;
}
@media (prefers-reduced-motion: reduce) {
.Accordion .Panel {
transition-duration: 0s;
}
}
Shared anatomy
Section titled “Shared anatomy”| Part | Native selector | Managed selector |
|---|---|---|
| Host | <pe-accordion> |
<pe-accordion> |
| Item | details[data-accordion-item] |
div[data-accordion-item] |
| Trigger | Direct <summary> child |
<button data-accordion-trigger> |
| Panel | Native details content | [data-accordion-panel] |
| Open state | open on item |
data-accordion-open on item |
| Value | data-accordion-value on item |
data-accordion-value on item |
Managed accordions allow multiple open items by default; add data-accordion-single for one at a time. All parts expose data-state="open|closed".
API Reference
Section titled “API Reference”Managed attributes
Section titled “Managed attributes”| Name | Description |
|---|---|
data-accordion-open | On a managed div item: reflects its open state and can be changed externally. |
data-accordion-single | On a managed host: allows only one managed item to remain open. |
data-accordion-hidden-until-found | On a managed host or panel: uses hidden="until-found" while closed and opens synchronously on beforematch. |
Host methods
Section titled “Host methods”| Name | Type | Description |
|---|---|---|
open(valueOrItem) | void | Opens an item by data-accordion-value or details/div element reference. |
close(valueOrItem) | void | Closes an item by value or element reference. |
toggle(valueOrItem) | void | Toggles an item by value or element reference. |
The host’s value property gets or sets an array of open data-accordion-value strings. Its disabled boolean property reflects root data-accordion-disabled. Native details[name] and managed data-accordion-single determine whether multiple requested values can remain open.
Host properties
Section titled “Host properties”| Name | Type | Description |
|---|---|---|
value | string[] | Gets or sets open item values from `data-accordion-value`. |
disabled | boolean | Gets or sets root disabled state via `data-accordion-disabled`. |
Item attributes
Section titled “Item attributes”| Name | Description |
|---|---|
data-accordion-item | Marks a native `<details>` or managed `<div>` item. |
data-accordion-value | Stable string identifier for programmatic open/close/toggle. |
data-accordion-open | On managed items: marks the initial or current open state. |
data-accordion-disabled | On the host or an item: ignores user interaction. |
data-state | On items, triggers, and panels: "open" or "closed". |
data-index | Zero-based item index within the host. |
data-disabled | On the host when root disabled: present as an empty attribute. |
Events
Section titled “Events”| Name | Description |
|---|---|
pe-accordion:open | Bubbles and is composed. Fired when an item opens. |
pe-accordion:close | Bubbles and is composed. Fired when an item closes. |
Event detail fields: item, trigger, summary (alias of trigger), panel (null on native items), optional value, index, and reason (trigger, programmatic, attribute, beforematch, or native). The native toggle event remains available on each <details>.
Managed animation hooks
Section titled “Managed animation hooks”An open managed panel exposes --accordion-panel-height and --accordion-panel-width while enter and exit transitions run.
.Panel { height: var(--accordion-panel-height); overflow: hidden; transition: height 160ms ease;}
.Panel[data-starting-style],.Panel[data-ending-style] { height: 0;}Closed managed panels are hidden after finite exit animations finish. beforematch skips the opening animation so the browser can scroll immediately.