Accordion

Free

10 varieties: default, flush, separated, filled, multi-expand, icon placements, badges & subtitles and disabled items. Sizes (sm / md / lg), CSS-grid animation and keyboard navigation are free; multi-expand is available in the Pro package.

</>sv-accordion accordiondisclosurenavigation
import { SvAccordionComponent } from '@styloviz/accordion';

Demo & usage

Basic usage

Usage example — open the gallery for the full interactive demo.

An interactive demo for this component lives in the gallery.

Open live demo

More examples

Sizes — sm / md / lg

example.htmlAngular
<sv-accordion [items]="items" size="sm" />
<sv-accordion [items]="items" size="md" />
<sv-accordion [items]="items" size="lg" />

Multi-expand, separated (Pro)

example.htmlAngular
<sv-accordion-pro [items]="items" expandMode="multi" variant="separated" iconPosition="right" (itemToggled)="onToggle($event)" />

When to use

  • Job: disclosure — owns visibility of its panels in place and never navigates, so back should do nothing (compare all seven in the “Choosing a navigation pattern” guide under Guides).
  • Condensing long content into expandable sections (FAQs, settings, details).
  • You want to reduce scroll length while keeping everything reachable.
  • For switching between mutually exclusive views, use Tabs.

Async states

None — this component has nothing to load, so it has no loading, empty or error state to drive.

Item identity & owned state

Restorable state
  • openIds

Bind these to put a saved view back — the open sections, the hidden columns, the selected rows — and read their Change events to store what the user does.

Inputs

NameTypeDefaultDescription
itemsAccordionItem[][]Array of panel definitions.
variantAccordionVariant'default'Visual style.
expandModeAccordionExpand'single'Whether one or many panels can be open at once.
sizeAccordionSize'md'Padding / font scale.
iconPositionAccordionIconPos'right'Which side the chevron appears on.
customIconstring''Icon-library NAME used as the expand/collapse indicator instead of the default chevron (e.g. `'plus'`). Leave empty to use the built-in chevron. When only this is set the glyph rotates 180° on open (good for arrows). Was `customIconPath`, which took a raw `<path d="…">`.
customIconOpenstring''Optional alternate icon-library NAME shown when the panel is **open**. Set this alongside `customIcon` for a true swap (e.g. plus → minus). When set, rotation is disabled — the glyphs switch instead. Was `customIconOpenPath`.
customClassstring''Extra CSS classes applied to the root wrapper.
headingLevelnumberDEFAULT_HEADING_LEVELHeading level of the element wrapped around each panel header — `1`–`6`, so the panels sit correctly in the page outline and can be reached by heading navigation. A value outside `1`–`6` is clamped, with one dev warning.
findInPagebooleantrueLet browser find-in-page (`Ctrl+F`) reach the text inside COLLAPSED panels, by marking them `hidden="until-found"` where the browser supports it. The browser expands the panel it matched and the open state follows. Turn it off for content that is meant to stay hidden until asked for — a quiz answer, a spoiler. Disabled panels are never findable.
searchQuerystring''Reveal and highlight the panels whose title, subtitle or body contains this text. The reveal is transient: clearing the query returns every panel to the state the user left it in, and nothing is written to `openIds` or to the URL. Matching is case-insensitive substring.
urlKeystring | nullnullPut the open sections in the URL under this key, so one answer can be linked to and survives a refresh. Needs `provideSvNavLocation()` at the app root; multiple open sections are comma-separated.
openIdsSet<string> (two-way)new Set()Ids of the currently-open panels (two-way). A `model` (audit item X-6) so the open set can be restored as well as observed: `itemToggled` reports one gesture at a time, and rebuilding `items` with `defaultOpen` only ever affects panels this accordion has not seen — so a host could not reopen the three sections a user had left open before a route change. A bound value wins over `defaultOpen`. This used to be seeded once, in `ngOnInit`, from whatever `items` held at that moment — which for anything fetched is an empty array, so `defaultOpen` was dead API for every async host. `defaultOpen` is now applied only to ids this component has not seen before, so a host that rebuilds its `items` array each change-detection pass cannot reopen a section the user closed.

Outputs

EventPayload typeDescription
itemToggledAccordionToggleEventFires whenever a panel is opened or closed. `source` says what did it — `'user'` for a click or key, `'find-in-page'` when the browser revealed the panel itself. A transient reveal (a search query, `reveal()`) does not fire.
unresolvedSvUnresolvedValue<readonly string[]>Emitted when the open set named sections this accordion cannot show. Carries the set as bound, the set after pruning, and whether `openIds` was written back.
openIdsChangeSet<string>Emits the new `openIds` whenever it changes — the output half of `[(openIds)]`.

<sv-accordion-pro>

Also in this family — SvAccordionProComponent from @styloviz-pro/accordion.

Async states

None — this component has nothing to load, so it has no loading, empty or error state to drive.

Item identity & owned state

Restorable state
  • openIds

Bind these to put a saved view back — the open sections, the hidden columns, the selected rows — and read their Change events to store what the user does.

Inputs

NameTypeDefaultDescription
itemsAccordionItem[][]Array of panel definitions.
variantAccordionVariant'default'Visual style.
expandModeAccordionExpand'single'Whether one or many panels can be open at once.
sizeAccordionSize'md'Padding / font scale.
iconPositionAccordionIconPos'right'Which side the chevron appears on.
customIconstring''Icon-library NAME used as the expand/collapse indicator instead of the default chevron (e.g. `'plus'`). Leave empty to use the built-in chevron. When only this is set the glyph rotates 180° on open (good for arrows). Was `customIconPath`, which took a raw `<path d="…">`.
customIconOpenstring''Optional alternate icon-library NAME shown when the panel is **open**. Set this alongside `customIcon` for a true swap (e.g. plus → minus). When set, rotation is disabled — the glyphs switch instead. Was `customIconOpenPath`.
customClassstring''Extra CSS classes applied to the root wrapper.
headingLevelnumberDEFAULT_HEADING_LEVELHeading level of the element wrapped around each panel header — `1`–`6`, so the panels sit correctly in the page outline and can be reached by heading navigation. A value outside `1`–`6` is clamped, with one dev warning.
findInPagebooleantrueLet browser find-in-page (`Ctrl+F`) reach the text inside COLLAPSED panels, by marking them `hidden="until-found"` where the browser supports it. The browser expands the panel it matched and the open state follows. Turn it off for content that is meant to stay hidden until asked for — a quiz answer, a spoiler. Disabled panels are never findable.
searchQuerystring''Reveal and highlight the panels whose title, subtitle or body contains this text. The reveal is transient: clearing the query returns every panel to the state the user left it in, and nothing is written to `openIds` or to the URL. Matching is case-insensitive substring.
urlKeystring | nullnullPut the open sections in the URL under this key, so one answer can be linked to and survives a refresh. Needs `provideSvNavLocation()` at the app root; multiple open sections are comma-separated.
openIdsSet<string> (two-way)new Set()Ids of the currently-open panels (two-way). A `model` (audit item X-6) so the open set can be restored as well as observed: `itemToggled` reports one gesture at a time, and rebuilding `items` with `defaultOpen` only ever affects panels this accordion has not seen — so a host could not reopen the three sections a user had left open before a route change. A bound value wins over `defaultOpen`. This used to be seeded once, in `ngOnInit`, from whatever `items` held at that moment — which for anything fetched is an empty array. An accordion whose sections arrive from a request could not have one open on arrival, however its data was marked, and `defaultOpen` was quietly dead API for every async host. Reconciling against each new `items` array fixes that, but naively it introduces the opposite bug: a host that rebuilds its `items` on every change detection would reopen a section the user had just closed. So `defaultOpen` is offered **only to ids this component has not seen before** — which is what "default" means — and ids that have gone away are dropped.

Outputs

EventPayload typeDescription
itemToggledAccordionToggleEventFires whenever a panel is opened or closed. `source` says what did it — `'user'` for a click or key, `'find-in-page'` when the browser revealed the panel itself. A transient reveal (a search query, `reveal()`) does not fire.
unresolvedSvUnresolvedValue<readonly string[]>Emitted when the open set named sections this accordion cannot show. Carries the set as bound, the set after pruning, and whether `openIds` was written back.
openIdsChangeSet<string>Emits the new `openIds` whenever it changes — the output half of `[(openIds)]`.

Customization & theming

  • Three sizes: sm / md / lg.
  • Single-expand free; multi-expand in the Pro package.
  • Flush, separated and filled styles.
  • Badges, subtitles and icon placement.

Best practices

  • Do: Use single-expand for guided reading; multi-expand (Pro) when users compare sections.
  • Do: Write headers that summarize their panel so users can scan without opening.
  • Don't: Don't hide critical primary content behind a collapsed panel by default.

Accessibility

  • Headers are native buttons with aria-expanded; panels linked via aria-controls.
  • Keyboard: Enter/Space toggles, ↑↓/Home/End move focus between headers.
  • Collapsed panels are marked inert so their content is skipped by assistive tech and the tab order.

Feedback on Accordion

Spotted a gap in this page, or something that didn't work as described? Comments are GitHub Discussions in the public styloviz/community repo, so posting needs a GitHub account.

Loads giscus.app and GitHub — nothing is fetched until you click.