Conversation
There was a problem hiding this comment.
Pull request overview
This PR proposes automatically assigning an id to combobox options that lack one so aria-activedescendant can reliably reference the active option, addressing the accessibility degradation described in issue #96.
Changes:
- Generate an
idfor the active option at navigation time when the option has noid. - Set
aria-activedescendantusing the (possibly newly generated) optionid.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (!target.id) { | ||
| target.id = `combobox-item-${Math.random().toString().slice(2, 6)}` | ||
| } |
There was a problem hiding this comment.
The auto-generated option id uses only a short Math.random().toString().slice(2, 6) fragment and doesn’t check for existing IDs in the document. This makes duplicate IDs very likely as users navigate through many options (and Number#toString() can sometimes yield fewer than 4 digits, shrinking the space further), which can break aria-activedescendant resolution and violates unique-id requirements.
Suggested fix: generate IDs in a much larger space and guarantee uniqueness (e.g., prefix with this.list.id and use a per-instance counter, or use crypto.randomUUID() when available with a fallback), and/or loop until document.getElementById(candidate) is null before assigning.
| if (!target.id) { | ||
| target.id = `combobox-item-${Math.random().toString().slice(2, 6)}` | ||
| } |
There was a problem hiding this comment.
This change adds new behavior (auto-assigning an id when an option lacks one) but the test suite currently only covers options that already have explicit IDs. Please add a test case with a [role="option"] element missing id to assert that navigating sets a generated id and that aria-activedescendant points to it.
This is a demo of one possible solution to #96. In the screen recording below you can see random IDs being automatically added to each option in order to give
aria-activedescendantsomething to point at.auto-id.mp4
The big upside of this approach is that it amounts to an automated retrofit for any and all ARIA widget implementations that use this package and omit the IDs on the options. Applications using this package would get an accessibility upgrade free via dependabot.
One downside I can think of is that the risk of ID collisions grows with both the length of the list and the number of other lists on the page. If you had five comboboxes with 100 elements each on the same page you'd have a 50/50 chance of a collision with the code I've written here. We can easily improve those odds by incorporating
list.idintotarget.idand adding more random digits, though.Another downside is the potential for further screen reader bugs due to the last-minute insertion of the random IDs. It works fine in VoiceOver in the screen recording above, but my gut feeling is that results may vary in a screen reader like JAWS due to the last-minute nature of the change. If you want to move forward with this approach I can commit to manually testing it in JAWS first. If there is an issue there, we can fix it by moving the ID generation into the constructor alongside the one for
list.idso that JAWS has time to notice them.