I’m working on building a custom select component to allow for custom styling of options.
So far I’ve decided on the following structure for it:
<label>
<span>Label goes here</span><br>
<span class="custom-select">
<input role="combobox" aria-controls="custom-select-listbox-1">
<ul role="listbox" id="custom-select-listbox-1">
<li role="option">Option 1</li>
<li role="option">Option 2</li>
<li role="option">Option 3</li>
</ul>
</span><br>
<span>Validation error goes here</span>
</label>
The input element is styled so that it looks just like a native select, the ancestor label gives me interactions between the label and the input for free (like propagating hover from label to input, etc) so I don’t have to reimplement it using JS, and also gives me label text for assistive tech without the need to micromanage it with aria-labelledby
.
I also implemented most of common mouse and keyboard interactions that are present in native selects and styled everything to mimick native selects as close as possible.
This all works pretty well so far in testing, both for mouse and keyboard interactions and for assistive tech, but there are two issues with this structure that I’m not sure how to resolve:
- For some reason, the contents of the listbox are not included in label text on screen readers. This is actually a good thing, but I’m struggling to understand why.
- Technically speaking, an
<ul>
is not a valid descendant of<label>
.
The second item is what bothers me the most. I’m not sure what the real consequences are of generating a bit of invalid HTML like this via JS (none of this would be present in static markup generated by the server, this is all client-side only). I don’t want to disjoin the label and the input because then I would lose the ability to also put validation errors inside <label>
, and I don’t want to move the listbox outside of the label because then I won’t be able to properly position it relative to the input using only CSS, and would need to bother with dynamic coordinates calculating and fixed positioning, which I’d really like to avoid.
Ilya Pinyagin is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.