Every gesture needs a button
Every drag, swipe or slide also needs a click and a keyboard path.
Vercel Labsweb-interface-guidelines · Touch & Drag
Drag/swipe/pinch/path gestures need tap/click and keyboard alternatives unless essential
Vercel Labsweb-interface-guidelines · Anti-patterns
Gesture-only action without tap/click and keyboard alternative
From web-design-guidelines. Related: WCAG 2.5.7 Dragging Movements ↗.
Swipe to delete
Swipe a row left, then try again keyboard only.
Swipe onlyBad
Swipe, or menuGood
function remove(row) {
const next = row.nextElementSibling ?? row.previousElementSibling;
row.remove();
next?.querySelector(".more").focus();
}
swipe.onCommit = remove;
menu.onDelete = remove;
Drag to reorder
Add a task, then move it to the top.
Drag onlyBad
Drag, buttons, keysGood
Alt plus Up or Down arrow moves this task.
row.onkeydown = (e) => {
if (e.altKey && e.key === "ArrowUp") move(row, -1);
if (e.altKey && e.key === "ArrowDown") move(row, 1);
};
upButton.onclick = () => move(row, -1);
Slide to confirm
Pay with each slider, then raise the distance.
Path gesture onlyBad
Slide to pay $1,240.00
Slide, tap twice, keysGood
Arrow keys or End to slide. Or press Enter twice.
<div role="slider" tabindex="0" aria-label="Slide to pay"
aria-valuemin="0" aria-valuemax="100" aria-valuenow="0"></div>
Why it matters, pros and cons
Why it matters
Keyboard and screen-reader users can't trace a path; a button can.
Pros
- Meets WCAG 2.1.1, 2.5.1 and 2.5.7.
- A visible button shows the action exists.
- One code path for every input.
Cons
- More chrome per row; one menu keeps it small.
- Key bindings are invisible; announce moves via
aria-live. - Exempt only when the path is the input, like signatures.