Accordion
Collapsible sections with one or many open at a time.
A vertically stacked set of collapsible sections. Each item is a heading-wrapped `<button>` toggle (label + optional hint + a chevron that rotates 180° on open) over an animated content `region`. Single-open by default; `multiple` lets several stay open. Works uncontrolled (`defaultValue`) or controlled (bindable `value` + `onChange`). The reveal uses the `--ss-dur` / `--ss-ease` motion tokens and collapses instantly under `prefers-reduced-motion`.
Demo
Usage
<script>
import { Accordion } from 'dssoca';
const items = [
{ id: 'shipping', label: 'Shipping' },
{ id: 'returns', label: 'Returns', hint: '30 days' },
];
</script>
<Accordion {items} defaultValue="shipping">
{#snippet panel(item)}
<p>Content for {item.label}.</p>
{/snippet}
</Accordion>Each header is a real `<button>` with `aria-expanded` + `aria-controls`; the panel is a `role="region"` labelled by its header (`aria-labelledby`). Enter/Space toggle; ArrowUp/ArrowDown move between headers (wrapping), Home/End jump to the first/last. Disabled sections use `aria-disabled` so they stay reachable by keyboard but do not toggle. The toggle chevron is the shared `Icon` `chevron` glyph (DS-0110) — `aria-hidden`, inside the header button — rotating 180° on open; the Accordion resolves its size once and passes it explicitly to that Icon (DS-0111), so a sm/md/lg Accordion gets a matching chevron, text, and spacing. The panel content is laid out as an always-padded block inside the grid-rows reveal clip, so it animates open as a stable padded block with no mid-animation reflow (DS-0115). Use `overflow="truncate"` to keep a long header label on a single line with an ellipsis (default `wrap`); a custom `header` snippet owns its own overflow handling. Zero border-radius; padding/typography scale with the size axis. WCAG 2.2 AA.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
items | AccordionItem[] | — | Required. Sections — each `{ id, label, hint?, disabled? }`. |
panel | Snippet<[AccordionItem]> | — | Required. Renders the content for each section; receives the item. |
header | Snippet<[AccordionItem]> | — | Optional custom header content (replaces the default label + hint). |
multiple | boolean | false | Allow several sections open at once; otherwise opening one closes the others. |
value | string | string[] | — | Controlled, bindable open state — an id (single) or list of ids (`multiple`). |
defaultValue | string | string[] | — | Initial open section(s) when uncontrolled. |
onChange | (value) => void | — | Fired with the new open value whenever a section toggles. |
overflow | 'truncate' | 'wrap' | 'wrap' | How a long header label behaves when it exceeds the available width. `wrap` (default) flows onto multiple lines; `truncate` keeps it on a single line with an ellipsis. Applies to the default header markup only. |
headingLevel | 1 | 2 | 3 | 4 | 5 | 6 | 3 | Heading level wrapping each header button (document outline). |
idBase | string | — | Base id used to namespace the generated header/panel ids. Defaults to a stable per-instance id (from `$props.id()`) so server- and client-rendered IDs match (SSR-safe). |
size | 'sm' | 'md' | 'lg' | — | Per-instance size override; inherits the ancestor `data-size-variant` when unset. |