Diwa Design System

Accordion

An animated, accessible panel that reveals or hides content when the user clicks its heading. Follows the controlled component pattern — the consumer manages open state.

When to use

Do

  • Use to organize large amounts of content in a structured, scannable way.
  • Group related content together within each panel.
  • Use clear, concise headings so users can quickly identify what is inside.
  • Choose a heading tag that fits the surrounding page outline (headingTag prop).
  • Use dense mode (compact) in sidebars, configuration panels, or data tables.

Don't

  • Don't hide content that is critical or required — users may not discover it.
  • Don't overuse accordions on a single page; too many create navigation fatigue.
  • Don't use ambiguous or vague headings — users must understand the content without expanding.
  • Don't overload individual panels with excessive content.
  • Don't add a manual divider above the first accordion — the component provides its own.

Controlled component pattern

diwa-accordion never mutates its own open prop. Instead it emits an update event with { open: boolean } representing the requested new state. The consumer must reflect that value back onto the open prop. This pattern gives the consumer full control: you can intercept, delay, or veto state transitions.

// Vanilla JS
const el = document.querySelector('diwa-accordion');
el.addEventListener('update', (e) => { el.open = e.detail.open; });

// React (JSX) — note lowercase onupdate for custom element event mapping
// See: https://react.dev/reference/react-dom/components/common#custom-html-elements
<diwa-accordion
  heading="FAQ item"
  open={isOpen}
  onupdate={(e) => setIsOpen(e.detail.open)}
/>

Exclusive group (accordion accordion)

To make only one panel open at a time, track a single active index and close all others when a panel is opened. Because each accordion is independent, this is entirely handled in user-land without any group container prop:

const [activeIndex, setActiveIndex] = useState<number | null>(null);

items.map((item, i) => (
  <diwa-accordion
    key={i}
    heading={item.heading}
    open={activeIndex === i}
    onupdate={(e) => e.detail.open ? setActiveIndex(i) : setActiveIndex(null)}
  >
    {item.content}
  </diwa-accordion>
))

Heading tag

The headingTag prop wraps the toggle button in a semantic heading element. Set it to match the heading hierarchy at the point the accordion appears in the document. If the nearest ancestor section heading is h2, use headingTag="h3". The default is h2, which is appropriate for top-level FAQ or content sections.

Dense mode (compact)

The compact prop enables dense mode and reduces header padding from 20px 0 to 4px 0 and the font from the lg to base scale. Use it in space-constrained contexts such as sidebars, inspection panels, or nested accordion groups.