What
role="listbox" only permits option and group children, but both selectors push a Divider — which renders role="separator" — straight into their listbox:
MultiSelector renders one above the options when hasSelectAll is set, and one per {type: 'divider'} / section option.
Selector does the same for {type: 'divider'} and section options. (Its "no results" branch already avoids this — it renders role="presentation" with a comment saying listbox only permits option/group children, so the constraint is known; the dividers just predate it.)
axe rates it critical (aria-required-children, WCAG 2 A / 1.3.1). A screen reader can drop or mis-report the listbox's children when an unallowed role appears among them.
Element has children which are not allowed: [role=separator]
<div id="…" role="listbox" aria-multiselectable="true" aria-labelledby="…">
<div role="separator" aria-orientation="horizontal" class="astryx-divider subtle horizontal" …>
Why we haven't seen it
The a11y audit never opens a closed popup, and until #4993 no MultiSelector story used isDefaultOpen — so nothing in CI had ever looked inside an open listbox. The violation surfaced the moment a story opened one with hasSelectAll on. Anything that only exists inside an open popover is invisible to the audit today; worth keeping in mind beyond this bug.
To reproduce: any MultiSelector story with hasSelectAll + isDefaultOpen, then pnpm a11y:audit -- --components MultiSelector.
Fix direction
The dividers are decorative — they carry no information the options don't. Making them presentational inside a listbox (role="none", or a variant of Divider that renders no role) keeps the visual and removes the unallowed child. Sections are the case to think about: a titled section already renders a role="group", so its heading divider may want to move inside the group or become the group's aria-label source rather than sit beside it.
Both components share the pattern, so they should be fixed together.
What
role="listbox"only permitsoptionandgroupchildren, but both selectors push aDivider— which rendersrole="separator"— straight into their listbox:MultiSelectorrenders one above the options whenhasSelectAllis set, and one per{type: 'divider'}/ section option.Selectordoes the same for{type: 'divider'}and section options. (Its "no results" branch already avoids this — it rendersrole="presentation"with a comment saying listbox only permits option/group children, so the constraint is known; the dividers just predate it.)axe rates it critical (
aria-required-children, WCAG 2 A / 1.3.1). A screen reader can drop or mis-report the listbox's children when an unallowed role appears among them.Why we haven't seen it
The a11y audit never opens a closed popup, and until #4993 no
MultiSelectorstory usedisDefaultOpen— so nothing in CI had ever looked inside an open listbox. The violation surfaced the moment a story opened one withhasSelectAllon. Anything that only exists inside an open popover is invisible to the audit today; worth keeping in mind beyond this bug.To reproduce: any
MultiSelectorstory withhasSelectAll+isDefaultOpen, thenpnpm a11y:audit -- --components MultiSelector.Fix direction
The dividers are decorative — they carry no information the options don't. Making them presentational inside a listbox (
role="none", or a variant ofDividerthat renders no role) keeps the visual and removes the unallowed child. Sections are the case to think about: a titled section already renders arole="group", so its heading divider may want to move inside the group or become the group'saria-labelsource rather than sit beside it.Both components share the pattern, so they should be fixed together.