Mobile and Touch Accessibility

1. Touch Target Size and Spacing

Target Type Minimum Size (WCAG 2.2) Recommended Size Minimum Spacing
Primary actions (buttons, links) 24×24 CSS pixels WCAG 2.5.8 AA 44×44 CSS pixels (iOS), 48×48 dp (Android) 8px between targets
Critical interactive elements 24×24 CSS pixels 48×48 CSS pixels minimum 12px spacing preferred
Text input fields 24px height minimum 44-48px height (includes padding) 16px vertical spacing
Icon-only buttons 24×24 CSS pixels 48×48 CSS pixels (includes padding) 8px on all sides
Inline text links Exception: sentence flow allowed Increase line-height to 1.5+ for easier tapping Underline + adequate spacing
Toggle switches 44px width × 24px height minimum 51px × 31px (iOS standard) 12px spacing
Checkbox/Radio buttons 24×24 CSS pixels 32×32 CSS pixels (visual + padding) 16px spacing between options

Example: Accessible touch target with proper sizing

<!-- CSS for touch targets -->
.touch-target {
  min-width: 48px;
  min-height: 48px;
  padding: 12px;
  margin: 8px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

<!-- Small icon with extended touch area -->
<button class="touch-target" aria-label="Delete item">
  <svg width="16" height="16" aria-hidden="true">
    <use href="#icon-delete"></use>
  </svg>
</button>
Touch Target Exceptions: WCAG 2.5.8 allows exceptions for inline text links, user agent controls (native browser UI), and when target size is essential to the information being conveyed.

2. Mobile Screen Reader Support

Platform Screen Reader Key Gestures Testing Focus
iOS VoiceOver Swipe right/left: Navigate
Double-tap: Activate
Three-finger swipe: Scroll
Rotor: Quick navigation
Heading navigation, landmark navigation, custom actions
Android TalkBack Swipe right/left: Navigate
Double-tap: Activate
Two-finger swipe: Scroll
Local context menu: Actions
Reading order, custom actions, state announcements
Mobile Safari VoiceOver (iOS) Two-finger Z: Skip web content
Rotor + swipe: Jump by element type
Web vs native behavior, form controls, ARIA support
Chrome/Firefox (Android) TalkBack Volume key shortcuts
TalkBack menu: Advanced controls
Web navigation, custom widget support

Example: Mobile-optimized ARIA labels and hints

<!-- Concise labels for mobile screen readers -->
<button 
  aria-label="Add to cart"
  aria-describedby="product-price">
  <span aria-hidden="true">+</span>
</button>
<span id="product-price" class="sr-only">$29.99</span>

<!-- Custom actions for swipe gestures (iOS) -->
<div role="article" 
     aria-label="Product: Wireless Headphones"
     data-voiceover-actions="delete,share,favorite">
  <!-- Content -->
</div>
Mobile Screen Reader Pitfalls: Avoid excessive ARIA descriptions (verbose announcements), ensure touch targets don't overlap (causes navigation confusion), and test swipe-to-delete patterns with actual screen readers.

3. Gesture Accessibility Alternatives

Gesture Pattern WCAG Requirement Alternative Method Implementation
Swipe to delete Provide visible delete button 2.5.1 Edit mode with delete buttons, long-press menu Show delete button on item focus or in action menu
Pinch to zoom Alternative zoom controls required +/- buttons, zoom slider, double-tap zoom Provide visible zoom controls or use native browser zoom
Drag and drop Pointer-independent alternative 2.5.7 Move up/down buttons, cut/paste, select-and-place Provide button-based reordering mechanism
Multi-touch gestures Single-pointer alternative required Sequential taps, menu options, mode switches Toolbar buttons or settings menu for multi-touch actions
Path-based gestures (draw shape) Alternative input method required 2.5.1 Button selection, voice input, keyboard shortcuts Provide pattern picker or command palette
Long press Not required but recommended alternative Menu button, right-click, dedicated action button Show "..." menu button for context actions
Swipe navigation (carousel) Previous/Next buttons required Arrow buttons, pagination dots, keyboard arrows Visible navigation controls always present

Example: Accessible drag-and-drop with alternatives

<!-- List item with both drag and button-based reordering -->
<li role="listitem" aria-label="Task: Review pull request"
    draggable="true">
  <span class="drag-handle" aria-label="Drag to reorder">⋮⋮</span>
  <span>Review pull request</span>
  
  <!-- Alternative to dragging -->
  <div class="reorder-controls" role="group" aria-label="Reorder controls">
    <button aria-label="Move up">↑</button>
    <button aria-label="Move down">↓</button>
  </div>
</li>
Gesture Best Practices: Always provide at least one pointer-independent alternative (WCAG 2.5.1). Ensure alternatives are discoverable without requiring gesture knowledge. Test with assistive technologies like switch controls and voice access.

4. Orientation and Zoom Handling

Feature WCAG Requirement Implementation Exceptions
Orientation support Content not restricted to single orientation 1.3.4 AA Support both portrait and landscape; use CSS media queries Essential: piano app, bank check, projector slides
Zoom and scaling Allow up to 200% zoom without loss of content 1.4.4 AA <meta name="viewport" content="width=device-width, initial-scale=1"> Never use user-scalable=no or maximum-scale=1
Text resize (400%) Text scales to 400% without horizontal scroll 1.4.10 AA Use responsive design, relative units (rem, em), fluid layouts Images of text, captions (but must reflow on zoom)
Reflow at 320px Content reflows without 2D scrolling at 320 CSS pixels 1.4.10 AA Mobile-first design, avoid fixed widths, use flexbox/grid Data tables, maps, diagrams, toolbars (horizontal scroll OK)
Orientation change Content must not be lost on rotation Test form data persistence, modal positions, focus retention None - always preserve state

Example: Proper viewport and orientation CSS

<!-- Correct viewport meta tag -->
<meta name="viewport" 
      content="width=device-width, initial-scale=1, minimum-scale=1">

<!-- NEVER do this (blocks zoom) -->
<!-- <meta name="viewport" content="maximum-scale=1, user-scalable=no"> -->

<!-- CSS for orientation support -->
@media (orientation: portrait) {
  .toolbar {
    flex-direction: column;
  }
}

@media (orientation: landscape) {
  .toolbar {
    flex-direction: row;
  }
}

/* Responsive font sizing with safe minimums */
body {
  font-size: clamp(16px, 1rem + 0.5vw, 20px);
}
Common Zoom Mistakes: Disabling zoom via viewport meta, using position: fixed with pixel widths (causes overflow at zoom), using viewport units (vw/vh) for font sizes without clamp(), and forgetting to test horizontal scrolling at 200%+ zoom levels.

5. Mobile Focus Management

Focus Scenario Mobile Consideration Implementation Testing Method
Virtual keyboard appearance Scroll input into view, maintain focus visibility element.scrollIntoView({ behavior: 'smooth', block: 'center' }) Test with on-screen keyboard on iOS/Android
Modal dialogs Trap focus, prevent body scroll, return focus on close Use inert attribute on background or focus trap library Test with screen reader swipe navigation
Dynamic content insertion Move focus to new content or announce with ARIA live Set focus to first interactive element or heading in new content Verify screen reader announces insertion
Touch vs keyboard focus Different focus indicators for touch and keyboard Use :focus-visible to show focus only for keyboard Test with external keyboard on mobile device
Focus order in landscape Maintain logical reading order across orientations Use flexbox order or grid with careful tabindex management Rotate device and verify tab order consistency
Bottom sheets / drawers Focus first focusable element when opened Programmatic focus on open, restore on close Screen reader navigation and keyboard tab order

Example: Mobile keyboard and focus management

// Handle virtual keyboard appearance
const input = document.getElementById('mobile-search');

input.addEventListener('focus', () => {
  // Scroll element into view with padding for keyboard
  setTimeout(() => {
    input.scrollIntoView({ 
      behavior: 'smooth', 
      block: 'center',
      inline: 'nearest' 
    });
  }, 300); // Delay for keyboard animation
});

// Mobile modal with focus trap
class MobileModal {
  open() {
    this.previousFocus = document.activeElement;
    this.modal.showModal(); // Native focus trap
    
    // Focus first interactive element
    const firstFocusable = this.modal.querySelector(
      'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
    );
    firstFocusable?.focus();
    
    // Prevent body scroll on mobile
    document.body.style.overflow = 'hidden';
  }
  
  close() {
    this.modal.close();
    document.body.style.overflow = '';
    this.previousFocus?.focus(); // Restore focus
  }
}
Mobile Focus Tips: Use :focus-visible to show focus rings only for keyboard navigation (not touch). Test with external keyboard on tablets. Ensure focus indicators are at least 3:1 contrast ratio against adjacent colors (WCAG 2.4.13).

6. Voice Control Integration

Voice Control Feature Platform Accessibility Requirement Implementation
Voice Access (Android) Android 11+ Visible labels match accessible names Ensure aria-label matches visible text or use aria-labelledby
Voice Control (iOS) iOS 13+ Interactive elements have accessible names All tappable elements must have text or aria-label
Click by number Both platforms Elements numbered for voice activation Proper semantic markup ensures numbering works
Click by name Both platforms Visible label text must be in accessible name 2.5.3 Start aria-label with visible text: "Delete product X"
Show labels/numbers Overlay mode All interactive elements discoverable Avoid pointer-events: none on focusable elements
Custom commands Platform-specific Document available voice commands Provide help text for complex interactions

Example: Voice control accessible button labeling

<!-- CORRECT: Visible label matches accessible name -->
<button aria-label="Delete product: Wireless Mouse">
  Delete
</button>
<!-- Voice command: "Click Delete" works! -->

<!-- INCORRECT: Accessible name doesn't include visible text -->
<button aria-label="Remove item from cart">
  Delete
</button>
<!-- Voice command: "Click Delete" FAILS -->

<!-- BEST: Use aria-labelledby for compound labels -->
<button aria-labelledby="delete-label product-name">
  <span id="delete-label">Delete</span>
</button>
<span id="product-name" class="sr-only">Wireless Mouse</span>
Voice Control Label Rule (WCAG 2.5.3): When visible text labels exist, the accessible name MUST start with the visible text. Example: Button shows "Search" → aria-label can be "Search products" but NOT "Find products". This ensures voice commands like "Click Search" work reliably.

Mobile and Touch Accessibility Quick Reference

  • Touch Targets: Minimum 24×24px (WCAG 2.2), recommended 48×48px with 8px spacing
  • Screen Readers: Test with VoiceOver (iOS) and TalkBack (Android); keep labels concise
  • Gestures: Provide pointer-independent alternatives for swipe, drag, pinch (WCAG 2.5.1, 2.5.7)
  • Orientation: Support both portrait/landscape; never restrict to single orientation (WCAG 1.3.4)
  • Zoom: Allow 200% zoom minimum; never use user-scalable=no or maximum-scale=1
  • Reflow: Content must reflow at 320px width without horizontal scrolling (WCAG 1.4.10)
  • Focus: Use :focus-visible for keyboard-only indicators; manage virtual keyboard with scrollIntoView
  • Voice Control: Visible labels must start accessible names (WCAG 2.5.3); test "Click [label]" commands
  • Testing: Use real devices with screen readers, external keyboards, voice control, and switch access
  • Key Tools: iOS VoiceOver, Android TalkBack, Voice Access, Chrome DevTools device mode, Xcode Accessibility Inspector