Pho Design System

Accordion

A stack of headers that each reveal a section of content — most often an FAQ. Built on Base UI Accordion. Single-open by default; one section expands as another collapses.

Import

import { Accordion } from "@photon-ai/pho-ui/components/accordion";

Basic

Each Accordion.Item carries a value. Set the initially-open items with defaultValue (an array), or drive the open set with value / onValueChange. The chevron in the trigger rotates when its panel is open.

Photon's design system — accessible React components on Base UI with the Photon brand baked in.

import { IconChevronDown } from "@tabler/icons-react";

<Accordion.Root defaultValue={["what"]}>
  <Accordion.Item value="what">
    <Accordion.Header>
      <Accordion.Trigger>
        What is Pho?
        <IconChevronDown />
      </Accordion.Trigger>
    </Accordion.Header>
    <Accordion.Panel>
      <div className="pb-4">…</div>
    </Accordion.Panel>
  </Accordion.Item>
</Accordion.Root>;

Multiple open

By default one panel opens as another closes. Set multiple to let several stay open at once — defaultValue (and value) then hold every open item.

Photon's design system — accessible React components on Base UI with the Photon brand baked in.

Base UI gives unstyled, accessible primitives; Pho layers the spectrum-blue, squircle, PolySans look on top.

<Accordion.Root multiple defaultValue={["what", "base-ui"]}>
  {/* items */}
</Accordion.Root>

Anatomy

Props

Set on Accordion.Root:

Set on Accordion.Item:

Accordion.Trigger renders a native <button>; Accordion.Panel accepts keepMounted and hiddenUntilFound to override the Root defaults per item.

Accessibility

Each Accordion.Header renders an <h3> that wraps the Accordion.Trigger <button>. The trigger carries aria-expanded and aria-controls pointing at its panel; the Accordion.Panel is a role="region" labelled by its trigger via aria-labelledby. Keep the surrounding heading levels sensible — if <h3> is wrong for the page, re-render the header as another heading with render.

Keyboard:

Base UI follows the updated APG guidance that removed roving focus, so triggers sit in the normal tab order — there is no arrow-key navigation between headers. A collapsed panel is removed from the accessibility tree (and the DOM, unless keepMounted or hiddenUntilFound is set).

Best practices