$npx -y skills add mgifford/accessibility-skills --skill touch-pointerLoad this skill whenever the project contains interactive UI elements that users touch, tap, click, drag, swipe, or gesture with — buttons, links, drag-and-drop interfaces, sliders, carousels, or custom touch interactions. Under no circumstances create touch targets smaller than
| 1 | # Touch and Pointer Accessibility Skill |
| 2 | |
| 3 | > **Canonical source**: `examples/TOUCH_POINTER_ACCESSIBILITY_BEST_PRACTICES.md` in `mgifford/ACCESSIBILITY.md` |
| 4 | > This skill is derived from that file. When in doubt, the example is authoritative. |
| 5 | |
| 6 | Apply these rules when implementing any interactive UI that users touch, click, |
| 7 | tap, drag, or gesture with. |
| 8 | **Load alongside `keyboard/SKILL.md` — pointer and keyboard requirements are complementary, not interchangeable.** |
| 9 | |
| 10 | --- |
| 11 | |
| 12 | ## Core Mandate |
| 13 | |
| 14 | Build interfaces that work with touchscreens, mice, trackpads, pens, head |
| 15 | pointers, switch-controlled pointers, and other pointing devices. Do not |
| 16 | infer a person's abilities from the device they use — a touchscreen user may |
| 17 | also use a keyboard, speech input, a screen reader, or a mouse. |
| 18 | |
| 19 | **A keyboard-only alternative does not by itself satisfy pointer-gesture |
| 20 | (2.5.1) or dragging (2.5.7) requirements** — the alternative must work with a |
| 21 | single pointer, without a path-based gesture or drag, even though keyboard |
| 22 | operability is separately required by 2.1.1. Native buttons conveniently |
| 23 | satisfy both at once. |
| 24 | |
| 25 | --- |
| 26 | |
| 27 | ## Severity Scale (this skill) |
| 28 | |
| 29 | | Level | Meaning | |
| 30 | |---|---| |
| 31 | | **Critical** | Functionality only available via multi-point gesture with no single-pointer alternative; `user-scalable=no` prevents zoom | |
| 32 | | **Serious** | Drag-to-reorder with no single-pointer alternative; touch target under 24×24px for primary actions | |
| 33 | | **Moderate** | Mousedown/touchdown action with no up-event cancellation; motion gesture without UI alternative | |
| 34 | | **Minor** | Target under 44×44px for non-primary actions; spacing between targets too small | |
| 35 | |
| 36 | Prioritize by actual user impact, task criticality, reach, and frequency — |
| 37 | don't assign severity solely from a success-criterion number or tool output. |
| 38 | |
| 39 | --- |
| 40 | |
| 41 | ## Start With an Input-Agnostic Base |
| 42 | |
| 43 | Use native HTML controls and links — their built-in `click` activation works |
| 44 | across mouse, touch, pen, keyboard, and many AT. |
| 45 | |
| 46 | ```html |
| 47 | <!-- Incorrect --> |
| 48 | <div class="button" ontouchend="saveChanges()">Save changes</div> |
| 49 | |
| 50 | <!-- Correct --> |
| 51 | <button type="button" id="save-button">Save changes</button> |
| 52 | <script> |
| 53 | document.querySelector("#save-button").addEventListener("click", saveChanges); |
| 54 | </script> |
| 55 | ``` |
| 56 | |
| 57 | Use Pointer Events for custom direct-manipulation components rather than |
| 58 | maintaining separate mouse/touch implementations. Do not gate core |
| 59 | functionality on `pointerType` — a pen user shouldn't lose a function a mouse user gets. |
| 60 | |
| 61 | --- |
| 62 | |
| 63 | ## Critical: Never Block Zoom (`user-scalable=no`) |
| 64 | |
| 65 | ```html |
| 66 | <!-- Critical violation — never do this --> |
| 67 | <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> |
| 68 | |
| 69 | <!-- Correct --> |
| 70 | <meta name="viewport" content="width=device-width, initial-scale=1"> |
| 71 | ``` |
| 72 | |
| 73 | Some libraries/frameworks add `user-scalable=no` automatically — audit the |
| 74 | viewport meta tag on every project. Do not rely on browsers ignoring |
| 75 | restrictive viewport settings; remove the restriction from the source. Test |
| 76 | text at 200% and layout at 400% zoom. |
| 77 | |
| 78 | --- |
| 79 | |
| 80 | ## Critical: Single-Pointer Alternatives for Multi-Point/Path Gestures (WCAG 2.5.1) |
| 81 | |
| 82 | A **multipoint gesture** uses two or more contact points (pinch zoom). A |
| 83 | **path-based gesture** depends on the route/direction/shape traced, not just |
| 84 | start/end points. When content defines such a gesture, provide a way to |
| 85 | perform the same function with a single pointer and no path-based gesture — |
| 86 | the alternative must exist **in the content**, not just as a keyboard command. |
| 87 | |
| 88 | ```html |
| 89 | <div class="map-controls" aria-label="Map controls"> |
| 90 | <button type="button" data-map-action="zoom-in">Zoom in</button> |
| 91 | <button type="button" data-map-action="zoom-out">Zoom out</button> |
| 92 | <button type="button" data-map-action="north">Pan north</button> |
| 93 | </div> |
| 94 | ``` |
| 95 | |
| 96 | Examples: buttons for zooming/panning a map; previous/next buttons for a |
| 97 | swipe carousel; a menu command alongside drawing a shape; visible rotate/ |
| 98 | resize controls. Gestures required by the browser or AT itself (a mobile |
| 99 | screen reader's navigation gestures) are outside the author's control — don't |
| 100 | reproduce or interfere with them. |
| 101 | |
| 102 | --- |
| 103 | |
| 104 | ## Critical: Non-Dragging Alternatives (WCAG 2.5.7 — WCAG 2.2) |
| 105 | |
| 106 | If a function uses dragging, provide a way to complete it with a **single |
| 107 | pointer without dragging** — a keyboard-only alternative does not by itself |
| 108 | satisfy 2.5.7. Native buttons satisfy both the pointer requirement and |
| 109 | keyboard access at once. |
| 110 | |
| 111 | ```html |
| 112 | <ul id="task-list"> |
| 113 | <li> |