Accordion
A disclosure component using native <details>/<summary> elements, supporting single and multiple open modes with keyboard navigation. Item titles accept plain strings or snippets.
Import
import { Accordion } from "@hyzer-labs/ui"Demo
Panels ship no display, height, or overflow of their own, so you can animate the open and close
yourself. The reference theme's summary-icon rotation honors --hz-duration-* / --hz-ease-*. See Motion for the token values
and the @hyzer-labs/ui/motion script-side helpers built on them.
What is @hyzer-labs/ui?
It is a Svelte component library that prioritizes behavior, structure, and accessibility.
Is it accessible?
Every component ships its WAI-ARIA pattern, and the token engine grades each color pairing against WCAG AA.
Can I use it with React?
No. It is Svelte 5 only.
<!-- The panel snippet receives the item: render per-item content -->
<Accordion items={faqs}>
{#snippet panel(item)}
<p>{answers[item.id]}</p>
{/snippet}
</Accordion>Install the package
pnpm add @hyzer-labs/ui. Svelte 5 is the only peer dependency.
Import the tokens
Import '@hyzer-labs/ui/tokens.css' once at your app's root layout.
Add the reference theme
Optionally add '@hyzer-labs/ui/theme' for the styled starting point.
<!-- defaultOpen seeds the initial open set (array in 'multiple' mode) -->
<Accordion type="multiple" items={steps} defaultOpen={['install', 'tokens']}>
{#snippet panel(item)}
<p>{instructions[item.id]}</p>
{/snippet}
</Accordion>Free plan
Panel for the free plan.
Pro plan new
Panel for the pro plan.
<!-- title accepts string | Snippet: use a snippet for inner markup -->
{#snippet proTitle()}
Pro plan <span class="badge">new</span>
{/snippet}
<Accordion items={[{ id: 'free', title: 'Free plan' }, { id: 'pro', title: proTitle }]}>
{#snippet panel(item)}…{/snippet}
</Accordion>What is @hyzer-labs/ui?
It is a Svelte component library that prioritizes behavior, structure, and accessibility.
Is it accessible?
Every component ships its WAI-ARIA pattern, and the token engine grades each color pairing against WCAG AA.
Can I use it with React?
No. It is Svelte 5 only.
<Accordion {items}>
{#snippet icon()}<IconPlus size={16} />{/snippet}
{#snippet panel(item)}…{/snippet}
</Accordion>With collapsible={false} in single mode, you cannot close the open panel.
One section is always expanded.
What is @hyzer-labs/ui?
It is a Svelte component library that prioritizes behavior, structure, and accessibility.
Is it accessible?
Every component ships its WAI-ARIA pattern, and the token engine grades each color pairing against WCAG AA.
Can I use it with React?
No. It is Svelte 5 only.
<!-- One panel always stays open in single mode -->
<Accordion items={faqs} collapsible={false} defaultOpen="what">
{#snippet panel(item)}…{/snippet}
</Accordion>Open: none
Install the package
pnpm add @hyzer-labs/ui. Svelte 5 is the only peer dependency.
Import the tokens
Import '@hyzer-labs/ui/tokens.css' once at your app's root layout.
Add the reference theme
Optionally add '@hyzer-labs/ui/theme' for the styled starting point.
<Accordion type="multiple" items={steps} onToggle={(ids) => (openIds = ids)}>
{#snippet panel(item)}…{/snippet}
</Accordion>Props
| Name | Type | Default | Note |
|---|---|---|---|
items | AccordionItem[] | — | Required. See AccordionItem below. |
type | 'single' | 'multiple' | 'single' | |
defaultOpen | string | string[] | [] | |
collapsible | boolean | true | false keeps one panel open in single mode. |
headingLevel | 2 | 3 | 4 | 5 | 6 | 3 | |
panel | Snippet<[AccordionItem]> | — | Required. Renders each panel. |
icon | Snippet | — | Replaces the default chevron. |
onToggle | (openIds: string[]) => void | — | |
class | string | — | Merged after the hz-accordion class. |
AccordionItem
| Name | Type | Default | Note |
|---|---|---|---|
id | string | — | Required. Must be unique. |
title | string | Snippet | — | Required. String for plain text; snippet for inner markup. |
disabled | boolean | false |
Theme hooks
What this component promises your CSS. The reference theme styles exactly these — from @layer hz-theme, so your unlayered rules win. See Styling Components for the how.
Root class: .hz-accordion
Data attributes
| Hook | Values | Styles |
|---|---|---|
data-state | 'open' | 'closed' | On .hz-accordion-item; rotates the icon. |
data-type | 'single' | 'multiple' | Mirrors the prop. The behaviour is native <details> grouping, not CSS, so nothing in the theme reads this — it is yours. |
[aria-disabled] | 'true' | Disabled styling hangs off aria-disabled on .hz-accordion-trigger — not off the data-disabled that also appears on the item. Tabs does the opposite; match the component you are styling. |
Part classes
| Hook | Values | Styles |
|---|---|---|
.hz-accordion-item | child element | The <details> element. |
.hz-accordion-trigger | child element | The <summary>. |
.hz-accordion-heading | child element | The heading inside the summary, at your headingLevel. |
.hz-accordion-icon | child element | The disclosure chevron. |
.hz-accordion-panel | child element | The content. The component ships no display/height/overflow on it on purpose, so it is free for you to animate. |
Accessibility
Built on native <details>, so no ARIA is needed for disclosure semantics. Arrow keys move between summaries, and Home/End jump to the first and last.
Disabled items have aria-disabled='true' and block interaction. Summaries wrap a real heading (headingLevel) so panels join the document outline.
References: APG Accordion pattern