abaabil

Accordion

lightest of the eight, by js weight

A group of native <details>/<summary> disclosures. Every tier, including a11y, is server-renderable: no 'use client' anywhere, no hooks, no JavaScript at all. Open/close, exclusive selection and the expanded/collapsed state announced to assistive tech all come from the browser's native <details> behaviour.

Example (a11y tier)

How long does shipping take?

Standard shipping takes 5-7 business days.

What is your return policy?

Returns are accepted within 30 days of delivery, unused and in original packaging.

How do I contact support?

Email support@example.com, or use the chat widget in the bottom corner.

Import

import { Accordion, Disclosure } from 'abaabil/accordion'                // structure only
import { Accordion, Disclosure } from 'abaabil/accordion/styled'         // + CSS
import { Accordion_a11y, Disclosure } from 'abaabil/accordion/a11y'  // + optional group label

Every tier renders with zero client JavaScript

Unlike every other component on this site, the accordion's a11y tier adds no client-only behaviour at all, because it needs none: <details> already has the correct implicit semantics, and <summary> already has the correct implicit interactive role. Exclusive open, so opening one panel closes its siblings, comes entirely from giving each <details> the same native name attribute; the a11y tier's only addition is an optional label, which applies role="group" and aria-label to the wrapper, and only when a consumer actually supplies one (an unnamed group announces as just "group", which is worse than not grouping at all, so this is opt-in rather than automatic).

The honest limitation: no headingLevel

This component deliberately offers no way to give each panel heading semantics. <summary> has an implicit ARIA role of button, and a heading nested inside a button is not reliably exposed to assistive technology: VoiceOver, for one, does not expose a heading nested inside <summary> as a heading at all. The reverse, wrapping <summary> in a heading element, is not valid either, because <summary> must be the literal first child of <details> for the browser to recognize it as the disclosure trigger.

An API that emitted a heading here would let a consumer believe they had solved heading navigation for screen reader users when they had not, so this component does not offer one. Consumers who need reliable heading navigation across sections need the button-in-heading accordion pattern instead, an explicit heading wrapping a button with aria-expanded and aria-controls, which is a different, ARIA-driven widget. This component is a native disclosure/accordion, not a substitute for that pattern.

Props

PropTypeDefaultTierDescription
itemsArray<{key?, summary, children?}>allAccordion only. One disclosure per item.
namestring'abaabil-accordion'allAccordion only. Shared native group name for exclusive open/close; pass name={undefined} to opt out.
labelstringa11yAccordion only. Accessible name for the group; applies role="group" and aria-label. Omitted entirely when not given.
summaryReactNodeallDisclosure only. Content for the <summary>.
summaryClassNamestringallDisclosure only. Merged with the summary's base class.
classNamestringallMerged with the base class, on either component.
...propsallEverything else spreads onto the <details> (Disclosure) or wrapper <div> (Accordion).