Accordion
An animated, accessible panel that reveals or hides content when the user clicks its heading. Follows the controlled component pattern — the consumer manages open state.
Keyboard interaction
| Key | Action |
|---|---|
| Tab | Move focus to the accordion header button |
| Shift + Tab | Move focus to the previous focusable element |
| Enter | Toggle the accordion panel open or closed |
| Space | Toggle the accordion panel open or closed |
Screen reader behaviour
The following ARIA attributes are managed internally by the component:
| Attribute | Description |
|---|---|
| aria-expanded="true|false" | Set on the toggle button. Reflects the current open prop so screen readers announce the collapsed/expanded state. |
| aria-controls="[panel-id]" | Set on the toggle button. Points to the collapsible panel div, establishing the controlling relationship. |
| role="region" | Set on the collapsible panel. Marks it as a landmark region for screen reader navigation. |
| aria-labelledby="[button-id]" | Set on the collapsible panel. References the toggle button, giving the region an accessible name. |
| visibility: hidden (CSS) | Applied to the collapsed panel. Removes interactive child content from the accessibility tree and tab order when the panel is closed. |
WCAG 2.2 compliance
1.4.3 Contrast (Minimum) — AAPass
Header text and chevron icon use --diwa-text-primary; meets the 4.5:1 minimum contrast ratio against panel background in both themes.
1.4.11 Non-text Contrast — AAPass
The accordion border (--diwa-border) is validated to have ≥ 3:1 contrast against adjacent surface colours.
2.1.1 Keyboard — APass
The toggle button is reachable by Tab and activatable with Enter or Space. Slotted content inside an open panel is in the natural tab order.
2.4.7 Focus Visible — AAPass
A tokenized focus outline (var(--diwa-focus-ring-width) solid --diwa-border-focus) is rendered on the inner button via :focus-visible when navigating by keyboard.
4.1.2 Name, Role, Value — APass
aria-expanded, aria-controls, role="region", and aria-labelledby are all managed automatically by the component.
Best practices
- Heading hierarchy — always set
headingTagto fit the outline of the page. Defaulting toh2implicitly makes every accordion a top-level section — only appropriate when the accordion IS the first heading level on the page. Incorrect hierarchy confuses screen reader users navigating by heading. - Focus management — the component uses
shadow: { delegatesFocus: true }. Calling.focus()on the host element correctly forwards focus to the inner<button>. The:focus-visiblering is rendered on the inner button and visible to all pointer and keyboard users. - Controlled pattern — the component emits
updatebut never mutatesopenitself. The consumer must always respond to the event. Failing to do so means the accordion will appear frozen after the first click. - Slot content — interactive elements (links, buttons, inputs) slotted inside become unreachable when the panel is closed because
visibility: hiddenremoves them from the accessibility tree. There is no need for additionaltabindex="-1"management. - Reduced motion — all CSS transitions inside the shadow DOM respond to
prefers-reduced-motion: reduceand are suppressed to an instant switch.