Data Display

Collapsible

Single-panel disclosure that toggles one region with an animated height.

import { Collapsible } from '@lostgradient/cinder/collapsible';
disclosurecollapsible
01

Overview

Single-panel show/hide disclosure — the single-item counterpart to Accordion. A labeled trigger toggles one region, animating its height open and closed with Svelte's native slide transition.

Usage

Uncontrolled — the component owns its state:

svelte
<script lang="ts">
  import Collapsible from '@lostgradient/cinder/collapsible';
</script>

<Collapsible trigger="Shipping details">
  <p>Orders ship within two business days.</p>
</Collapsible>

Controlled — the parent owns the state with bind:open:

svelte
<script lang="ts">
  import Collapsible from '@lostgradient/cinder/collapsible';

  let open = $state(false);
</script>

<Collapsible bind:open trigger="Controlled panel">
  <p>Open state is shared with the parent.</p>
</Collapsible>

State model

open is bindable. Without bind:open, it seeds local state and can still be updated by parent prop changes, but trigger clicks update local state only. Use bind:open (or mirror onToggle in the parent) when parent and trigger interactions must stay fully synchronized. onToggle fires on every toggle with the next boolean, so one-way observers stay in sync.

The trigger prop is either a string or a snippet receiving { open, disabled }, letting the label react to state (for example, swapping "Show" and "Hide"). Use triggerAriaLabel when you need a dedicated accessible name on the internal trigger button. It accepts either a static string or a state-aware function (({ open, disabled }) => string) for dynamic labels and stable getByRole(..., { name }) selectors.

Accessibility

  • The trigger is a native <button type="button">, so Enter and Space activation and tab order come from the browser. disabled uses the real attribute, removing it from the tab order.
  • The trigger carries aria-expanded; the panel is a role="region" labeled by the trigger via aria-labelledby.
  • The panel is removed from the DOM when closed (standard disclosure behavior). aria-controls is emitted only while the panel exists, so it never references a missing element. Each open panel registers as a named region landmark — on a page with many open Collapsibles, weigh that against landmark-navigation noise.
  • Height animation collapses to zero duration under prefers-reduced-motion: reduce, and the chevron rotation transition is disabled in CSS.

For multiple coordinated sections where opening one may close others, use Accordion instead. See collapsible.a11y.md for a detailed comparison of when to use each pattern.

Live preview
02

When to use

Use when
  • Hiding a single optional region behind a labeled trigger.
  • You need controlled or uncontrolled show/hide with an accessible toggle.
Avoid when
  • Coordinating multiple sections where opening one may close others — use accordion.
03

Examples

Uncontrolled disclosure

Without bind:open the component owns its toggle state.

Controlled with bind:open

An external control drives open state via two-way binding.

Disabled trigger

A disabled Collapsible cannot be toggled. The native disabled attribute removes the trigger from the tab order.

State-aware trigger label

A snippet trigger receives { open, disabled } so the label can react to state.

04

Props

Props for collapsible
NameTypeDefaultDescription
trigger required text Trigger label: a plain string, or a snippet receiving { open, disabled }.
children required snippet Panel content shown when open.
open bindable boolean false Bindable open state. Without bind:open, this seeds local state and can be updated by parent prop changes, while trigger clicks update local state. Use bind:open for full parent/trigger synchronization.
onToggle (open: boolean) => void Fired on every successful toggle with the next open state. Not called while disabled.
disabled boolean false When true, the trigger cannot be toggled.
triggerAriaLabel text Accessible name override for the internal trigger button. Accepts either a fixed string or a function receiving { open, disabled } so labels can react without requiring bind:open.
idBase text Base used to derive the trigger and panel ARIA ids (<base>-header, <base>-label, <base>-panel). NOT the root element id. Auto-generated when omitted.