Collapsible
A disclosure primitive with ARIA wiring, disabled state, find-in-page support, and programmatic control.
<pe-collapsible class="Collapsible">
<button type="button" class="Trigger" data-collapsible-trigger>
Project details
</button>
<div class="Panel" data-collapsible-panel>
<p class="Content">Plain Elements provides accessible, unstyled UI primitives.</p>
</div>
</pe-collapsible>.Collapsible {
display: block;
max-width: 20rem;
width: 100%;
border: 1px solid oklch(14.5% 0 0);
color: oklch(14.5% 0 0);
@media (prefers-color-scheme: dark) {
border-color: white;
color: white;
}
}
.Collapsible .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;
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);
}
}
}
}
.Collapsible .Panel {
padding: 0 0.75rem 0.75rem;
font-size: 0.875rem;
line-height: 1.25rem;
&[hidden]:not([hidden="until-found"]) {
display: none;
}
}
.Collapsible .Content {
margin: 0;
}
Use Collapsible for one independent disclosure region and Accordion for a related group. Use a native button trigger. Add data-collapsible-hidden-until-found when closed content must remain available to browser find-in-page.
Anatomy
Section titled “Anatomy”| Part | Selector | Role |
|---|---|---|
| Host | <pe-collapsible> |
Lifecycle and open-state boundary |
| Trigger | [data-collapsible-trigger] |
Toggles the panel and receives aria-expanded / aria-controls |
| Panel | [data-collapsible-panel] |
Receives a generated ID, hidden state, and size CSS variables |
Host, trigger, and panel expose data-state="open|closed". Disabled parts expose data-disabled.
API Reference
Section titled “API Reference”Attributes
Section titled “Attributes”| Name | Description |
|---|---|
data-collapsible-open | Boolean host attribute reflecting the current open state. It can also be changed externally. |
data-collapsible-disabled | On the host or a trigger: ignores user interaction while retaining focusability. |
data-collapsible-hidden-until-found | On the host or panel: uses hidden="until-found" while closed and opens on beforematch. |
Host API
Section titled “Host API”| Name | Type | Description |
|---|---|---|
isOpen | boolean (readonly) | Reads the current open state. Set state with open(), close(), or toggle(). |
disabled | boolean | Gets or sets root disabled state. |
open(trigger?) / close(trigger?) / toggle(trigger?) | void | Changes state programmatically and optionally records the initiating trigger. |
CSS variables and animation hooks
Section titled “CSS variables and animation hooks”The panel exposes --collapsible-panel-height and --collapsible-panel-width while enter and exit transitions run. After the open animation finishes, both return to auto so panel content can grow naturally. The component adds data-starting-style while animating in and data-ending-style while animating out. It waits for finite animations on the panel before applying hidden.
.Collapsible.Animated .Panel { height: var(--collapsible-panel-height); overflow: hidden; transition: height 150ms ease-out;}
.Collapsible.Animated .Panel[hidden]:not([hidden="until-found"]) { display: none;}
.Collapsible.Animated .Panel[data-starting-style],.Collapsible.Animated .Panel[data-ending-style] { height: 0;}Closed panels are hidden after finite exit animations finish. beforematch skips the opening animation so the browser can scroll immediately.
Events
Section titled “Events”| Name | Description |
|---|---|
pe-collapsible:open | Bubbles and is composed. Fired when the panel opens. |
pe-collapsible:close | Bubbles and is composed. Fired when the panel closes. |
Event detail fields: open, panel, optional trigger, and reason (trigger, programmatic, attribute, or beforematch).