A collapse/expand component with a unique animation behavior. When collapsing, the content animates from the top down (rather than the typical bottom-up collapse), keeping the user's viewport position stable.
Expand: Standard bottom-down reveal (content pushes down as it appears)
Collapse: Top-down collapse (content disappears from top, viewport stays stable)
<Collapse title="Click to expand">
<Text.body>
This is the collapsible content.
</Text.body>
</Collapse>Use defaultOpen to have the collapse expanded initially.
This content is visible when the component first renders.
<Collapse title="Expanded by default" defaultOpen> <Text.body>This content is visible initially.</Text.body> </Collapse>
Control the open state externally using the open and onOpenChange props.
Current state: closed
const [open, setOpen] = createSignal(false);
<Collapse
title="Controlled collapse"
open={open()}
onOpenChange={setOpen}
>
<Text.body>Controlled content.</Text.body>
</Collapse><Collapse title="Cannot be toggled" disabled> <Text.body>This content cannot be revealed.</Text.body> </Collapse>
By default, CollapseGroup works as an accordion - only one item can be open at a time.
<CollapseGroup>
<Collapse title="Section 1">
<Text.body>Content for section 1.</Text.body>
</Collapse>
<Collapse title="Section 2">
<Text.body>Content for section 2.</Text.body>
</Collapse>
<Collapse title="Section 3">
<Text.body>Content for section 3.</Text.body>
</Collapse>
</CollapseGroup>Set multiple={true} to allow multiple items to be open at once.
This can stay open while others open.
<CollapseGroup multiple>
<Collapse title="First item" defaultOpen>
<Text.body>This can stay open while others open.</Text.body>
</Collapse>
<Collapse title="Second item">
<Text.body>Try opening this while the first is open.</Text.body>
</Collapse>
<Collapse title="Third item">
<Text.body>All three can be open simultaneously.</Text.body>
</Collapse>
</CollapseGroup>Use requireOne to prevent all items from being closed in accordion mode.
Try closing this - another item will need to open first.
<CollapseGroup requireOne>
<Collapse title="Always one open" defaultOpen>
<Text.body>You cannot close the last open item.</Text.body>
</Collapse>
<Collapse title="Second option">
<Text.body>At least one must remain open.</Text.body>
</Collapse>
</CollapseGroup>Individual Collapse items' defaultOpen props are respected for initial state.
This starts open because of defaultOpen.
<CollapseGroup> <Collapse title="Closed initially">...</Collapse> <Collapse title="Open initially" defaultOpen>...</Collapse> <Collapse title="Also closed">...</Collapse> </CollapseGroup>
| Name | Type | Default | Description |
|---|---|---|---|
title | string | required | Text displayed in the trigger header |
children | JSX.Element | required | Content to be collapsed/expanded |
defaultOpen | boolean | false | Initial open state for uncontrolled mode |
open | boolean | undefined | Controlled open state |
onOpenChange | (open: boolean) => void | undefined | Callback when open state changes |
disabled | boolean | false | Prevents toggling and forces closed state |
styles | StyleXStyles | undefined | Additional StyleX styles to apply |
| Name | Type | Default | Description |
|---|---|---|---|
children | JSX.Element | required | Collapse components to group together |
multiple | boolean | false | Allow multiple items open at once (false = accordion mode) |
requireOne | boolean | false | When multiple={false}, require at least one item to stay open |
styles | StyleXStyles | undefined | Additional StyleX styles for the group wrapper |
The Collapse component includes full keyboard support:
Enter / Space - Toggle the focused item
ArrowUp / ArrowDown - Navigate between items in a CollapseGroup (with wrap-around)
The Collapse component follows WAI-ARIA patterns for disclosure widgets:
Trigger button with aria-expanded reflecting current state
Content region with aria-hidden when collapsed
aria-controls linking trigger to content
Focus-visible outline for keyboard navigation
Respects prefers-reduced-motion for animations
The Collapse component uses chevron icons from the IconProvider. Make sure your icon index includes:
const icons: IconIndex = {
"chevron-up": {
default: `<svg>...</svg>`,
},
"chevron-down": {
default: `<svg>...</svg>`,
},
// ... other icons
};