Vertically stacked disclosure sections on Radix Accordion, single or multiple open, with hairline dividers, a rotating plus and height animation.
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "@/components/ballmac/accordion"
export default function AccordionDemo() {
return (
<Accordion type="single" collapsible defaultValue="install" className="max-w-lg">
<AccordionItem value="install">
<AccordionTrigger>Where do components get installed?</AccordionTrigger>
<AccordionContent>Into components/ballmac, so they never overwrite your shadcn/ui files.</AccordionContent>
</AccordionItem>
<AccordionItem value="theme">
<AccordionTrigger>Do I need the Ballmac theme?</AccordionTrigger>
<AccordionContent>No. Components use the standard shadcn CSS variables and follow your theme.</AccordionContent>
</AccordionItem>
<AccordionItem value="license">
<AccordionTrigger>Can I use them commercially?</AccordionTrigger>
<AccordionContent>Yes. Free components work in personal, client and commercial projects.</AccordionContent>
</AccordionItem>
</Accordion>
)
}Installation
$ pnpm dlx shadcn@latest add @ballmac/accordionInstall the dependencies.
$ pnpm add radix-ui lucide-reactCopy the source into your project.
components/ballmac/accordion.tsx// Ballmac UI: Accordion. https://ui.ballmac.com/components/accordion // Based on shadcn/ui's Accordion (MIT, Copyright (c) 2023 shadcn), restyled with hairline dividers and a rotating plus. "use client" import * as React from "react" import { Accordion as AccordionPrimitive } from "radix-ui" import { Plus } from "lucide-react" import { cn } from "@/lib/utils" type AccordionProps = React.ComponentProps<typeof AccordionPrimitive.Root> function Accordion({ className, ...props }: AccordionProps) { return <AccordionPrimitive.Root data-slot="accordion" className={cn("w-full border-y", className)} {...props} /> } type AccordionItemProps = React.ComponentProps<typeof AccordionPrimitive.Item> function AccordionItem({ className, ...props }: AccordionItemProps) { return <AccordionPrimitive.Item data-slot="accordion-item" className={cn("border-b last:border-b-0", className)} {...props} /> } type AccordionTriggerProps = React.ComponentProps<typeof AccordionPrimitive.Trigger> function AccordionTrigger({ className, children, ...props }: AccordionTriggerProps) { return ( <AccordionPrimitive.Header className="flex"> <AccordionPrimitive.Trigger data-slot="accordion-trigger" className={cn( "group flex flex-1 items-center justify-between gap-4 rounded-md py-5 text-start text-base font-medium outline-none transition-colors hover:text-foreground/80 focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50", className )} {...props} > {children} <Plus aria-hidden="true" className="size-4 shrink-0 text-muted-foreground transition-transform duration-200 ease-[var(--bm-ease-out,ease-out)] group-data-[state=open]:rotate-45 motion-reduce:transition-none" /> </AccordionPrimitive.Trigger> </AccordionPrimitive.Header> ) } type AccordionContentProps = React.ComponentProps<typeof AccordionPrimitive.Content> function AccordionContent({ className, children, ...props }: AccordionContentProps) { return ( <AccordionPrimitive.Content data-slot="accordion-content" className="overflow-hidden text-sm data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down motion-reduce:animate-none" {...props} > <div className={cn("pb-5 leading-relaxed text-muted-foreground", className)}>{children}</div> </AccordionPrimitive.Content> ) } export { Accordion, AccordionItem, AccordionTrigger, AccordionContent, type AccordionProps, type AccordionItemProps, type AccordionTriggerProps, type AccordionContentProps, }Update the import paths to match your project setup.
Usage
import { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from "@/components/ballmac/accordion"The full example is in the Code tab above.
Examples
Multiple open
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "@/components/ballmac/accordion"
export default function AccordionMultiple() {
return (
<Accordion type="multiple" defaultValue={["shipping", "returns"]} className="max-w-lg">
<AccordionItem value="shipping">
<AccordionTrigger>Shipping</AccordionTrigger>
<AccordionContent>Orders ship within two business days.</AccordionContent>
</AccordionItem>
<AccordionItem value="returns">
<AccordionTrigger>Returns</AccordionTrigger>
<AccordionContent>Return anything within 30 days for a full refund.</AccordionContent>
</AccordionItem>
<AccordionItem value="warranty">
<AccordionTrigger>Warranty</AccordionTrigger>
<AccordionContent>Every product has a one-year warranty.</AccordionContent>
</AccordionItem>
</Accordion>
)
}Accessibility
| Key | Action |
|---|---|
| Enter / Space | Toggles the focused section |
| ↓ / ↑ | Moves focus between section headers |
| Home / End | Moves focus to the first or last header |
Use with AI
Show and hide sections of content one at a time (type="single" collapsible) or several at once (type="multiple"). With the shadcn MCP server set up (guide), ask your agent:
Add the Ballmac UI Accordion (@ballmac/accordion) to this project with the shadcn MCP, then use it where it fits.
Use it for
- FAQ sections
- Settings or docs with optional detail
- Long content where readers scan headings first
Not for
- A single show/hide toggle (use reasoning-disclosure or a Radix Collapsible)
- Navigation between views (use tabs)
Registry JSON: https://ui.ballmac.com/r/accordion.json
Credits
Based on shadcn/ui Accordion, adapted by Ballmac. Free to use in personal and commercial projects.
- Registry
- shadcn/utils
Pairs well with
Badge
A small pill label in four variants with an optional status dot and success, warning and error tones. Style links with badgeVariants().
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.
Button Group
Join adjacent actions into one compact control surface while retaining each button's visible keyboard focus and accessible name.
Button
A button with six variants, three sizes, a pill shape and a built-in loading state. buttonVariants() styles links the same way.