Accordion from @prepared911/ui/accordion is built on Radix Accordion and follows the WAI-ARIA Disclosure pattern. The component wires the trigger-to-content relationship; your job is to supply descriptive trigger text, correct heading levels, and reasonable focus behavior when content loads dynamically.
What the component handles
Disclosure semantics. Each AccordionTrigger is a button with aria-expanded reflecting open state; each AccordionContent is a role="region" labeled by its trigger via aria-labelledby and referenced from the trigger via aria-controls.
Keyboard navigation. Tab, Shift+Tab, Enter, Space, Home, End, and Up/Down arrows move between triggers and toggle items per Radix.
Open state semantics.Single vs. Multiple types are respected for assistive tech; only the currently open item responds as expanded.
Heading outline.AccordionTrigger does not force a heading level. Wrap it in the level that fits the surrounding page outline (usually h3 inside an h2 section). See W3C WAI: Heading ranksopens in a new tab.
No nested interactive content in the trigger. A delete button inside a trigger is a confused semantic and is unreachable via the Enter-to-toggle keyboard behavior. Move such controls into the content region.
Dynamic content. When content loads asynchronously, keep focus stable (don't reset it to the trigger on every update) and keep aria-expanded in sync with the actual state.
Keyboard behavior
Tab / Shift+Tab moves focus between triggers.
Enter / Space toggles the focused item.
Home / End moves to the first / last trigger.
Arrow Down / Arrow Up moves to the next / previous trigger per Radix defaults.
Semantics and roles
Each trigger is a button with aria-expanded and aria-controls.
Each content panel is a role="region" with aria-labelledby pointing at the trigger's id.
Don't override these roles. Doing so breaks the disclosure pattern that assistive tech depends on.
Content structure
Use semantic HTML inside AccordionContent (headings, lists). This makes the content structure navigable with screen-reader rotor commands.
Reduced motion
Accordion expansion uses CSS transitions; respect system motion preferences by overriding transition durations when prefers-reduced-motion: reduce is set. Don't remove state change feedback entirely—fade faster, don't fade away.
Known caveats
Heading level. Not configurable on AccordionTrigger; validate against each page's outline.
Nested interactive content. Do not place buttons or links inside a trigger.
Always-open single.collapsible={false} on a Single accordion forces at least one open section. Use only when the surface genuinely requires it, since it reduces operator control.