Accessibility
Keyboard support, ARIA, and reduced motion.
A picker is an input, so it behaves like one. All of this is on by default and none of it needs configuring.
Keyboard
Focus the track and it answers:
| Key | Does |
|---|---|
| ← → | step one item (horizontal track) |
| ↑ ↓ | step one item (vertical track) |
| PgUp PgDn | jump five |
| Home End | first and last item |
| Enter Space | choose the centered item |
The arrows follow the axis, so an arrow key always moves the track the way it points.
Screen readers
The container is a listbox, each slot an option, and a single id hops to
whichever slot is centered so aria-activedescendant can stay put on the
container.
<div class="loopem loopem-interactive" role="listbox"
tabindex="0" aria-orientation="horizontal"
aria-activedescendant="loopem-active-1">
<div class="loopem-track">
<div class="loopem-slot" role="option" data-loopem-key="3" aria-selected="false">…</div>
<div class="loopem-slot" role="option" data-loopem-key="4" id="loopem-active-1"
data-loopem-centered aria-selected="true">…</div>
</div>
</div>If you put your own focusable element inside a slot, it stays focusable and reachable in the normal way. The track owns the container’s focus, not yours.
Reduced motion
Under prefers-reduced-motion, autoplay never starts. Motion that begins
with no way to have asked for it is precisely what that setting refuses.
Dragging, flicking and the settle spring still work, because those are motion someone asked for by touching the thing.
Touch and pointer
The track claims one axis only. A horizontal one keeps touch-action: pan-y, so a thumb dragged up the page over it still scrolls the page. A
diagonal swipe is decided by its first few pixels and handed to whichever of the
two it more nearly belongs to. Nobody gets trapped.
What you still have to do
Labelling. Loopem does not know what your items are, so give each one an accessible name and give the container one too:
createPicker(el, {
items: colors,
renderItem: (color) => {
const button = document.createElement('button')
button.type = 'button'
button.setAttribute('aria-label', color.name)
return button
},
})<div id="picker" aria-label="Colorway"></div>