Overview
The Accordion component provides collapsible content sections with full keyboard navigation and screen reader support. Built on React Aria Components with class-variance-authority for variant management.
Installation
import {
Accordion,
AccordionItem,
AccordionTrigger,
AccordionContent,
} from "@/components/ui/Accordion";
Anatomy
The Accordion is composed of four sub-components:
- Accordion: Root container (uses React Aria’s DisclosureGroup)
- AccordionItem: Individual collapsible section (uses React Aria’s Disclosure)
- AccordionTrigger: Clickable header button to toggle content
- AccordionContent: Collapsible content panel (uses React Aria’s DisclosurePanel)
Basic Usage
<Accordion>
<AccordionItem>
<AccordionTrigger>Is it accessible?</AccordionTrigger>
<AccordionContent>
Yes. It adheres to the WAI-ARIA design pattern and supports full
keyboard navigation with screen reader support.
</AccordionContent>
</AccordionItem>
<AccordionItem>
<AccordionTrigger>Is it styled?</AccordionTrigger>
<AccordionContent>
Yes. It comes with default styles using design tokens that
automatically adapt to all brand themes and dark mode.
</AccordionContent>
</AccordionItem>
<AccordionItem>
<AccordionTrigger>Is it animated?</AccordionTrigger>
<AccordionContent>
By default, yes. The accordion includes smooth expand/collapse
animations using CSS transitions.
</AccordionContent>
</AccordionItem>
</Accordion>
Variants
The Accordion supports three visual variants:
Default
Standard accordion with border and background.
<Accordion variant="default">
<AccordionItem>
<AccordionTrigger>Default variant</AccordionTrigger>
<AccordionContent>
This is the default accordion variant with border and background.
</AccordionContent>
</AccordionItem>
</Accordion>
Ghost
Minimal styling with transparent background.
<Accordion variant="ghost">
<AccordionItem>
<AccordionTrigger>Ghost variant</AccordionTrigger>
<AccordionContent>
This is the ghost accordion variant with minimal styling.
</AccordionContent>
</AccordionItem>
</Accordion>
Filled
Muted background for subtle emphasis.
<Accordion variant="filled">
<AccordionItem>
<AccordionTrigger>Filled variant</AccordionTrigger>
<AccordionContent>
This is the filled accordion variant with muted background.
</AccordionContent>
</AccordionItem>
</Accordion>
Sizes
Three size options control spacing and text size:
{/* Small - Compact spacing */}
<Accordion size="sm">
<AccordionItem>
<AccordionTrigger>Small accordion</AccordionTrigger>
<AccordionContent>
This is a small accordion with compact spacing and text.
</AccordionContent>
</AccordionItem>
</Accordion>
{/* Medium - Default size */}
<Accordion size="md">
<AccordionItem>
<AccordionTrigger>Medium accordion</AccordionTrigger>
<AccordionContent>
This is a medium accordion with standard spacing and text.
</AccordionContent>
</AccordionItem>
</Accordion>
{/* Large - Generous spacing */}
<Accordion size="lg">
<AccordionItem>
<AccordionTrigger>Large accordion</AccordionTrigger>
<AccordionContent>
This is a large accordion with generous spacing and text.
</AccordionContent>
</AccordionItem>
</Accordion>
Props
Accordion
Visual style variant: "default" | "ghost" | "filled"
Size affecting padding and text: "sm" | "md" | "lg"
Allow multiple panels to be expanded simultaneously
Default expanded items (from React Aria)
Controlled expanded items (from React Aria)
Callback when expanded items change (from React Aria)
Additional CSS classes to apply
AccordionItem
Unique identifier for the item (required by React Aria)
Alternative to id for identification
Additional CSS classes to apply
AccordionTrigger
Override size: "sm" | "md" | "lg"
Custom icon component to replace the default ChevronDown
Additional CSS classes to apply
AccordionContent
Override size: "sm" | "md" | "lg"
Additional CSS classes to apply
Multiple Expanded Items
Allow multiple accordion panels to be open at once:
<Accordion allowsMultipleExpanded>
<AccordionItem>
<AccordionTrigger>Can I expand multiple items?</AccordionTrigger>
<AccordionContent>
Yes! This accordion allows multiple items to be expanded at the same time.
</AccordionContent>
</AccordionItem>
<AccordionItem>
<AccordionTrigger>This one too?</AccordionTrigger>
<AccordionContent>
Absolutely. You can have as many panels open as you want.
</AccordionContent>
</AccordionItem>
<AccordionItem>
<AccordionTrigger>And this one?</AccordionTrigger>
<AccordionContent>
Yes, all of them can be open simultaneously.
</AccordionContent>
</AccordionItem>
</Accordion>
Custom Icons
Replace the default chevron icon or hide it completely:
import { ChevronRight, Plus } from "lucide-react";
<Accordion>
<AccordionItem>
<AccordionTrigger icon={ChevronRight}>
Custom chevron right
</AccordionTrigger>
<AccordionContent>
This accordion item uses a chevron right icon instead of down.
</AccordionContent>
</AccordionItem>
<AccordionItem>
<AccordionTrigger icon={Plus}>
Plus/minus style
</AccordionTrigger>
<AccordionContent>
This uses a plus icon that rotates to become a minus when expanded.
</AccordionContent>
</AccordionItem>
<AccordionItem>
<AccordionTrigger hideIcon>
No icon at all
</AccordionTrigger>
<AccordionContent>
This accordion item has no icon, just text.
</AccordionContent>
</AccordionItem>
</Accordion>
Complex Content
Accordion content can contain any React elements:
import { Settings, User, Bell } from "lucide-react";
<Accordion>
<AccordionItem>
<AccordionTrigger icon={Settings}>Account Settings</AccordionTrigger>
<AccordionContent>
<div className="space-y-3">
<div className="flex items-center gap-2">
<User className="h-4 w-4" />
<span>Profile Information</span>
</div>
<div className="flex items-center gap-2">
<Bell className="h-4 w-4" />
<span>Notification Preferences</span>
</div>
<button
className="mt-2 px-3 py-1 rounded text-sm"
style={{
backgroundColor: "var(--interactive-primary)",
color: "var(--interactive-primary-text)",
}}
>
Edit Settings
</button>
</div>
</AccordionContent>
</AccordionItem>
</Accordion>
React Aria Components Integration
The Accordion uses React Aria Components for accessibility:
DisclosureGroup
Provides group-level behavior:
- Manages expanded state across items
- Supports single or multiple expansion modes
- Handles keyboard navigation between items
Disclosure
Manages individual item state:
- Tracks expanded/collapsed state
- Provides data attributes for styling
- Manages ARIA attributes
DisclosurePanel
Handles content visibility:
- Smooth expand/collapse animations
- Proper ARIA roles and attributes
- Screen reader announcements
Accessibility Features
Keyboard Navigation
- Tab: Move focus to the next trigger
- Shift + Tab: Move focus to the previous trigger
- Enter/Space: Toggle the focused item
- Arrow Keys: Navigate between triggers (when focused)
ARIA Attributes
role="group" on the Accordion
role="button" on triggers
aria-expanded indicates panel state
aria-controls links triggers to panels
aria-labelledby associates panels with triggers
Screen Reader Support
- Item state changes are announced
- Content is properly associated with triggers
- Focus management maintains context
Design Tokens
The Accordion uses semantic design tokens:
--text-primary/secondary/tertiary: Text colors
--bg-primary/secondary/tertiary: Background colors
--border-primary/focus: Border colors
--font-family-primary: Typography
--brand-transition: Animation duration
Best Practices
- Clear trigger text: Use descriptive text that indicates the panel content
- Reasonable content length: Keep panel content focused and scannable
- Consistent structure: Maintain similar content structure across items
- Consider default state: Expand important items by default when appropriate
- Icon consistency: Use the same icon style throughout the accordion
- Avoid nested accordions: Don’t nest accordions within accordion content
Dark Mode
The Accordion automatically adapts to dark mode using semantic tokens:
{/* Works in both light and dark themes */}
<Accordion variant="default">
<AccordionItem>
<AccordionTrigger>Dark mode support</AccordionTrigger>
<AccordionContent>
This accordion automatically adapts to dark mode with proper
contrast and colors.
</AccordionContent>
</AccordionItem>
</Accordion>
All text, backgrounds, and borders adjust automatically based on the active theme.