Collapsible
Overview
Intent-oriented alias of the Disclosure primitive.
Pattern: Disclosure. Status: stable.
When to use
- A product-language alias for a single Disclosure-powered expandable region.
- Teams that prefer “collapsible” naming while sharing the Disclosure contract and tests.
When not to use
- Grouped collapsibles; use Accordion.
- Complex overlays or animated drawers that need focus trapping.
Import
ts
import { createCollapsible } from 'ui-headless-runtime';Controller creation
Create Collapsible during component mount or setup, subscribe before rendering derived UI, and keep every cleanup returned by registrations or DOM binding.
Options
- Public options:
disabled,id,defaultValue,getValue,onValueChange,subscribeValue. - Collapsible accepts
DisclosureOptions; the state name isexpanded, notopen.
Snapshot
- Snapshot fields:
controlled,disabled,expanded,panel,trigger. triggerandpanelcontain the stable IDs, relationship metadata, disabled state, role, and visibility required by consumer markup.
Commands
- Component commands:
collapse,expand,handleTriggerClick,handleTriggerKeyDown,setDisabled,toggle. - Collapsible is a type alias of
DisclosureController; it has no DOMbindmethod.
Events
- Events:
beforeClose,beforeOpen,close,open,afterClose,afterOpen,stateChange. - The before-events are cancellable; all payloads use
DisclosureChangeEvent.
Change reasons
- Change reasons:
programmatic,trigger,keyboard.
Controlled mode
Controlled Collapsible exists for naming consistency while delegating ownership semantics to Disclosure.
Uncontrolled mode
Uncontrolled Collapsible owns a single boolean open state.
DOM binding
- Bind a trigger and content region, or manually apply snapshot IDs and expanded state.
Required markup
- Use a button trigger and a panel/region connected with
aria-controls.
ARIA contract
- Apply the same
aria-expandedandaria-controlscontract as Disclosure.
Keyboard interaction
- Enter / Space: Toggle content.
Focus behavior
- Enter and Space toggle; focus otherwise follows normal document order.
Nested behavior
- Nested collapsibles should use separate controllers and IDs.
Cleanup
- Release the Disclosure binding and destroy the controller during unmount.
Minimal lifecycle example
ts
import { createCollapsible } from 'ui-headless-runtime';
const controller = createCollapsible();
const unsubscribe = controller.subscribe((snapshot) => {
console.log(snapshot);
});
console.log(controller.getSnapshot());
unsubscribe();
controller.destroy();The production demo loads the exact executable module from apps/demo/src/examples/collapsible.ts.
Edge cases
basic: Simple expandable content.controlled: External ownership with the same contract.
Limitations
- It intentionally adds no state model beyond Disclosure.
Related links
API reference
See createCollapsible.