ADA/Accessibility Compliance Checklist for Mobile Apps
A comprehensive guide to making mobile applications accessible to all users — from screen reader support to motor accessibility.
Why Accessibility Matters
Accessibility isn't just about compliance — it's about ensuring everyone can use your app. Approximately 15% of the world's population lives with some form of disability. That's over a billion people.
Beyond the moral imperative:
- Legal requirements — ADA, Section 508, and international regulations
- Market opportunity — Accessible apps reach more users
- Better UX for everyone — Accessibility improvements often benefit all users
- App store visibility — Both Apple and Google promote accessible apps
This guide provides a practical checklist for mobile accessibility, organized by disability category and implementation priority.
Visual Accessibility
Screen Reader Support
Screen readers (VoiceOver on iOS, TalkBack on Android) are essential for blind and low-vision users.
Labels and Hints
// iOS
button.accessibilityLabel = "Submit order"
button.accessibilityHint = "Double tap to submit your order for processing"
// Avoid redundant type information
// Bad: "Submit button"
// Good: "Submit order"
// Android
button.contentDescription = "Submit order"
button.accessibilityDelegate = object : View.AccessibilityDelegate() {
override fun onInitializeAccessibilityNodeInfo(
host: View,
info: AccessibilityNodeInfo
) {
super.onInitializeAccessibilityNodeInfo(host, info)
info.hintText = "Double tap to submit your order for processing"
}
}
Element Ordering
// iOS: Control focus order
view.accessibilityElements = [headerLabel, contentView, actionButton]
// Group related elements
containerView.isAccessibilityElement = false
containerView.accessibilityElements = [childElements]
<!-- Android: Traversal order -->
<Button
android:id="@+id/submitButton"
android:accessibilityTraversalAfter="@id/formFields"
android:accessibilityTraversalBefore="@id/cancelButton" />
Dynamic Content Announcements
// iOS: Announce changes
UIAccessibility.post(
notification: .announcement,
argument: "Order submitted successfully"
)
// Screen layout changed
UIAccessibility.post(notification: .layoutChanged, argument: newFocusElement)
// Android
view.announceForAccessibility("Order submitted successfully")
Color and Contrast
Minimum Contrast Ratios
| Text Size | Minimum Ratio (AA) | Enhanced (AAA) |
|---|---|---|
| Normal text | 4.5:1 | 7:1 |
| Large text (18pt+) | 3:1 | 4.5:1 |
| UI components | 3:1 | 3:1 |
Testing Contrast
// Calculate contrast ratio
func contrastRatio(foreground: UIColor, background: UIColor) -> CGFloat {
let fgLuminance = relativeLuminance(foreground)
let bgLuminance = relativeLuminance(background)
let lighter = max(fgLuminance, bgLuminance)
let darker = min(fgLuminance, bgLuminance)
return (lighter + 0.05) / (darker + 0.05)
}
Color Independence
Never use color as the only indicator:
// Bad: Only color indicates error
textField.layer.borderColor = UIColor.red.cgColor
// Good: Color + icon + text
textField.layer.borderColor = UIColor.red.cgColor
errorIcon.isHidden = false
errorLabel.text = "Please enter a valid email"
Text Scaling
Support dynamic type for users who need larger text:
// iOS: Dynamic Type
label.font = UIFont.preferredFont(forTextStyle: .body)
label.adjustsFontForContentSizeCategory = true
// SwiftUI
Text("Hello")
.font(.body)
.dynamicTypeSize(.large ... .accessibility5)
<!-- Android: Scalable text -->
<TextView
android:textSize="16sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
Motor Accessibility
Touch Target Size
Minimum touch target: 44x44 points (iOS) / 48x48dp (Android)
// iOS: Expand hit area
class LargeTouchButton: UIButton {
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
let expandedBounds = bounds.insetBy(dx: -10, dy: -10)
return expandedBounds.contains(point)
}
}
Gesture Alternatives
Provide alternatives to complex gestures:
// If swipe-to-delete is primary, also provide:
// 1. Edit mode with delete buttons
// 2. Long-press context menu
// 3. Accessibility custom actions
cell.accessibilityCustomActions = [
UIAccessibilityCustomAction(
name: "Delete",
target: self,
selector: #selector(deleteItem)
)
]
Switch Control Support
Ensure all functionality is reachable via switch control:
// Make custom controls focusable
customControl.isAccessibilityElement = true
customControl.accessibilityTraits = .button
// Avoid time-dependent interactions
// Bad: Auto-dismissing toast after 2 seconds
// Good: Dismissable notification with button
Hearing Accessibility
Captions and Transcripts
// AVFoundation captions
let asset = AVAsset(url: videoURL)
let group = asset.mediaSelectionGroup(forMediaCharacteristic: .legible)
let options = group?.options.filter {
$0.mediaType == .closedCaption
}
Visual Indicators for Audio
Replace audio-only feedback with visual alternatives:
// Bad: Only sound on error
AudioServicesPlaySystemSound(kSystemSoundID_Vibrate)
// Good: Sound + haptic + visual
if UIAccessibility.isVoiceOverRunning || !userPrefersSounds {
showVisualFeedback()
}
playSound()
provideHapticFeedback()
Cognitive Accessibility
Clear Language
- Use simple, direct language
- Avoid jargon and abbreviations
- Provide definitions for technical terms
Consistent Navigation
- Maintain consistent navigation patterns
- Use familiar icons with labels
- Provide clear back/cancel options
Error Prevention and Recovery
// Confirm destructive actions
let alert = UIAlertController(
title: "Delete Account?",
message: "This action cannot be undone. All your data will be permanently deleted.",
preferredStyle: .alert
)
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel))
alert.addAction(UIAlertAction(title: "Delete", style: .destructive) { _ in
self.deleteAccount()
})
Reduce Motion
Respect user preferences for reduced motion:
// iOS
if UIAccessibility.isReduceMotionEnabled {
// Use fade instead of slide animations
transition = .crossDissolve
} else {
transition = .slide
}
// SwiftUI
withAnimation(reduceMotion ? nil : .default) {
// Animation
}
Testing Accessibility
Automated Testing
// XCTest accessibility audit
func testAccessibility() throws {
let app = XCUIApplication()
app.launch()
try app.performAccessibilityAudit()
}
Manual Testing Checklist
Screen Reader Testing
- Navigate entire app with VoiceOver/TalkBack
- Verify all elements have meaningful labels
- Check reading order is logical
- Confirm all actions are accessible
- Test with screen reader at different speeds
Visual Testing
- Test with largest text size
- Verify contrast in light and dark modes
- Check color independence
- Test with color blindness simulators
- Verify focus indicators are visible
Motor Testing
- Test with Switch Control / Switch Access
- Verify all touch targets meet minimum size
- Check for gesture alternatives
- Test with one hand
- Verify no time-dependent interactions
Cognitive Testing
- Review all text for clarity
- Check error messages are helpful
- Verify confirmation for destructive actions
- Test with reduced motion enabled
- Check navigation consistency
Implementation Checklist
Critical (P0)
- All images have alt text / contentDescription
- All form fields have labels
- Color is not the only indicator
- Touch targets are 44x44 / 48x48 minimum
- App works with screen reader
High Priority (P1)
- Supports dynamic text sizing
- Meets WCAG AA contrast requirements
- Logical focus order
- Error messages are descriptive
- No time-dependent interactions
Medium Priority (P2)
- Supports reduced motion
- Gesture alternatives available
- Captions for video content
- Custom actions for complex interactions
- Semantic headings structure
Continuous
- Accessibility audit in CI pipeline
- User testing with people with disabilities
- Accessibility training for team
- Documentation of accessibility features
Resources
Guidelines
Testing Tools
- Accessibility Inspector (Xcode)
- Accessibility Scanner (Android)
- Colour Contrast Analyser
- axe DevTools
Conclusion
Accessibility is not a feature — it's a quality attribute that should be present from the start. Building accessibly:
- Start early — Retrofitting is harder than building right
- Test continuously — Include in your CI pipeline
- Listen to users — Real feedback from users with disabilities is invaluable
- Train your team — Everyone should understand accessibility basics
An accessible app is a better app for everyone.
Accessibility guidelines refined through enterprise mobile development with strict compliance requirements.
