# Accordion

**When to use:** Collapsible sections for progressive disclosure — FAQs, settings panels, long-form content that users expand on demand.

**Keywords:** collapse, dropdown panel

**Import:** `import { Accordion, AccordionStyleType, ThemeColor } from '@ahrowe/ui'`

**Styles:** the library's own, meaning what they mean on Button, Badge and Chip, applied to the
header; `color` is the colour they use (a `ThemeColor`, default `Neutral`, or any CSS colour, see
[ThemeColor.md](ThemeColor.md)). The body is ordinary text on the page in every style. There is no
`Filled`: an accordion structures content, and a solid header would compete with the page's main
action.

- `AccordionStyleType.Outlined` (default): a border in the colour, the header's text in the colour
- `AccordionStyleType.Light`: the header in a light tint of the colour, its text in the colour
- `AccordionStyleType.Plain`: the header's text in the colour, no chrome and no padding, for a disclosure inside other content

Each accordion is one section; render several directly next to each other for a list, without a
wrapper around each one, and the arrow keys move between their headers. **Neighbours** keep a
small gap by default. With `joined` they make one block instead: one line between each, in the
border colour for Outlined and in the page colour for Light, open or closed. Two neighbours join
only when both are joined.

```tsx
import { Accordion, AccordionStyleType, ThemeColor } from '@ahrowe/ui';

// Uncontrolled — manages its own open state
<Accordion title="Section title">
  <p>Content goes here</p>
</Accordion>

// A list of separate sections
{faqs.map((faq) => (
  <Accordion key={faq.id} title={faq.question}>
    {faq.answer}
  </Accordion>
))}

// The same list as one bordered control
{faqs.map((faq) => (
  <Accordion key={faq.id} title={faq.question} joined>
    {faq.answer}
  </Accordion>
))}

// Headers tinted in the primary colour
{steps.map((step) => (
  <Accordion key={step.id} title={step.title} styleType={AccordionStyleType.Light} color={ThemeColor.Primary}>
    {step.body}
  </Accordion>
))}

// Controlled
<Accordion
  id="section-1"
  title="Controlled section"
  expanded={isOpen}
  onHeaderClicked={(id, isExpanded) => setIsOpen(isExpanded)}
  styleType={AccordionStyleType.Light}
>
  Content
</Accordion>
```

**Key props:**

| Prop | Type | Description |
|------|------|-------------|
| `title` | `string` | Header text |
| `expanded` | `boolean \| null` | Controlled open state |
| `startOpen` | `boolean` | Uncontrolled initial state |
| `onHeaderClicked` | `(id, isExpanded) => void` | Toggle callback |
| `styleType` | `AccordionStyleType` | `Outlined` (default), `Light` or `Plain` |
| `color` | `ColorValue` | A `ThemeColor` (default `Neutral`) or any CSS colour |
| `joined` | `boolean` | Joins the section to joined neighbours into one block. Default `false`: a small gap |
| `hideHeader` | `boolean` | Hide the clickable header |
| `id` | `string` | Passed back in `onHeaderClicked` |

## Accessibility

The header is a button: it takes focus, toggles on Enter and Space, and reports the section's
state through `aria-expanded`. It names the body it controls with `aria-controls`, and the body is
a `role="region"` labelled by the header, so a screen reader can move between the two.

An activation key pressed inside the body does not reach the header, so a Space typed into a
field in an open section types a space instead of collapsing the section.

**Arrow keys**, the optional keys of the
[WAI-ARIA accordion pattern](https://www.w3.org/WAI/ARIA/apg/patterns/accordion/): Down and Up
move the focus to the next and previous header and wrap at the ends, Home and End to the first and
last. They move among the accordions rendered beside each other under one parent, so a list nested
inside a section's body is a set of its own, and a key pressed inside a body stays there.

The pattern also wraps each header in a `role="heading"` with an `aria-level` that fits the page;
this header is not, because only the page knows that level.

**Slots:** `root` `header` `title` `icon` `body` `bodyInner`

## Sizing

The header's font, padding and chevron follow `--default-font-size`, as does the body's padding. There is no fixed height: the header is that padding plus one line of text, so a larger base font simply makes it taller.

**Theming:** every colour comes from `color`, through the variables listed in
[ThemeColor.md](ThemeColor.md); in `Neutral` that is the surrounding text colour and a
`--border-color` outline. The header tints on hover in its own text colour and shows a
`--focus-border-color` ring inside its edge on keyboard focus.

## Migration from `Default`, `Primary` and `Plain`

Before 0.43 the styles were `Default` (bordered sections that moved apart when one opened),
`Primary` (borderless until open, then framed in `--primary-color`) and `Plain`. The styles are
now the library's shared ones, and `Default` and `Primary` are gone, so TypeScript points at each
use:

| Before | After |
|--------|-------|
| `styleType={AccordionStyleType.Default}` | `joined`: `Outlined` is the default style, and `joined` keeps neighbours in one box |
| `styleType={AccordionStyleType.Primary}` | `color={ThemeColor.Primary}`: Outlined, bordered in the primary colour |
| `styleType={AccordionStyleType.Plain}` | unchanged, with a small gap added above its body |

An Accordion without `styleType` compiles unchanged and looks different in a list: neighbours
used to join into one box and now keep a small gap. Add `joined` to every one in the list to keep
the box, now with square corners at each join; an open one stays in the box instead of moving
apart from the others.
