SvAccordion

A WAI-ARIA accordion of collapsible sections, with single- or multiple-expand and roving header focus.

SvAccordion stacks labelled sections whose bodies are rendered through a panel snippet. It is controlled - you own the open ids via expanded and onChange - and every color comes from the grid's --sg-* tokens so it matches the rest of the kit in light and dark. The expand logic, keyboard handling, and ARIA wiring live in a headless createAccordion core; this component is one styled renderer.

Basic usage

<script lang="ts">
  import { SvAccordion, type AccordionItem } from '@svgrid/grid'
  const items: AccordionItem[] = [
    { id: 'ship', label: 'Shipping' },
    { id: 'pay', label: 'Payment' },
    { id: 'gift', label: 'Gift options', disabled: true },
  ]
  let expanded = $state(['ship'])
</script>

<SvAccordion {items} {expanded} onChange={(ids) => (expanded = ids)}>
  {#snippet panel(item)}
    <p>Details for {item.label}.</p>
  {/snippet}
</SvAccordion>

Props

Prop Type Default Description
items ReadonlyArray<AccordionItem> - The section items. See AccordionItem.
expanded string[] [] Expanded item ids (bindable). Controlled via onChange.
onChange (expandedIds: string[]) => void - Fired when the set of open sections changes.
expandMode AccordionExpandMode (single | multiple) single single keeps at most one open; multiple allows any.
disabled boolean false Disables the whole accordion.
dir EditorDir (ltr | rtl | auto) - Text direction. rtl mirrors layout and the chevron.
panel Snippet<[AccordionItem]> - Renders a section body; receives the item.

AccordionItem

type AccordionItem = { id: string; label: string; disabled?: boolean }
type AccordionExpandMode = 'single' | 'multiple'

Patterns

Multiple open sections

Set expandMode="multiple" for a settings-style list where several panels can be open at once:

<SvAccordion {items} {expanded} expandMode="multiple"
  onChange={(ids) => (expanded = ids)}>
  {#snippet panel(item)}<Section id={item.id} />{/snippet}
</SvAccordion>

FAQ list

The default single mode gives a classic FAQ where opening one answer collapses the last - keep expanded in $state and mirror it in onChange.

Filter sidebar

Group a long filter set into collapsible sections that can stay open together with expandMode="multiple", and seed which groups start open:

<script lang="ts">
  import { SvAccordion, type AccordionItem } from '@svgrid/grid'
  const groups: AccordionItem[] = [
    { id: 'price', label: 'Price' },
    { id: 'brand', label: 'Brand' },
    { id: 'rating', label: 'Rating' },
  ]
  let expanded = $state(['price', 'brand'])
</script>

<SvAccordion items={groups} {expanded} expandMode="multiple"
  onChange={(ids) => (expanded = ids)}>
  {#snippet panel(item)}
    {#if item.id === 'price'}<PriceRange />
    {:else if item.id === 'brand'}<BrandChecklist />
    {:else}<RatingFilter />{/if}
  {/snippet}
</SvAccordion>

Tip: disabled items are skipped by keyboard navigation and cannot be toggled, so mark a section disabled (rather than dropping it) to keep its place in the list while its data loads.

Accessibility

See also