Skip to main content

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

variant
string
default:"default"
Visual style variant: "default" | "ghost" | "filled"
size
string
default:"md"
Size affecting padding and text: "sm" | "md" | "lg"
allowsMultipleExpanded
boolean
default:false
Allow multiple panels to be expanded simultaneously
defaultExpandedKeys
Iterable<Key>
Default expanded items (from React Aria)
expandedKeys
Iterable<Key>
Controlled expanded items (from React Aria)
onExpandedChange
(keys: Set<Key>) => void
Callback when expanded items change (from React Aria)
className
string
Additional CSS classes to apply

AccordionItem

id
string
Unique identifier for the item (required by React Aria)
value
string
Alternative to id for identification
className
string
Additional CSS classes to apply

AccordionTrigger

size
string
Override size: "sm" | "md" | "lg"
icon
React.ComponentType
Custom icon component to replace the default ChevronDown
hideIcon
boolean
default:false
Hide the icon completely
className
string
Additional CSS classes to apply

AccordionContent

size
string
Override size: "sm" | "md" | "lg"
className
string
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

  1. Clear trigger text: Use descriptive text that indicates the panel content
  2. Reasonable content length: Keep panel content focused and scannable
  3. Consistent structure: Maintain similar content structure across items
  4. Consider default state: Expand important items by default when appropriate
  5. Icon consistency: Use the same icon style throughout the accordion
  6. 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.

Build docs developers (and LLMs) love