Skip to content

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>

Props

NameTypeDefaultNote
itemsAccordionItem[]—Required. See AccordionItem below.
type'single' | 'multiple''single'
defaultOpenstring | string[][]
collapsiblebooleantruefalse keeps one panel open in single mode.
headingLevel2 | 3 | 4 | 5 | 63
panelSnippet<[AccordionItem]>—Required. Renders each panel.
metaSnippet<[AccordionItem]>—Renders once per item inside the summary, after the heading and before the icon — a price, a teaser, or other supplementary detail. It receives the item, so one shared snippet renders every row. Meta content is never part of the trigger’s accessible name: see the note in the Meta example.
iconSnippet—Replaces the default chevron.
onToggle(openIds: string[]) => void—
classstring—Merged after the hz-accordion class.

Anything not listed above is forwarded as an attribute to the root element (or the native control in form components). So id, data-*, aria-*, and event handlers just work.

AccordionItem

NameTypeDefaultNote
idstring—Required. Must be unique.
titlestring | Snippet<[AccordionItem]>—Required. Use a string for plain text, or a snippet for inner markup — the snippet receives the item, so one shared snippet can render every row. The title becomes the row’s accessible name: see the note in the Rich titles example.
disabledbooleanfalse

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

HookValuesStyles
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

HookValuesStyles
.hz-accordion-itemchild elementThe <details> element.
.hz-accordion-triggerchild elementThe <summary>.
.hz-accordion-headingchild elementThe heading inside the summary, at your headingLevel.
.hz-accordion-metachild elementThe optional meta slot between the heading and the icon (renders only when the meta snippet is passed). Ships no visual layout of its own — use this class to place the slot (right-align a price, stack a teaser) and style the contents with your own classes inside the snippet.
.hz-accordion-iconchild elementThe disclosure chevron.
.hz-accordion-panelchild elementThe 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