motif svelteArc

Accordion ported

Progressively reveal supporting information in place.

A single-open, collapsible list of question and answer rows built on Bits UI with spring-animated panel disclosure.

Live specimen

Loading demo…

Guidance

When to use

  • FAQ sections where only one answer should be open at a time.
  • Settings or help pages that group long content under short, scannable questions.
  • Page-level FAQs that need larger type, via size="lg".

When not to use

  • Use Expandable Card for a single standalone disclosure such as a plan or order summary.
  • Use Tabs when sections are peer views that people switch between.
  • Use Onboarding Checklist when the rows are setup tasks to complete.

Installation

pnpm dlx shadcn-svelte@latest add @arcui/accordion

Usage

<script lang="ts">
  import Accordion from '$lib/components/accordion/accordion.svelte';

  const items = [
    { title: 'Can I cancel anytime?', content: cancelAnswer },
    { title: 'Do you offer refunds?', content: refundAnswer }
  ];
</script>

<Accordion size="lg" {items} />

{#snippet cancelAnswer()}
  <p>Yes. Your plan stays active until the billing period ends.</p>
{/snippet}

{#snippet refundAnswer()}
  <p>Within 14 days of purchase, no questions asked.</p>
{/snippet}

Variants & Examples

Default (size="md")

Compact row height with standard question typography for cards or sidebars.

<Accordion {items} />

Large (size="lg")

Expanded row height with large question headings and a 62ch max-width for page-level FAQs.

<Accordion {items} size="lg" />

All Closed on First Render

Pass defaultOpen={-1} to start with all items collapsed.

<Accordion {items} defaultOpen={-1} />

API Reference

PropTypeDefaultDescription
items{ title: string; content?: Snippet }[]—Ordered rows. The title is the trigger button label; content is a Svelte 5 Snippet rendering the panel body.
defaultOpennumber0Index of the row open on first render. Pass -1 to start with every row closed.
size'md' | 'lg''md'"lg" sets questions at the large text size for page-level FAQs with responsive padding.

Keyboard Interactions

KeyAction
Enter / SpaceToggles the focused accordion row.
ArrowDownMoves focus to the next accordion trigger.
ArrowUpMoves focus to the previous accordion trigger.
HomeJumps focus directly to the first trigger.
EndJumps focus directly to the last trigger.

Accessibility

  • Bits UI wires aria-expanded, aria-controls, and region labelling between each trigger and panel.
  • Closed panels stay mounted in the DOM but switch to visibility: hidden once collapsed, cleanly leaving the accessibility tree.
  • Triggers sit inside h3 heading elements (level 3, matching the React original); the chevron icon is aria-hidden="true".
  • Keyboard navigation conforms to the WAI-ARIA Accordion design pattern with automatic looping/boundary handling.

Motion

Phase 1 still-state: closed panels hold at height 0 / opacity 0 / visibility hidden while staying mounted; the chevron sits at its rotation end value (0deg vs 180deg). Phase 2 wires the non-overshooting height spring, blur settle (slides down 6px out of a subtle blur), and snappy icon spring via @humanspeak/svelte-motion; mid-flight toggles retarget smoothly without restarting. Reduced-motion applies the same end states instantly in one step.

Notes for AI

  • Use for FAQs and settings groups where only one section should be open. Use expandable-card for single disclosures and tabs when sections are peers.
  • Content is passed via Svelte 5 snippets ({#snippet answer()}...{/snippet}) rather than ReactNode.
  • Pass defaultOpen={-1} if you need all accordion items to be collapsed on initial render.
  • Only single-expand mode is supported by design to keep disclosure focused.

Source of truth: registry/components/accordion/accordion.tsx