Collapsible
A single show/hide disclosure that expands and collapses a panel of content.
Show codeHide code
Installation
import { Collapsible } from "@makeplane/propel/components/collapsible";Usage
Pass trigger for the header label and children for the panel body. Pass a public <Icon> into
icon for the leading 16px glyph (Figma’s resting art includes it). The rotating chevron is on by
default (indicator defaults to true). Pass trailing for a header-end control (typically an
IconButton) — it renders as a sibling of the trigger, not inside it. Drive open state with
defaultOpen (uncontrolled) or open + onOpenChange (controlled). Pass keepMounted to leave
the panel in the DOM while closed, or hiddenUntilFound so closed content also stays findable via
the browser’s find-in-page — hiddenUntilFound alone already keeps the panel mounted, so the two
are not meant to be combined (Base UI Panel props).
Use placement="sidebar" for navigation sections: compact header insets, a rounded full-row hover
background, unpadded panel content, and an end-aligned chevron after the optional trailing
action. The action remains outside the disclosure button, so it does not toggle the section. The
default placement="inline" is unchanged. Pass hug on inline (or any placement) to drop panel
padding independently of placement.
import { Collapsible } from "@makeplane/propel/components/collapsible";
import { Icon } from "@makeplane/propel/components/icon";
import { ActivityOutline } from "@makeplane/propel/icons";
export default function BasicDemo() {
return (
<Collapsible
trigger="Collapsible header"
icon={<Icon icon={ActivityOutline} tint="secondary" size="inherit" />}
>
Content that expands and collapses with this section.
</Collapsible>
);
}Anatomy
import { Collapsible as BaseCollapsible } from "@base-ui/react/collapsible";
import {
Collapsible,
CollapsibleHeader,
CollapsibleTrigger,
CollapsibleTriggerTitle,
CollapsibleTriggerTrailing,
CollapsiblePanel,
CollapsiblePanelContent,
} from "@makeplane/propel/components/collapsible";
// Styled Root frame (`w-full`) — separate from the ready-made `Collapsible` above (name collision).
import { Collapsible as CollapsibleFrame } from "@makeplane/propel/elements/collapsible";
// Ready-made — grafts Base UI Root → CollapsibleFrame for you
<Collapsible trigger="…" icon={…} trailing={…}>
…
</Collapsible>
// Atomic parts — graft Base UI yourself
<BaseCollapsible.Root render={<CollapsibleFrame />}>
<CollapsibleHeader>
<BaseCollapsible.Trigger render={<CollapsibleTrigger />}>
{icon}
<CollapsibleTriggerTitle>…</CollapsibleTriggerTitle>
{/* Rotating chevron — the shared caret is internal; supply your own */}
</BaseCollapsible.Trigger>
<CollapsibleTriggerTrailing>{/* IconButton, never nested in the trigger */}</CollapsibleTriggerTrailing>
</CollapsibleHeader>
<BaseCollapsible.Panel render={<CollapsiblePanel />}>
<CollapsiblePanelContent>…</CollapsiblePanelContent>
</BaseCollapsible.Panel>
</BaseCollapsible.Root>The Root frame is full width so centered hosts don’t shrink-wrap when closed. The header is a flex
row: the trigger packs [icon?][title][chevron] as a left-aligned cluster and grows so leftover
space stays the toggle hit target; CollapsibleTriggerTrailing pins a sibling control at the
inline-end. Long titles wrap instead of staying single-line (Figma’s short sample uses nowrap).
Panel padding and body type live on CollapsiblePanelContent (px-3 pb-3 for inline, p-0 for
sidebar or whenever hug is set, + body-xs secondary) so the height animation on
CollapsiblePanel measures cleanly and List sections (children directly in the panel) do not
inherit Collapsible prose tokens. The rotating chevron
itself is an internal shared primitive (not exported) — an atomic assembly supplies its own, e.g. a
ChevronDownOutline from @makeplane/propel/icons rotated off data-panel-open.
Examples
Without icon
Omit icon when the row should be label + chevron only.
Show codeHide code
Default open
Show codeHide code
Controlled
Pass open and onOpenChange to drive the disclosure from your own state instead of defaultOpen.
Show codeHide code
Without indicator
Show codeHide code
Trailing action
Pass trailing for a header-end control. It is a sibling of the trigger — never nest a button
inside the trigger. Clicking it does not toggle the panel. Root disabled does not disable this
control; disable the control itself if both should refuse input.
Show codeHide code
Disabled
Pass Root disabled to dim the trigger and block open/close.
Show codeHide code
Keep mounted
Pass keepMounted to leave the panel in the DOM while closed. hiddenUntilFound does the same
(plus browser find-in-page) — pass one or the other, not both.
Show codeHide code
Accessibility
- The trigger is a native
<button>with Base UI’saria-expanded/aria-controls. - The chevron is decorative (
aria-hidden); the accessible name comes from the trigger label. - A
trailingcontrol is a sibling of the trigger, never nested inside it (nested buttons are invalid). It is its own tab stop and does not toggle the panel. - Disabled state dims the trigger and blocks open/close via Root
disabled(data-disabled/aria-disabled) and swaps the cursor; the trigger stays focusable but will not toggle. Trailing controls are not disabled by Rootdisabled. - Focus-visible draws the shared accent ring on the trigger.
API Reference
Collapsible
The ready-made collapsible: a single show/hide disclosure that wires the trigger and panel for
the 90% case. Pass trigger for the toggle button label and children for the body; optional
icon for a leading glyph and trailing for a header-end control (sibling of the trigger).
Forward defaultOpen (uncontrolled) or open + onOpenChange (controlled) to drive it. Set
indicator={false} to omit the rotating chevron from the trigger.