Collapsible
Single-section expandable region. The minimal building block for show-more toggles and inline disclosures.
Overview
Collapsible is the simplest disclosure primitive: one trigger, one panel, one open/closed state. Three parts - Collapsible (Root), CollapsibleTrigger, CollapsibleContent - wrap Radix Collapsible.
Reach for Collapsible when you have one thing that expands: a card's "more details" toggle, a sidebar group's expand/collapse, an inline disclosure inside long copy. When you have multiple coordinated sections that share keyboard navigation and a single-open invariant, use Accordion instead - Accordion is built on the same primitives but coordinates a group.
Preview
Installation
bash npx gremorie@latest add rx-collapsible bash pnpm dlx gremorie@latest add rx-collapsible
bash yarn dlx gremorie@latest add rx-collapsible
bash bunx --bun gremorie@latest add rx-collapsible
Usage
import {
Collapsible,
CollapsibleTrigger,
CollapsibleContent,
} from "@gremorie/rx-display";
import { Button } from "@gremorie/rx-forms";
export function Example() {
return (
<Collapsible>
<CollapsibleTrigger asChild>
<Button variant="outline">Toggle details</Button>
</CollapsibleTrigger>
<CollapsibleContent className="mt-2 rounded-md border p-4 text-sm">
Hidden content revealed when the trigger fires.
</CollapsibleContent>
</Collapsible>
);
}Angular edition planned for Phase 5h. Star the repo to track progress.
API
<Collapsible>
Wraps Radix Collapsible.Root.
| Prop | Type | Default | Description |
|---|---|---|---|
open | boolean | - | Controlled open state. |
defaultOpen | boolean | false | Uncontrolled default. |
onOpenChange | (open: boolean) => void | - | Fires when the open state changes. |
disabled | boolean | false | Prevents the trigger from firing. |
<CollapsibleTrigger>
The clickable element that toggles the panel. Wraps Radix Collapsible.Trigger.
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | boolean | false | When true, renders the immediate child as the trigger (typical pattern: wrap a Button). |
When asChild is used, the child receives aria-expanded, aria-controls, and the click handler.
<CollapsibleContent>
The panel that opens and closes. Wraps Radix Collapsible.Content. Exposes the data-state="open" \| "closed" attribute so you can animate via your own CSS or Tailwind keyframes.
The Gremorie CollapsibleContent does not ship default animations - drop in animate-collapsible-down / animate-collapsible-up if you want the standard reveal, or compose your own with data-[state] selectors.
Composition
<Collapsible>owns the open/closed state.<CollapsibleTrigger>sits inside - typically wrapped around aButtonviaasChild.<CollapsibleContent>holds the hidden content. Style it however you like; Radix setshiddenwhen closed anddata-state="open"when open.
The trigger and content can sit at any depth inside the root - Radix Collapsible coordinates via context, not DOM order.
Variations
Show more toggle
<Collapsible>
<CollapsibleTrigger asChild>
<Button variant="outline" className="w-full justify-between">
Show full bio
<ChevronDown className="size-4 transition-transform data-[state=open]:rotate-180" />
</Button>
</CollapsibleTrigger>
<CollapsibleContent className="mt-2 rounded-md border p-4 text-sm">
Hidden bio content.
</CollapsibleContent>
</Collapsible>Controlled open
const [open, setOpen] = useState(false);
<Collapsible open={open} onOpenChange={setOpen}>
<CollapsibleTrigger asChild>
<Button>{open ? 'Hide' : 'Show'} advanced settings</Button>
</CollapsibleTrigger>
<CollapsibleContent>
<SettingsPanel />
</CollapsibleContent>
</Collapsible>;Inline disclosure
Gremorie ships 100 primitives across 9 React packages.
<p>
Gremorie ships 100 primitives.{' '}
<Collapsible className="inline">
<CollapsibleTrigger>Show breakdown</CollapsibleTrigger>
<CollapsibleContent>
<ul>...</ul>
</CollapsibleContent>
</Collapsible>
</p>Accessibility
- ARIA: the trigger receives
aria-expanded(true/false) andaria-controlspointing at the content panel; the content panel ishiddenwhen closed. - Keyboard:
SpaceorEnteron the trigger toggles the panel. Standard<button>semantics when usingasChildwith aButton. - Focus: focus stays on the trigger across toggles. The content is not focus-trapped - readers can Tab through it once open.
- Animation: use
data-[state=open]anddata-[state=closed]selectors so reveals honorprefers-reduced-motionvia your own keyframes. - Disabled state:
disabled={true}on the root disables the trigger and removes it from the tab order.