LOKE Design System
Components

Accordion

Collapsible content sections with keyboard navigation, single/multiple modes, and accessible semantics.

Accordion

The Accordion component organizes content into collapsible sections so users can reveal details progressively. It supports robust keyboard navigation, single or multiple open items, and accessible semantics by default.

Choose type="single" when only one item should be open at a time (optionally collapsible), or type="multiple" to allow several items open simultaneously.


Features

  • Single or multiple item expansion modes
  • Optional collapsible behavior for single mode
  • Full keyboard support (Arrow keys, Home/End, Enter/Space)
  • RTL direction and horizontal/vertical orientations
  • Accessible by default with proper roles and relationships
  • Composition-friendly primitives: Accordion, AccordionItem, AccordionTrigger, AccordionContent

Usage

import {
  Accordion,
  AccordionItem,
  AccordionTrigger,
  AccordionContent,
} from '@loke/design-system/accordion';

export default function Example() {
  return (
    <Accordion type="single" collapsible>
      <AccordionItem value="item-1">
        <AccordionTrigger>Is it accessible?</AccordionTrigger>
        <AccordionContent>
          Yes. It follows the WAI‑ARIA accordion pattern with correct roles and keyboard support.
        </AccordionContent>
      </AccordionItem>

      <AccordionItem value="item-2">
        <AccordionTrigger>Is styling customizable?</AccordionTrigger>
        <AccordionContent>
          Yes. Use <code>className</code> on any part to add utility classes or custom CSS.
        </AccordionContent>
      </AccordionItem>
    </Accordion>
  );
}

Tips:

  • Use type="single" when content is mutually exclusive; use type="multiple" to compare sections side-by-side
  • In single mode, set collapsible to allow closing the active item
  • Keep item value strings stable and unique per accordion

Props

Accordion

Prop

Type

AccordionItem

Prop

Type

AccordionTrigger

Prop

Type

AccordionContent

Prop

Type


Examples

Single (collapsible)

<Accordion type="single" collapsible>
  <AccordionItem value="item-1">
    <AccordionTrigger>Is it accessible?</AccordionTrigger>
    <AccordionContent>Yes. It adheres to the WAI‑ARIA pattern.</AccordionContent>
  </AccordionItem>
  <AccordionItem value="item-2">
    <AccordionTrigger>Can I style it?</AccordionTrigger>
    <AccordionContent>Use className on any sub‑component.</AccordionContent>
  </AccordionItem>
  <AccordionItem value="item-3">
    <AccordionTrigger>Can it be controlled?</AccordionTrigger>
    <AccordionContent>Use value/onValueChange props.</AccordionContent>
  </AccordionItem>
</Accordion>

Multiple items open

Open alongside others.

<Accordion type="multiple" defaultValue={["a"]}>
  <AccordionItem value="a">
    <AccordionTrigger>First</AccordionTrigger>
    <AccordionContent>Open alongside others.</AccordionContent>
  </AccordionItem>
  <AccordionItem value="b">
    <AccordionTrigger>Second</AccordionTrigger>
    <AccordionContent>Useful for comparing content.</AccordionContent>
  </AccordionItem>
  <AccordionItem value="c">
    <AccordionTrigger>Third</AccordionTrigger>
    <AccordionContent>Several sections can be expanded.</AccordionContent>
  </AccordionItem>
</Accordion>

Controlled (single)

Controlled state via value/onValueChange.

function ControlledExample() {
  const [value, setValue] = React.useState("one");
  return (
    <>
      <div className="flex gap-2">
        <Button onClick={() => setValue("one")}>Open One</Button>
        <Button onClick={() => setValue("two")} variant="secondary">Open Two</Button>
        <Button onClick={() => setValue("three")} variant="outline">Open Three</Button>
        <Button onClick={() => setValue("")} variant="ghost">Close All</Button>
      </div>
      <Accordion type="single" value={value} onValueChange={setValue} collapsible>
        {/* items */}
      </Accordion>
    </>
  );
}

Disabled

Item-level disabled:

Entirely disabled accordion:

<Accordion type="single" collapsible>
  <AccordionItem value="i-1">
    <AccordionTrigger>Enabled section</AccordionTrigger>
    <AccordionContent>Works normally.</AccordionContent>
  </AccordionItem>
  <AccordionItem value="i-2" disabled>
    <AccordionTrigger>Disabled section</AccordionTrigger>
    <AccordionContent>Cannot be expanded or focused.</AccordionContent>
  </AccordionItem>
</Accordion>

<Accordion type="single" collapsible disabled className="mt-4">
  <AccordionItem value="x-1">
    <AccordionTrigger>Completely disabled</AccordionTrigger>
    <AccordionContent>No items can be expanded.</AccordionContent>
  </AccordionItem>
</Accordion>

Nested accordions

<Accordion type="single" collapsible>
  <AccordionItem value="parent-1">
    <AccordionTrigger>Parent</AccordionTrigger>
    <AccordionContent>
      <Accordion type="multiple">
        {/* nested items */}
      </Accordion>
    </AccordionContent>
  </AccordionItem>
</Accordion>

Keyboard controls

  • Tab: Move focus through triggers
  • Enter/Space: Toggle the focused item
  • Home/End: Focus first/last trigger
  • Vertical orientation:
    • ArrowDown/ArrowUp: Next/previous trigger
  • Horizontal orientation:
    • ArrowRight/ArrowLeft: Next/previous trigger (respects RTL via dir)

Best practices

  • Keep trigger text concise and descriptive; it should clearly indicate the content behind it
  • Prefer type="single" for mutually exclusive sections, type="multiple" for comparison
  • Use collapsible in single mode if “close all” is desirable
  • Avoid placing interactive elements directly in triggers; keep them in content
  • For nested structures, keep levels shallow and clearly styled to preserve readability