Skip to main content
Graphite UI

Accordion

A vertically stacked set of headings that each reveal a section of content. Use it to shorten a long page, never to hide information a reader needs to complete the task in front of them.

Contract 1.1.0Kit · 5 sets · 141 variantsOpen in Figma

Live preview

Rendered by the component itself from the same generated tokens as the rest of the site.

Thirty-six components carry a versioned contract and are checked against the Figma kit on every build.

Not for the components. The site still leans on Carbon for a few pieces of chrome, and moving off it is a tracked migration.

One source color becomes the ramps and the roles. Change it in the header and the page repaints.

Installation

Install it with the shadcn CLI, along with everything it uses. New project? Set it up first, as Quick start shows.

npx shadcn@latest add https://www.graphite-ui.com/r/accordion.json
import {
  Accordion,
  AccordionItem,
  AccordionTrigger,
  AccordionContent,
} from '@/components/ui/accordion'

Anatomy

Slots come from the contract, so this list and the code cannot disagree about what the component is made of.

The panel. It is labelled by its own trigger, so a screen reader announces the pair rather than an orphaned region.
  1. TriggerRequiredThe heading. It is a button, and it owns the expanded state.
  2. IndicatorRequiredRotates to show state. Never the only signal that a panel is open.
  3. PanelRequiredThe revealed region, labelled by its own trigger.
  4. Panel controlOptionalOne instance-swap slot inside the panel, for a control the content needs.

Variants

Three axes travel from the kit into the code as props. Size, alignment and flush are all one-word choices; everything else about an accordion is composition.

Size: Small

Panel content.
Size: Large

Panel content.
Alignment: Left

Panel content.
Flush: True

Panel content.

States

These are variant axes in the kit and pseudo-classes in the code. A State=Hover variant is a picture of a behaviour, not an instruction to add a hover prop, so the page shows them as a matrix rather than as props.

Enabled

Panel content.
Hover

Panel content.
Focus

Panel content.
Disabled

Panel content.

API reference

Generated from the contract, versioned with it, and checked by drift-check. This table cannot describe props the component does not have.

Accordion props
PropTypeDefaultNotes
type'single' | 'multiple''single'Whether one panel or several may be open at once.
collapsiblebooleanfalseLets the open panel close again, leaving none open. Only meaningful for single; a multiple accordion can always close every panel.
size'sm' | 'md' | 'lg''md'The kit's Small, Medium and Large.
flushbooleanfalseDrops the outer rules and the horizontal inset, so the list sits flush inside a container that already has its own edge.
align'left' | 'right''right'Which edge the indicator sits on.
classNamestring—Merged after the variant recipe, so a caller can extend without forking.

Design tokens

Every swatch is live. Change the source color in the header and this table repaints, because it reads the same roles the component does.

  • on-surfaceTrigger label, panel copy and the indicator glyph, all Text/text-primary and Icon/icon-primary in the kit.
  • outlineThe rule between items and the outer rule when flush is false, through its subtle step (the kit's Border/border-subtle-00).
  • elevationThe trigger's hover fill, elevation-02 (the kit's Layer/layer-hover-01). The item itself is transparent.
  • primaryThe focus ring on the trigger, through the page-level focus variable, and the disabled title, copy and indicator, through its disabled-content step.
  • spacingTrigger and panel padding.
  • textTrigger label and panel copy, both Body/3 at every size; size changes the trigger height only.
  • motionThe panel's open and close transition, on the settle curve.

Usage

The contract's prohibitions, written as the choices you will actually face.

Do

  • Let a reader open more than one panel when the panels are independent.
  • Keep the trigger a real button, so Enter and Space both work.
  • Use flush when the list already sits inside something with a border.
  • Pair the indicator with a change the reader can also feel in layout.

Don’t

  • Hide anything the reader needs to finish the task in front of them.
  • Nest an accordion inside an accordion. Two levels of disclosure is a navigation problem wearing a component.
  • Animate the panel on a hardcoded duration. The motion tokens exist, so bind them.
  • Let the indicator carry the open state on its own.

Accessibility

What the component does for you, and what it leaves to you.

Keyboard
Tab moves between triggers. Enter and Space both toggle the panel the focus is on. Nothing traps focus inside a panel.
Roles
Each trigger is a button carrying aria-expanded and aria-controls; each panel is labelled by its own trigger.
Focus
The focus ring is --graphite-focus and is never removed, only moved.
Contrast
Trigger label against surface is measured at the theme’s target, AA or AAA, and the pairing is checked rather than reviewed.
Motion
The open transition respects prefers-reduced-motion and falls back to an instant change.

Figma parity

The kit's Accordion page ships 141 variants across 5 sets, and 120 of them are the one Accordion item set. The code exposes 6 props. This table is where those two facts are reconciled instead of quietly diverging.

Kit axes and how they map to code
Kit axisValuesCodeHow it maps
SizeSmall · Medium · LargesizeOne to one.
AlignmentRight · LeftalignOne to one.
FlushFalse · TrueflushOne to one.
ExpandedFalse · True—Runtime state, not a prop. The kit draws it because Figma has no other way to show it.
StateEnabled → Skeleton—Pseudo-classes in code. Governance rule 7: a State=Hover variant is not an instruction to add a hover prop. Hover fills elevation-02, the kit’s layer-hover-01. Disabled dims the title, copy and chevron; the kit leaves the chevron at full strength, which the code treats as a slip. Skeleton has no counterpart: nothing in an accordion loads asynchronously.
SlotBoolean + swapchildrenComposition. The caller passes content instead of choosing from a fixed pair.