Skip to content

Accordion

Collapsible content sections with single or multiple open items. Supports controlled and uncontrolled modes.

Preview

Installation

$pnpm exec dgadd add ui/accordion
[Installs to]src/components/ui/accordion[Item]ui/accordion

dgadd is not public on npm yet. Until the first release, pack @diffgazer/add from the repository and install that tarball into this app, which is what puts dgadd on pnpm exec.

UI components require Tailwind CSS v4. Local copy mode imports src/styles/styles.css; package mode uses @diffgazer/ui CSS once packages are available.

Usage

tsx
import {  Accordion,  AccordionContent,  AccordionHeader,  AccordionItem,  AccordionTrigger,} from "@/components/ui/accordion";import { InlineCode } from "@/components/ui/code-block";export default function AccordionDefault() {  return (    <Accordion defaultValue="item-1">      <AccordionItem value="item-1">        <AccordionHeader>          <AccordionTrigger>What is @diffgazer/ui?</AccordionTrigger>        </AccordionHeader>        <AccordionContent>          <p className="text-sm text-muted-foreground">            A terminal-inspired component registry for React. Install components via CLI and own the            source.          </p>        </AccordionContent>      </AccordionItem>      <AccordionItem value="item-2">        <AccordionHeader>          <AccordionTrigger>How do I install components?</AccordionTrigger>        </AccordionHeader>        <AccordionContent>          <p className="text-sm text-muted-foreground">            Run <InlineCode>dgadd add button</InlineCode> to add a component to your project.          </p>        </AccordionContent>      </AccordionItem>      <AccordionItem value="item-3">        <AccordionHeader>          <AccordionTrigger>Can I customize components?</AccordionTrigger>        </AccordionHeader>        <AccordionContent>          <p className="text-sm text-muted-foreground">            Yes. Components are copied into your project as source files. Modify them freely.          </p>        </AccordionContent>      </AccordionItem>      {/* Disabled items dim their trigger and are skipped by arrow navigation. */}      <AccordionItem value="item-4" disabled>        <AccordionHeader>          <AccordionTrigger>Enterprise support (disabled)</AccordionTrigger>        </AccordionHeader>        <AccordionContent>          <p className="text-sm text-muted-foreground">Not available on this plan.</p>        </AccordionContent>      </AccordionItem>    </Accordion>  );}

Examples

Multiple Open

Preview

Custom Handle

Preview

Source Variant

Preview

API Reference

Accordion

NameTypeDefaultDescription
type"single" | "multiple""single"Single allows one open item; multiple allows several open at once. Switches the value/onChange/defaultValue shape from string to string[].
valuestring | undefined | string[]Controlled open value(s). In single mode, undefined means no item is open. In multiple mode, undefined is normalized to an empty array while remaining controlled.
defaultValuestring | string[]Initial open value(s) for uncontrolled mode.
onChange((value: string | undefined) => void) | ((value: string[]) => void)Fired when the open value(s) change. Single mode emits undefined when no item is open; multiple mode emits an array.
collapsiblebooleantrue (single mode)Single mode only. When false, the currently open item cannot be closed by clicking it.
childrenrequiredReactNodeAccordionItem children.

Accordion.Item

NameTypeDefaultDescription
valuerequiredstringStable identifier matched against the Accordion value.
disabledbooleanfalseDisables the item: trigger is not focusable and not toggleable.
childrenrequiredReactNodeHeader and Content children.

Accordion.Header

NameTypeDefaultDescription
as"h2" | "h3" | "h4" | "h5" | "h6""h3"Heading level wrapping the trigger.
childrenrequiredReactNodeTypically an Accordion.Trigger.

Accordion.Trigger

NameTypeDefaultDescription
variant"default" | "source""default"Visual style. Source is a smaller variant used for inline source toggles.
headingLevel"h2" | "h3" | "h4" | "h5" | "h6""h3"Heading level wrapping the trigger button (APG heading requirement). Ignored when the trigger is composed inside an AccordionHeader, which then owns the heading.
handleReactNode | null<Chevron open={isOpen} size="sm" />Custom handle element. Pass null to hide the chevron entirely.
childrenrequiredReactNodeTrigger label.

Accordion.Content

NameTypeDefaultDescription
regionbooleanfalseOpt in to role="region" with aria-labelledby pointing at the trigger while the panel is open. Off by default per the APG accordion pattern (region is listed as optional). Enable for a small number of substantive panels (typically six or fewer); leave it off for many short items to avoid landmark noise.
childrenrequiredReactNodeCollapsible body content.

Data attributes

AttributeApplies toValuesDescription
data-stateAccordion.Item / Accordion.Trigger / Accordion.Content"open" | "closed"Open state for item, trigger, and panel styling.
data-disabledAccordion.Item / Accordion.Triggerpresent when disabledMarks disabled accordion items and triggers.
data-valueAccordion.Triggeritem valueStable item value used by navigation and open-state lookup.

Accessibility

Keyboard Navigation

Built-in keyboard navigation via @diffgazer/keys navigation helpers. Arrow Up/Down moves focus between accordion triggers, Home/End jumps to first/last, Enter/Space toggles the focused item.

KeyAction
ArrowUp / ArrowDownMoves focus to the previous or next enabled trigger.
Home / EndMoves focus to the first or last enabled trigger.
Enter / SpaceToggles the focused trigger.

Default (with keyboard support)

Preview

Notes

Requires @diffgazer/keys (package mode)

Accordion's arrow-key trigger navigation imports from the required @diffgazer/keys peer. Package consumers must install @diffgazer/keys with @diffgazer/ui; packages are not yet published to npm, so until the first release install both from locally packed tarballs. Importing @diffgazer/ui/components/accordion without keys fails at module load with an error naming the missing @diffgazer/keys package. Copy/dgadd consumers do not need the package — copy mode rewrites the keyboard helpers to local source.

Single vs Multiple

In single mode (default), only one item can be open. Set type='multiple' to allow several items open at once.

Collapsible

In single mode, collapsible defaults to true. With collapsible={false}, an accordion may still initialize with no open item, but once an item is selected, activating it again cannot close it.

Heading semantics

The APG accordion pattern requires each trigger to sit inside a heading. AccordionTrigger wraps itself in a default h3; set its headingLevel prop to match the surrounding document outline. Compose an explicit AccordionHeader to control the level instead — the trigger then skips its own wrapper so no doubled heading appears.

Keyboard Navigation

Arrow Up/Down moves focus between triggers. Home/End jumps to first/last trigger. Enter or Space toggles the focused item. Navigation wraps around.

Region role (opt-in)

AccordionContent accepts a region prop. When set, the open panel exposes role="region" with aria-labelledby pointing at its trigger. The APG accordion pattern lists this role as optional, so the default is off. Enable it for a small number of substantive panels (typically six or fewer) where each panel functions as its own landmark; leave it off when an accordion has many short items, since extra landmarks add noise to assistive-technology rotors.

Region role

AccordionContent accepts a region prop that opts the open panel into role="region" with aria-labelledby pointing at its trigger. The default is false.

tsx
<Accordion.Content region>{children}</Accordion.Content>

The APG accordion pattern lists this role as optional ("MAY"), which is why the prop is opt-in rather than the default:

  • Enable it when each panel functions as its own landmark and the accordion has a small number of substantive sections (typically six or fewer). Assistive-technology users can then jump straight to a panel via the landmark rotor.
  • Leave it off when an accordion has many short items or stacks alongside other accordions on the page. Extra landmarks add noise to the rotor and dilute the value of true page-level regions.

Behavior:

  • The role is only exposed while the panel is open. Collapsed panels never expose role="region".
  • The accessible name comes from the trigger via aria-labelledby; no extra label markup is required on the panel.
  • All other a11y wiring (aria-expanded, aria-controls, native <button> semantics, inert on collapsed content) is unconditional and unaffected by this prop.

Source

Install via CLI: pnpm exec dgadd add ui/accordion. Keyboard hooks are included as standalone copies. For the full experience, use --integration keys.

Highlighted source loads after this disclosure opens. Browse the source repository.