v0.5

Accordion

Stacked disclosure rows that expand a single panel at a time or many in parallel.

Yes. It follows the WAI-ARIA disclosure pattern.

Description

Accordion is a compound component built on Radix's Accordion primitive. It renders a vertical stack of AccordionItems, each pairing an AccordionTrigger (the clickable header) with an AccordionContent (the expanding panel). A built-in chevron rotates with the open state and a data-state attribute streams from Radix to drive the visuals.

Reach for it when you have a list of disclosure rows that share the same shape, such as FAQ pages, settings groups, nested form sections, and long product specs. Switch type="single" (with optional collapsible) for one-at-a-time reveal, or type="multiple" when the user should be able to open several panels at once. The appearance prop covers six visual treatments, from a borderless ghost-default for in-page FAQs to a card-like outline-rounded or filled-rounded for dense settings panes.

Don't reach for Accordion when you need true tab navigation between sibling views; use Tabs instead. For a single, standalone show/hide region with no list around it, drop down to a Collapsible or Disclosure primitive. For transient, overlay-style content (confirmations, forms that block the page), prefer Dialog or Popover instead.

Installation

pnpm dlx @create-ui/cli add accordion

Anatomy

<Accordion>
  <AccordionItem>
    <AccordionTrigger />
    <AccordionContent />
  </AccordionItem>
</Accordion>

Usage

import {
  Accordion,
  AccordionContent,
  AccordionItem,
  AccordionTrigger,
} from "@/components/ui/accordion"
<Accordion type="single" collapsible>
  <AccordionItem value="item-1">
    <AccordionTrigger>Is it accessible?</AccordionTrigger>
    <AccordionContent>Yes. It follows WAI-ARIA.</AccordionContent>
  </AccordionItem>
</Accordion>

Examples

Ghost Appearance

The three ghost treatments add no card surface around each item. ghost-default is the lightest and works well for inline FAQs. ghost-underline separates rows with a single border, ideal for long lists. ghost-rounded keeps the borderless feel but rounds the open-state surface.

ghost-default

ghost-underline

ghost-rounded

Card Appearance

The card treatments wrap each item in its own surface. outline-rounded is the default card look, with rounded corners, a hairline border, and a soft shadow that clears on hover. outline-sharp is the dense, table-like option with flush corners. filled-rounded swaps the border for a filled background.

outline-rounded

outline-sharp

filled-rounded

Multiple

type="multiple" lets the user open several panels at once and accepts an array value / defaultValue. Pair it with appearance="ghost-underline" when the list is long and you want clear row separators.

Yes. Open me and the others.

Yes. Stays open alongside siblings.

With Icon

AccordionTrigger accepts an icon prop that renders a leading glyph next to the label, sized to size-5. The chevron stays anchored to the trailing edge.

With Trailing Content

The trigger forwards children into a flex label slot, so anything you put there sits between the leading icon and the chevron. Push a Badge to the trailing edge with className="ml-auto" to surface status, counts, or severity inline.

Disabled

Set disabled on an AccordionItem to lock it. The trigger becomes non-interactive, the chevron fades to text-disabled, and Radix forwards data-disabled for downstream styling. Setting disabled on the Accordion root disables every item.

Rich Content

AccordionContent accepts any React tree. Use it to nest paragraphs, InlineAlerts, action Buttons, or even forms. The wrapper already supplies flex flex-col gap-4, so direct children stack with consistent spacing.

Payment failures are usually caused by incorrect card details, insufficient funds, or a temporary block from your bank. Double-check your billing information and try again. If the issue persists, contact your bank or reach out to our support team.

Accessibility

Accordion delegates focus and ARIA wiring to Radix's Accordion primitive. Each trigger is a real <button> with aria-expanded, aria-controls, and a unique pairing to its content region. The chevron is decorative and not announced.

KeyDescription
Space / EnterToggles the focused item open or closed.
TabMoves focus to the next focusable trigger.
Shift + TabMoves focus to the previous focusable trigger.
ArrowDownMoves focus to the next trigger in the accordion.
ArrowUpMoves focus to the previous trigger in the accordion.
HomeMoves focus to the first trigger.
EndMoves focus to the last trigger.

ARIA notes:

  • Each AccordionItem must have a unique value; Radix uses it to wire aria-controls / aria-labelledby between trigger and content.
  • AccordionContent is rendered as a region and labelled by its trigger; screen readers announce it when it opens.
  • disabled items are skipped by arrow-key navigation and expose data-disabled for styling.
  • Full keyboard and focus behavior is documented in the Radix Accordion primitive.

Styling

Tailwind override: pass className to merge Tailwind classes with the component's CVA classes (via cn()):

<Accordion className="rounded-2xl border" />

Data slots and attributes: the component sets these for CSS targeting:

  • data-slot="accordion" on the root element, with data-appearance="<appearance>".
  • data-slot="accordion-item" on each item wrapper, with data-state="open" | "closed" and data-disabled (from Radix).
  • data-slot="accordion-trigger" on the <button>, with data-state and data-disabled.
  • data-slot="accordion-trigger-icon" on the optional leading icon wrapper.
  • data-slot="accordion-trigger-label" on the inner label/flex slot.
  • data-slot="accordion-trigger-chevron" on the trailing chevron SVG (rotates 180° on open).
  • data-slot="accordion-content" on the expanding panel, with data-state (drives the open/close keyframes).

Target a specific state in CSS:

[data-slot="accordion"][data-appearance="outline-rounded"]
  [data-slot="accordion-item"][data-state="open"] {
  /* … */
}
  • Tabs: use this when only one panel is ever visible and the labels act as primary navigation between sibling views.
  • Collapsible: single, standalone show/hide region without a list around it.
  • Dialog: modal overlay for content that should block the page (confirmations, multi-step forms).
  • Card: when the content should always be visible and doesn't need to expand or collapse.

API Reference

Accordion

The root container. Wraps Radix's Accordion.Root, broadcasts appearance to nested items, triggers, and content via context, and writes data-slot="accordion" plus data-appearance on the root. Extends React.ComponentProps<typeof Accordion.Root> from radix-ui, so all Radix props (type, collapsible, value, defaultValue, onValueChange, disabled, dir, orientation) are accepted.

Props

PropTypeDefaultDescription
type"single" | "multiple"-Whether one or many items can be open at a time. Required by Radix.
collapsiblebooleanfalseWhen type="single", allows the open item to be closed (no item open). Ignored otherwise.
valuestring | string[]-Controlled open value(s). string for single, string[] for multiple.
defaultValuestring | string[]-Uncontrolled initial open value(s).
onValueChange(value: string | string[]) => void-Fires when the open value changes.
disabledbooleanfalseDisables every item in the accordion.
dir"ltr" | "rtl""ltr"Reading direction; flips arrow-key navigation.
orientation"horizontal" | "vertical""vertical"Layout orientation; vertical is the default and what the visual styles are tuned for.
classNamestring-Tailwind classes merged with the component's CVA classes via cn().
childrenReact.ReactNode-One or more AccordionItem elements.

Variants

VariantOptionsDefaultDescription
appearance"ghost-default" "ghost-underline" "ghost-rounded" "outline-rounded" "outline-sharp" "filled-rounded""ghost-default"Visual treatment; cascades to every item, trigger, and content through context.

AccordionItem

A single disclosure row. Wraps Radix's Accordion.Item, owns the open-state surface (background, border, hover, focus ring), and writes data-slot="accordion-item". Extends React.ComponentProps<typeof Accordion.Item> from radix-ui.

Props

PropTypeDefaultDescription
valuestring-Unique identifier used by Radix to track open state and wire aria-controls. Required.
disabledbooleanfalseDisables this item; skipped by arrow-key navigation and exposes data-disabled.
classNamestring-Tailwind classes merged with the component's CVA classes via cn().
childrenReact.ReactNode-An AccordionTrigger followed by an AccordionContent.

AccordionTrigger

The clickable header. Wraps Radix's Accordion.Trigger inside Accordion.Header, renders the optional leading icon, a flex label slot, and the trailing chevron. Writes data-slot="accordion-trigger" plus inner slots accordion-trigger-icon, accordion-trigger-label, and accordion-trigger-chevron. Extends React.ComponentProps<typeof Accordion.Trigger> from radix-ui with one extra prop.

Props

PropTypeDefaultDescription
iconReact.ReactNode-Leading icon rendered before the label. Sized to size-5 automatically.
classNamestring-Tailwind classes merged with the component's CVA classes via cn().
childrenReact.ReactNode-Label content. Anything after a ml-auto element is pushed to the trailing edge.

AccordionContent

The expanding panel. Wraps Radix's Accordion.Content, applies the registered accordion-down / accordion-up keyframes, and ships a flex flex-col gap-4 inner wrapper so direct children stack with consistent spacing. Writes data-slot="accordion-content". Extends React.ComponentProps<typeof Accordion.Content> from radix-ui.

Props

PropTypeDefaultDescription
forceMountboolean-Keep the content mounted when closed (useful for animations or analytics). (advanced)
classNamestring-Tailwind classes merged on the inner content wrapper via cn().
childrenReact.ReactNode-Panel content. Paragraphs, alerts, action buttons, forms; anything React renders.