Skip to content

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.

tsx
import { Collapsible } from 'neba';

<Collapsible title="Shipping and returns">
  <p>Orders placed before 2pm ship the same day.</p>
</Collapsible>;

Props

PropTypeDefaultDescription
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
elevationshared0 | 1 | 2 | 30Drop shadow depth. 0 means no shadow at all
openbooleanWhether the panel is showing. Pass it to drive the Collapsible yourself
defaultOpenbooleanfalseWhere an uncontrolled Collapsible starts
onOpenChange(open: boolean) => voidCalled when the trigger opens or closes the panel
titleReactNodeThe heading on the trigger
subtitleReactNodeA second line under the title, one step down the type scale and muted
startIconReactNodeContent before the title: an icon, a status dot, a count
actionReactNodeA control pinned to the end of the header, outside the trigger so it can be pressed on its own
triggerReactElementReplaces 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
indicatorbooleantrueThe chevron at the end of the header, turned to report the state
paddedbooleantrueInner padding around the panel’s content. Turn it off for something that should reach the edges
disabledbooleanfalseUnavailable. The trigger stops answering
hiddenUntilFoundbooleanfalseKeeps a closed panel in the DOM so the browser’s own page search can find and open it. Overrides keepMounted
keepMountedbooleanfalseKeeps a closed panel in the DOM. For content that is expensive to build, or that holds form state
childrenReactNodeThe 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.

tsx
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>, carrying aria-expanded and an aria-controls pointing at the panel it opens.
  • action sits 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. keepMounted does not change that.
  • With hiddenUntilFound, the browser's own page search can find closed content and open the panel around it.

Released under the MIT License