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
Name Type Default Required Bindable Description
trigger stringSnippet<[CollapsibleTriggerState]> req Trigger label: a plain string, or a snippet receiving { open, disabled }.
children snippet req Panel content shown when open.
open boolean false bind 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 string((state: CollapsibleTriggerState) => string) 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.