Collapsible
One section that folds, standing on its own. Pressing the header opens what is under it and pressing it again closes it. A set of these, of which only one is open, is an Accordion.
import { Collapsible } from 'neba';
<Collapsible title="Shipping and returns">
<p>Orders placed before 2pm ship the same day.</p>
</Collapsible>;Props
| Prop | Type | Default | Description |
|---|---|---|---|
| variantshared | 'solid' | 'outline' | 'text' | 'outline' | Weight of the sheet, said the way a container says it: never dyed. `text` draws no sheet at all, which is what a fold inside running prose or inside a Card wants |
| sizeshared | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'md' | The scale of the padding and the radius, and the type scale of the title and the body |
| colorshared | 'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'info' | 'primary' | Semantic colour role. Arbitrary colour values are not accepted |
| densityshared | 'default' | 'compact' | 'default' | Padding only: never the height, never the type scale |
| elevationshared | 0 | 1 | 2 | 3 | 0 | Drop shadow depth. 0 means no shadow at all |
| open | boolean | — | Whether the panel is showing. Pass it to drive the Collapsible yourself |
| defaultOpen | boolean | false | Where an uncontrolled Collapsible starts |
| onOpenChange | (open: boolean) => void | — | Called when the trigger opens or closes the panel |
| title | ReactNode | — | The heading on the trigger |
| subtitle | ReactNode | — | A second line under the title, one step down the type scale and muted |
| startIcon | ReactNode | — | Content before the title: an icon, a status dot, a count |
| action | ReactNode | — | A control pinned to the end of the header, outside the trigger so it can be pressed on its own |
| trigger | ReactElement | — | Replaces the header entirely with a control of your own. The element you pass becomes the trigger, and is handed the click handler, aria-expanded and aria-controls |
| indicator | boolean | true | The chevron at the end of the header, turned to report the state |
| padded | boolean | true | Inner padding around the panel’s content. Turn it off for something that should reach the edges |
| disabled | boolean | false | Unavailable. The trigger stops answering |
| hiddenUntilFound | boolean | false | Keeps a closed panel in the DOM so the browser’s own page search can find and open it. Overrides keepMounted |
| keepMounted | boolean | false | Keeps a closed panel in the DOM. For content that is expensive to build, or that holds form state |
| children | ReactNode | — | The body |
Every other <div> attribute passes through to the root, except onChange: the change worth listening for is onOpenChange.
The shared axes (variant size color density elevation) are defined in prop conventions.
Examples
title, subtitle, startIcon, action
title is the heading on the header and subtitle is the line under it. startIcon goes before the title; action goes at the end of the header but outside the trigger: a header that both folds and holds a switch has two things to press, and one of them cannot be nested inside the other.
indicator={false} drops the chevron, which leaves the header reporting its state in colour alone.
trigger
trigger replaces the header entirely with a control of your own. The element you pass becomes the trigger: it is handed the click handler, aria-expanded and the aria-controls pointing at the panel, so there is nothing to wire up.
variant
The three weights say what they say on every other container. text draws no sheet at all, which is what a fold inside running prose (or inside a Card, which is already a sheet) usually wants.
keepMounted, hiddenUntilFound
A closed panel leaves the DOM by default. keepMounted keeps it there, so content that is expensive to build or that holds form state survives being folded away. hiddenUntilFound keeps it there as hidden="until-found", which lets the browser's own page search find and open it, and overrides keepMounted.
Controlling it
Pass open and the Collapsible keeps no state of its own. Use it to open and close several at once, to put the open state in the URL, or to drive it from a control elsewhere on the page.
const [open, setOpen] = useState(false);
<Collapsible title="Advanced" open={open} onOpenChange={setOpen}>
<p>Everything else goes here.</p>
</Collapsible>;Accessibility
- The trigger is a real
<button>, carryingaria-expandedand anaria-controlspointing at the panel it opens. actionsits outside the trigger, so it is reachable and pressable on its own from a keyboard.- A closed panel leaves the DOM, so it is in neither the tab order nor the accessibility tree.
keepMounteddoes not change that. - With
hiddenUntilFound, the browser's own page search can find closed content and open the panel around it.