# Pointer gestures have single-pointer alternatives

- **Pattern:** `ab-000123` (`accessibility-wcag.understandable.pointer-alternative`)
- **Severity:** low
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-000123
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-000123)

## Why it matters

Pinch-to-zoom, two-finger swipe, or multi-touch gesture controls exclude users with one functional hand, tremor, or other conditions that prevent multi-pointer input. WCAG 2.2 SC 2.5.1 (Pointer Gestures) is a Level A requirement: all functionality that uses multipoint or path-based gestures must also be operable with a single pointer using no path constraint. An image gallery that requires pinch-to-zoom with no zoom buttons cannot be used with one finger.

## Severity rationale

Low because multi-pointer gesture requirements are limited to specific UI components rather than the whole interface, but wherever they occur they create a hard exclusion violating WCAG 2.2 SC 2.5.1 at Level A.

## Remediation

For every multi-pointer gesture, provide single-pointer button controls that perform the same action.

```tsx
// Image viewer with single-pointer zoom buttons alongside pinch-to-zoom
export function ImageViewer({ src }: { src: string }) {
  const [scale, setScale] = useState(1)

  return (
    <div>
      <div className="flex gap-2 mb-2">
        <button onClick={() => setScale(s => Math.max(1, s - 0.25))} aria-label="Zoom out">
          −
        </button>
        <button onClick={() => setScale(s => Math.min(4, s + 0.25))} aria-label="Zoom in">
          +
        </button>
        <button onClick={() => setScale(1)} aria-label="Reset zoom">
          Reset
        </button>
      </div>
      <img
        src={src}
        style={{ transform: `scale(${scale})`, transformOrigin: 'top left' }}
        alt=""
      />
    </div>
  )
}
```

For swipeable carousels, add previous/next buttons. For map panning, add directional controls. The pointer gesture can remain — it just cannot be the only way.

## Detection

- **ID:** `pointer-alternative`
- **Severity:** `low`
- **What to look for:** Enumerate every relevant item. Check for multi-pointer gestures like pinch-to-zoom, two-finger tap, or swipe with multiple fingers. Verify that single-pointer alternatives exist (e.g., buttons for zoom).
- **Pass criteria:** At least 1 of the following conditions is met. All multi-pointer gestures have single-pointer alternatives. Path-dependent gestures (draw, swipe in specific pattern) have simpler alternatives.
- **Fail criteria:** Functionality requires complex multi-pointer gestures with no alternative.
- **Skip (N/A) when:** The project has no pointer-based gestures.
- **Detail on fail:** Example: `"Image gallery requires pinch-to-zoom with no zoom buttons. Slider requires swipe gesture with no keyboard or button controls"`
- **Remediation:** Provide alternative controls:

  ```tsx
  // Instead of pinch-to-zoom only, add buttons
  <button onClick={() => setZoom(zoom + 1)}>Zoom In</button>
  <button onClick={() => setZoom(zoom - 1)}>Zoom Out</button>
  <Image style={{ transform: `scale(${zoom})` }} />
  ```

## External references

- wcag 2.5.1
- section-508 407.6

Taxons: accessibility

HTML version: https://auditbuffet.com/patterns/ab-000123
