A single disclosure panel with a generous trigger, visible open state, and reduced-motion-aware content transition.
import { BadgeCheck, GitCommitHorizontal } from "lucide-react";
import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
} from "@/components/ballmac/collapsible";
export default function CollapsibleDemo() {
return (
<Collapsible
defaultOpen
className="w-full max-w-sm rounded-xl border bg-card p-2 shadow-sm"
>
<CollapsibleTrigger>
<span className="flex items-center gap-2">
<BadgeCheck aria-hidden="true" className="size-4 text-primary" />
Release 2.4 is ready
</span>
</CollapsibleTrigger>
<CollapsibleContent>
<div className="mx-3 mb-2 border-t pt-3 text-sm">
<p className="mb-3 text-muted-foreground">
Three updates are included in this release.
</p>
<ul className="grid gap-2">
<li className="flex items-center gap-2">
<GitCommitHorizontal
aria-hidden="true"
className="size-4 text-muted-foreground"
/>{" "}
Faster project search
</li>
<li className="flex items-center gap-2">
<GitCommitHorizontal
aria-hidden="true"
className="size-4 text-muted-foreground"
/>{" "}
Share links for guests
</li>
<li className="flex items-center gap-2">
<GitCommitHorizontal
aria-hidden="true"
className="size-4 text-muted-foreground"
/>{" "}
Improved export flow
</li>
</ul>
</div>
</CollapsibleContent>
</Collapsible>
);
}Installation
$ pnpm dlx shadcn@latest add @ballmac/collapsibleInstall the dependencies.
$ pnpm add radix-ui lucide-reactCopy the source into your project.
components/ballmac/collapsible.tsx// Ballmac UI: Collapsible. https://ui.ballmac.com/components/collapsible "use client"; import * as React from "react"; import { ChevronDown } from "lucide-react"; import { Collapsible as Primitive } from "radix-ui"; import { cn } from "@/lib/utils"; type CollapsibleProps = React.ComponentProps<typeof Primitive.Root>; function Collapsible({ className, ...props }: CollapsibleProps) { return ( <Primitive.Root data-slot="collapsible" className={cn("w-full", className)} {...props} /> ); } type CollapsibleTriggerProps = React.ComponentProps< typeof Primitive.Trigger > & { /** Show the rotating disclosure chevron after the label. */ showChevron?: boolean; }; function CollapsibleTrigger({ className, children, showChevron = true, ...props }: CollapsibleTriggerProps) { return ( <Primitive.Trigger data-slot="collapsible-trigger" className={cn( "group flex min-h-11 w-full items-center justify-between gap-3 rounded-lg px-3 text-start text-sm font-medium outline-none transition-colors duration-150 hover:bg-accent/70 focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:opacity-50 motion-reduce:transition-none", className, )} {...props} > {children} {showChevron && ( <ChevronDown aria-hidden="true" className="size-4 shrink-0 text-muted-foreground transition-transform duration-200 group-data-[state=open]:rotate-180 motion-reduce:transition-none" /> )} </Primitive.Trigger> ); } type CollapsibleContentProps = React.ComponentProps<typeof Primitive.Content>; function CollapsibleContent({ className, ...props }: CollapsibleContentProps) { return ( <Primitive.Content data-slot="collapsible-content" className={cn( "overflow-hidden text-sm data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:slide-in-from-top-1 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 motion-reduce:animate-none", className, )} {...props} /> ); } export { Collapsible, CollapsibleTrigger, CollapsibleContent, type CollapsibleProps, type CollapsibleTriggerProps, type CollapsibleContentProps, };Update the import paths to match your project setup.
Usage
import { Collapsible, CollapsibleTrigger, CollapsibleContent } from "@/components/ballmac/collapsible"The full example is in the Code tab above.
Examples
Controlled disclosure
Controlled state: open
"use client";
import * as React from "react";
import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
} from "@/components/ballmac/collapsible";
export default function CollapsibleStates() {
const [open, setOpen] = React.useState(true);
return (
<div className="w-full max-w-xs">
<p className="mb-2 text-xs text-muted-foreground">
Controlled state: {open ? "open" : "closed"}
</p>
<Collapsible
open={open}
onOpenChange={setOpen}
className="rounded-xl border p-2"
>
<CollapsibleTrigger>Advanced settings</CollapsibleTrigger>
<CollapsibleContent>
<p className="px-3 pb-3 text-sm text-muted-foreground">
Only admins can change project defaults.
</p>
</CollapsibleContent>
</Collapsible>
</div>
);
}API reference
| Prop | Type | Default |
|---|---|---|
showChevronShow the rotating disclosure chevron after the label. | boolean | true |
Also accepts the standard attributes of its root element.
Accessibility
| Key | Action |
|---|---|
| Enter / Space | Opens or closes the disclosure |
| Tab | Moves into visible content |
Use with AI
Reveals one optional section of information with a keyboard-accessible disclosure trigger. With the shadcn MCP server set up (guide), ask your agent:
Add the Ballmac UI Collapsible (@ballmac/collapsible) to this project with the shadcn MCP, then use it where it fits.
Use it for
- Advanced settings
- Supporting detail in a dense card
Not for
- Several related disclosures; use accordion
- Primary content that should always be visible
Registry JSON: https://ui.ballmac.com/r/collapsible.json
Credits
Free to use in personal and commercial projects.
- Registry
- shadcn/utils
Pairs well with
Card
A composable content surface with compact spacing, an action slot, and optional interactive feedback.
Accordion
Vertically stacked disclosure sections on Radix Accordion, single or multiple open, with hairline dividers, a rotating plus and height animation.
Avatar
A Radix avatar with image and initials fallback, three sizes, an optional presence dot, and AvatarGroup for overlapping stacks with a +N overflow counter.
Badge
A small pill label in four variants with an optional status dot and success, warning and error tones. Style links with badgeVariants().