The server owns every item: a wrapper with data-value containing a [data-trigger] control and a [data-panel] body, plus value, type and collapsible. Render <hui-accordion type="multiple" value="x,y"> to open several items at once or value="a" for one, and read value back because it reflects; the comma-separated form is only meaningful while type="multiple". It is not form-associated and submits nothing; an item opening or closing emits a bubbling, composed hui-open or hui-close carrying { value }. The trigger and panel are light-DOM descendants of the slotted item, so an hx-swap can replace a panel's body without touching the component's state, and aria-expanded, aria-controls and the generated ids are re-synced. Replacing the whole hui-accordion requires the server to emit value again, since the component persists nothing.
Accessibility
Each trigger gets aria-expanded and aria-controls; each panel gets role="region" and aria-labelledby; ids are generated when absent.
Closed panels are hidden, and each item carries data-state="open" or "closed".
The component invents no item markup: the host renders the wrapper, trigger and panel.
Keyboard
Keyboard
Keys
Action
Arrow Up, Arrow Down
Move focus between item triggers, wrapping.
Home, End
Move focus to the first or last trigger.
Enter, Space
Activate the focused trigger, which is a native button, toggling its panel.
Gotchas
hui-accordion reports expansion with hui-open and hui-close, the same two names hui-tabs uses for selection; a listener shared between them cannot tell which component fired without checking the event target or detail.