Skip to main content

Accordion

<zeno-accordion>Data displaySource

A stack of sections that open and close. One section is open at a time unless multiple is set.

import '@usezeno/tokens/css';
import '@usezeno/core/web-components';

Examples​

Basic​

Sections are set as the items property. Each item needs a value, a label and a content string. Opening one section closes the others.

<zeno-accordion></zeno-accordion>
document.querySelector('zeno-accordion').items = [
{ value: 'delivery', label: 'Delivery', content: 'Orders leave the warehouse within two working days.' },
{ value: 'returns', label: 'Returns', content: 'You can send an item back within 30 days.' },
{ value: 'payment', label: 'Payment', content: 'We accept cards and bank transfer.' },
];

Several sections open​

With multiple, a section stays open when another one opens. toggleItem() opens or closes a section from code.

<zeno-accordion multiple></zeno-accordion>
const accordion = document.querySelector('zeno-accordion');
accordion.items = [
{ value: 'profile', label: 'Profile', content: 'Name, photo and time zone.' },
{ value: 'security', label: 'Security', content: 'Password and sign-in methods.' },
{ value: 'billing', label: 'Billing', content: 'Plan, invoices and payment method.' },
];
accordion.toggleItem('profile');
accordion.toggleItem('billing');

Variants​

The default look, then bordered, filled, ghost and flush.

<zeno-accordion></zeno-accordion>
<zeno-accordion variant="bordered"></zeno-accordion>
<zeno-accordion variant="filled"></zeno-accordion>
<zeno-accordion variant="ghost"></zeno-accordion>
<zeno-accordion variant="flush"></zeno-accordion>
document.querySelectorAll('zeno-accordion').forEach(accordion => {
accordion.items = [
{ value: 'summary', label: 'Summary', content: 'Three files changed this week.' },
{ value: 'details', label: 'Details', content: 'report.pdf, notes.txt and budget.csv.' },
];
});

Sizes, icons and a disabled section​

A disabled section cannot be opened and the arrow keys skip it. An item can name an icon to show before its label.

<zeno-accordion size="sm"></zeno-accordion>
<zeno-accordion size="lg"></zeno-accordion>
document.querySelectorAll('zeno-accordion').forEach(accordion => {
accordion.items = [
{ value: 'account', label: 'Account', icon: 'user', content: 'Signed in as ada@example.com.' },
{ value: 'settings', label: 'Settings', icon: 'settings', content: 'Language and notifications.' },
{ value: 'archive', label: 'Archive', content: 'Nothing here yet.', disabled: true },
];
});

Attributes and properties​

PropertyAttributeTypeDefaultDescription
itemsitemsAccordionItem[][]
multiplemultiplebooleanfalse
variantvariantreflects'default' | 'bordered' | 'ghost' | 'filled' | 'flush''default'
sizesizereflects'sm' | 'md' | 'lg''md'
defaultExpandeddefaultexpandedstring[][]
testIdstringTest ID for automated testing From ZenoElement.

Methods​

MethodDescription
toggleItem(value: string): voidToggle item expansion
expandAll(): voidExpand all items
collapseAll(): voidCollapse all items
isExpanded(value: string): booleanCheck if item is expanded
focus(): voidFocus the component or its first focusable element From ZenoElement.
blur(): voidBlur the component or its focused element From ZenoElement.

Events​

EventDetailDescription
expand{ value: string; item: AccordionItem }A section opened.
collapse{ value: string; item: AccordionItem }A section closed.
change{ expanded: string[] }The set of open sections changed.
expand-allexpandAll() opened every section.
collapse-allcollapseAll() closed every section.

Slots​

This element has no slots.

CSS parts​

Part
base
content
content-wrapper
expand-icon
header
icon
item
title

CSS custom properties​

This element documents no custom properties of its own. Style it through its parts and through the tokens it reads.

The element's own source reads no tokens.

Types​

interface AccordionItem {
id: string;
value: string;
title: string;
label: string;
content: string;
icon?: string;
disabled?: boolean;
expanded?: boolean;
}

Accessibility​

Keyboard​

KeyWhat it does
TabMoves focus to the next section header.
EnterSpaceOpens or closes the focused section.
ArrowDownMoves focus to the next header that is not disabled. It stops at the last one.
ArrowUpMoves focus to the previous header that is not disabled. It stops at the first one.
HomeMoves focus to the first header that is not disabled.
EndMoves focus to the last header that is not disabled.

Forms​

This element is not a form control. It adds nothing to a form's data.

Notes​

  • Each header is a native <button> with aria-expanded, and aria-controls that points at its panel.
  • Each panel has role="region" and takes its name from its header with aria-labelledby.
  • A disabled section sets the native disabled attribute on its header, so the header leaves the tab order.
  • The element wraps its sections in role="region". The name is the aria-label you set, or “Accordion”.
  • The chevron and item icons are decoration and hidden from assistive technology.

Tests and known gaps​

  • No tests of its own. The only check is that the element registers and renders (smoke.test.ts).
  • Not checked with axe-core.
  • Has no Storybook story.
13 of the 25 properties, methods, events, slots and parts above have no description in the source
  • the property items
  • the property multiple
  • the property variant
  • the property size
  • the property defaultExpanded
  • the part base
  • the part content
  • the part content-wrapper
  • the part expand-icon
  • the part header
  • the part icon
  • the part item
  • the part title

Known gaps:

  • defaultExpanded is read only once, after the first render, and does not ask for a new render. Sections named there stay closed until something else updates the element, and a value set later is ignored. Use toggleItem() for now.
  • The attribute for defaultExpanded is defaultexpanded, with no hyphen. Other elements use hyphens, as in max-items.
  • Panel content is a plain string. There is no slot, so a panel cannot hold links, lists or other elements.
  • The AccordionItem type requires id and title and offers expanded, but the element never reads them.
  • Header buttons are not inside heading elements, so screen reader users cannot move between sections by heading.
  • The wrapper and every panel are region landmarks. An accordion with many sections fills the landmark list.
  • The fallback name “Accordion” is English and can only be changed with aria-label.
  • A closed panel is hidden only with height: 0 and overflow: hidden. Its text stays in the accessibility tree, so screen readers still read it.
  • Opening and closing do not animate. --accordion-transition already starts with all, so transition: height var(--accordion-transition) and the chevron’s transition: transform … are invalid and the browser drops them.
  • The bottom border of the last section is cut off in the default look. Every section is pulled up by 1px to share a border with the next one, and the wrapper hides the border of the last one.
  • expandAll() opens every section even without multiple. expandAll() and collapseAll() do not fire change.