Collapsible

A region that opens and closes from a trigger. The primitive that .accordion, .sidebar submenus, and .navbar mobile menu compose for their own collapse behaviour.

Anatomy

A trigger button and a content region tied by id. The trigger points at the region via data-stisla-collapsible-trigger, and ARIA attributes wire up screen readers in the usual way. The region carries data-stisla-collapsible so the scanner picks it up, and data-state="open" or "closed" drives the paint.

The height transition runs around the state flip. The CSS display: none on the closed state stays put. The JS measures, animates, and clears the inline height once it lands.

Basic

One trigger, one region. Click the button to flip the panel below.

A collapsible is a region whose visibility flips between open and closed. The state lives on the element itself; the height transition runs around the flip so the panel doesn't jump.
<div class="d-flex flex-column gap-3 align-items-start">
  <button type="button" class="btn btn--neutral"
          data-stisla-collapsible-trigger="collapsible-basic"
          aria-controls="collapsible-basic" aria-expanded="false">
    Toggle details
  </button>
  <div id="collapsible-basic" class="collapsible" data-stisla-collapsible data-state="closed" role="region">
    <div style="padding: 1rem; border: 1px solid var(--st-border); border-radius: var(--st-radius); background: var(--st-surface);">
      A collapsible is a region whose visibility flips between open and closed. The state lives on the element itself; the height transition runs around the flip so the panel doesn't jump.
    </div>
  </div>
</div>

Open by default

Render the region with data-state="open" to start open. The trigger's aria-expanded follows on construction.

This region started open. Click the trigger to close it. The opening transition is the same one the trigger fires.
<div class="d-flex flex-column gap-3 align-items-start">
  <button type="button" class="btn btn--neutral"
          data-stisla-collapsible-trigger="collapsible-open"
          aria-controls="collapsible-open" aria-expanded="true">
    Toggle details
  </button>
  <div id="collapsible-open" class="collapsible" data-stisla-collapsible data-state="open" role="region">
    <div style="padding: 1rem; border: 1px solid var(--st-border); border-radius: var(--st-radius); background: var(--st-surface);">
      This region started open. Click the trigger to close it. The opening transition is the same one the trigger fires.
    </div>
  </div>
</div>

Inside a card

A common shape. A card with extra detail hidden behind a chevron. The region's content owns its own surface so the card body doesn't paint inside the collapsed footprint.

API token

Created two weeks ago. Last used yesterday.

  • read:repo
  • write:issues
  • read:user
<div class="card" style="max-width: 24rem;">
  <div class="card__body">
    <h4 class="card__title">API token</h4>
    <p class="card__text text-muted-foreground">Created two weeks ago. Last used yesterday.</p>
    <div class="d-flex gap-2 mt-3">
      <button type="button" class="btn btn--sm btn--ghost btn--neutral"
              data-stisla-collapsible-trigger="collapsible-card"
              aria-controls="collapsible-card" aria-expanded="false">
        Show permissions
      </button>
    </div>
    <div id="collapsible-card" class="collapsible mt-2" data-stisla-collapsible data-state="closed" role="region">
      <ul class="m-0 ps-5 text-muted-foreground">
        <li>read:repo</li>
        <li>write:issues</li>
        <li>read:user</li>
      </ul>
    </div>
  </div>
</div>

Programmatic control

Build a Stisla.Collapsible directly to drive a region from code. Pass any element as opts.triggers, or let the constructor scan for matching data-stisla-collapsible-triggers by id.

This region is opened and closed by the buttons above through Stisla.Collapsible's imperative API.
<div class="d-flex flex-column gap-3 align-items-start">
  <div class="d-flex gap-2">
    <button type="button" class="btn btn--sm btn--neutral" data-demo-collapsible="open">Open</button>
    <button type="button" class="btn btn--sm btn--neutral" data-demo-collapsible="close">Close</button>
    <button type="button" class="btn btn--sm btn--neutral" data-demo-collapsible="toggle">Toggle</button>
  </div>
  <div id="collapsible-programmatic" class="collapsible" data-stisla-collapsible data-state="closed" role="region">
    <div style="padding: 1rem; border: 1px solid var(--st-border); border-radius: var(--st-radius); background: var(--st-surface);">
      This region is opened and closed by the buttons above through <code>Stisla.Collapsible</code>'s imperative API.
    </div>
  </div>
</div>

Events

Four events bubble from the region element. opening and closing are cancelable. Call preventDefault() on the event to abort.

Event Cancelable Fires
stisla:collapsible:opening Yes Before the open transition starts
stisla:collapsible:opened No After the open transition ends
stisla:collapsible:closing Yes Before the close transition starts
stisla:collapsible:closed No After the close transition ends

Customization

One variable controls the transition duration. Override it on the region itself, on a parent scope, or on :root. Pass { duration: 350 } at construction for a per-instance override.

Variable Default Use
--collapsible-transition-duration 0.2s Height transition speed. Set 0 to snap. Honored at construction and on every open/close call. prefers-reduced-motion: reduce zeroes it regardless.