WCAG 2.5.1 · Level A · ADA Title II

Swipe and pinch gestures under the ADA

Anything done with a swipe or pinch must also work with a single tap or click. What this means for city and county apps.

What the criterion asks of a swipe or a pinch

A multipoint gesture uses two or more fingers at once, such as a two-finger pinch. A path-based gesture depends on the route the pointer takes, such as a swipe that has to travel in a mostly straight line or a loop drawn on a map. WCAG 2.1 success criterion 2.5.1 says that anything you can do with one of those gestures must also be doable with a single pointer (one finger, a mouse, a stylus) without a path-based gesture, for example with a tap, a click or a button.

Keyboard support does not satisfy this criterion on its own. Some people cannot use a keyboard but can still tap or click, so the single-pointer alternative should be a control that works by pointer, ideally a visible one. The criterion allows a gesture to stay when it is essential, such as a signature, and it does not cover gestures reserved by the browser, the operating system or assistive technology.

Who gets stuck without an alternative

  • People using a head pointer, mouth stick or eye-gaze system, which can only point at one place at a time.
  • People with tremor or limited fine motor control, for whom a straight swipe or a steady two-finger pinch is unreliable.
  • People holding a phone in one hand, or using a tablet fixed to a wheelchair mount.
  • People with cognitive disabilities, who often find a labelled button easier to discover than a hidden gesture.

Where public-service sites go wrong

Government and municipal sites often reuse consumer widgets: a park reservation calendar that changes month only on swipe, a library app where a loan is renewed by sliding the row sideways, a permit viewer for scanned plan sets that zooms only by pinching, or a GIS parcel search that asks residents to draw a loop around their street. Each of these is a service a resident has to complete. Since the Title II rule adopts WCAG 2.1 Level A and AA, a gesture-only control in a city or county app is judged against this criterion. Mobile apps are covered too; see mobile apps.

Web patterns: calendar, loan list, plan viewer, area search

Park reservation calendar

Incorrect
let startX = 0;
calendar.addEventListener("touchstart", e => { startX = e.touches[0].clientX; });
calendar.addEventListener("touchend", e => {
  const dx = e.changedTouches[0].clientX - startX;
  if (dx < -60) showMonth(current + 1);  // swipe is the only way
  if (dx > 60)  showMonth(current - 1);
});
Correct
<button type="button" id="prev-month">Previous month</button>
<button type="button" id="next-month">Next month</button>
<script>
  document.getElementById("prev-month")
    .addEventListener("click", () => showMonth(current - 1));
  document.getElementById("next-month")
    .addEventListener("click", () => showMonth(current + 1));
  // The swipe handler may remain as a shortcut.
</script>

Library loans: slide to renew

Incorrect
<li class="loan" data-reveal-on-swipe="renew">
  <span>The Grapes of Wrath</span>
  <span>Due May 3</span>
</li>
Correct
<li class="loan">
  <span>The Grapes of Wrath</span>
  <span>Due May 3</span>
  <button type="button" data-renew="1042">Renew</button>
</li>

Plan viewer for permit drawings

Incorrect
viewer.addEventListener("touchmove", e => {
  if (e.touches.length === 2) {
    zoomBy(distance(e.touches) / lastDistance);  // two fingers only
  }
});
Correct
<button type="button" id="zoom-in">Zoom in</button>
<button type="button" id="zoom-out">Zoom out</button>
<script>
  document.getElementById("zoom-in")
    .addEventListener("click", () => zoomBy(1.25));
  document.getElementById("zoom-out")
    .addEventListener("click", () => zoomBy(0.8));
  // Two-finger pinch can still call zoomBy().
</script>

Mobile patterns: SwiftUI and Jetpack Compose

Native apps hit the same problem with custom gesture recognizers. Keep the gesture if you like it, but add a button or menu item that does the same job with one finger.

Incorrect
// SwiftUI
Image("floorplan")
  .scaleEffect(scale)
  .gesture(MagnificationGesture().onChanged { scale = $0 })
Correct
// SwiftUI
VStack {
  Image("floorplan").scaleEffect(scale)
    .gesture(MagnificationGesture().onChanged { scale = $0 })
  HStack {
    Button("Zoom out") { scale = max(1, scale - 0.25) }
    Button("Zoom in")  { scale = min(4, scale + 0.25) }
  }
}
Incorrect
// Jetpack Compose
Image(
  painter = painterResource(R.drawable.floorplan),
  contentDescription = "Clinic floor plan",
  modifier = Modifier.pointerInput(Unit) {
    detectTransformGestures { _, _, zoom, _ -> scale *= zoom }
  }
)
Correct
// Jetpack Compose
Column {
  Image(
    painter = painterResource(R.drawable.floorplan),
    contentDescription = "Clinic floor plan",
    modifier = Modifier
      .graphicsLayer(scaleX = scale, scaleY = scale)
      .pointerInput(Unit) {
        detectTransformGestures { _, _, zoom, _ -> scale *= zoom }
      }
  )
  Row {
    Button(onClick = { scale = (scale - 0.25f).coerceAtLeast(1f) }) { Text("Zoom out") }
    Button(onClick = { scale = (scale + 0.25f).coerceAtMost(4f) }) { Text("Zoom in") }
  }
}

How to check it

  1. Make a list of every place a swipe, pinch, two-finger scroll, rotate or drawn shape triggers something.
  2. On a touch device, try to finish each task using only taps. If you need a gesture to reach a result, note it.
  3. Use a mouse or a stylus-only setup and confirm the same result is reachable with plain clicks.
  4. Test with the platform screen reader on: custom gestures often conflict with the reader, so a button is also the dependable route there.
  5. Tools: a DOM search for touchstart, touchmove and pointermove handlers helps you build the list, but a tool cannot tell whether a button alternative exists. That takes a person.

Slips that keep reappearing

  • Treating keyboard support as the alternative. A keyboard shortcut does not help someone who only has a pointer.
  • Hiding the alternative button until a gesture is detected.
  • Assuming a slider drag is a path-based gesture. Dragging that needs no specific path is not what this criterion targets; still, check the control works with a tap on the track.
  • Removing a gesture to comply. You can keep it as a convenience, as long as a single-pointer route exists.

Ship it when

  • Every swipe, pinch or drawn path has a one-tap equivalent, ideally visible on screen.
  • The equivalent has an accessible name that matches its visible text.
  • Any remaining gesture-only feature is genuinely essential, such as a handwritten signature, and you have written down why.

Sources

Standard version: WCAG 2.1 (W3C Recommendation, 5 June 2018), as named in the Title II web rule. This page is for informational purposes only and is not legal advice. For binding requirements, refer to the official ADA statute and regulations, and ask a lawyer how they apply to you.

Corpowid is recognized by Gartner

Corpowid has been recognized by Gartner, a leading global research and advisory firm, for our innovation and performance in digital accessibility. These badges reflect our commitment to creating inclusive, AI-powered web experiences.

Have questions about Corpowid?

Let’s connect.

We will get back to you as soon as possible.