UA Mobile App Guild

This roadmap was developed with the UA Mobile App Guild. Mobile developers at Arizona: join the #ua-mobile-dev Slack channel for real-time support, monthly audit syncs, and shared code reviews. Contact accessibility@arizona.edu for an invitation.

Overview

This roadmap outlines how mobile app teams at the University of Arizona can systematically reach and maintain WCAG 2.1 AA compliance. It’s organized into four phases — from baseline audit through ongoing maintenance — so you can plan sprints, allocate resources, and track progress concretely.

Who This Is For

What’s at Stake

Mobile apps created or maintained by public universities are covered under ADA Title II. Non-compliance after the deadline can result in Department of Justice enforcement actions, Office for Civil Rights complaints, and private lawsuits — in addition to the exclusion of students, faculty, and staff who depend on assistive technology.

Compliance Deadlines

Title II §35.200 — Web and Mobile App Accessibility

Entity Size Deadline WCAG Level
Population ≥ 50,000 April 24, 2026 WCAG 2.1 AA
Population < 50,000 April 26, 2027 WCAG 2.1 AA

UA falls in the ≥ 50,000 bracket. Treat April 24, 2026 as the hard compliance target for all flagship apps.

What “Compliance” Means for Mobile Apps

The DOJ rule applies WCAG 2.1 AA to mobile apps, covering:

Key point: Compliance is measured by user flows, not individual screens. If a user can’t complete a core task (register for classes, check grades, request accommodations) using assistive technology, that flow is non-compliant.

WCAG Mapping for Mobile

Not every WCAG criterion translates directly to mobile, and some are more critical than others. Here’s how the most impactful criteria map to mobile development:

Critical (blocks access entirely if missing)

WCAG Criterion Mobile Implementation
1.1.1 Non-text Content accessibilityLabel (iOS) / contentDescription (Android) on all meaningful images, icons, and controls
1.3.1 Info and Relationships Proper semantic roles and traits; group related elements with accessibilityElement containers
2.1.1 Keyboard Full external keyboard support; all elements reachable via Tab/Enter/Arrow; Switch Control / Switch Access support
2.4.3 Focus Order Logical reading order matching visual layout; accessibilityElements ordering (iOS) / traversalBefore/traversalAfter (Android)
4.1.2 Name, Role, Value Every control has an accessible name, correct role, and current state announced

High Priority (degrades experience significantly)

WCAG Criterion Mobile Implementation
1.3.4 Orientation Support both portrait and landscape (don’t lock orientation unless essential)
1.4.3 Contrast Minimum 4.5:1 for text, 3:1 for large text and UI components
1.4.4 Resize Text Support Dynamic Type (iOS) and font scaling (Android) up to 200% minimum
1.4.11 Non-text Contrast 3:1 for icons, borders, focus indicators, and custom controls
2.4.7 Focus Visible Clear focus indicator for external keyboard navigation
2.5.1 Pointer Gestures Provide single-pointer alternatives for multi-finger gestures (pinch, multi-tap)
2.5.5 Target Size Minimum 44×44 pt (iOS) or 48×48 dp (Android) for all interactive elements

Important (required for full AA compliance)

WCAG Criterion Mobile Implementation
1.4.10 Reflow Content reflows without horizontal scroll at all Dynamic Type sizes
1.4.12 Text Spacing Text adapts when users override spacing settings
2.4.6 Headings and Labels Screen headings are descriptive and announced as headings by VoiceOver/TalkBack
2.5.8 Target Size (Minimum) 24×24 CSS pixels minimum (WCAG 2.2)
3.3.1 Error Identification Errors announced by screen readers with UIAccessibility.post(.announcement) or AccessibilityEvent
3.3.2 Labels or Instructions Input fields have associated labels announced by assistive technology

Phase 1: Foundation (Weeks 1–4)

Goals

Key Actions

1. Run Baseline Audit

2. Fix Critical Blockers

3. Set Up Automated Testing

4. Document Current State

Phase 2: Core Compliance (Weeks 5–12)

Goals

Key Actions

1. Semantic Structure

2. Color Contrast

3. Dynamic Type / Font Scaling

4. Focus Order and Management

5. Error Handling

6. Motion and Animations

Phase 3: Enhanced Experience (Weeks 13–20)

Goals

Key Actions

1. Custom Actions

2. Live Regions

3. Voice Control Support

4. Switch Control / Switch Access

5. Haptic Feedback

Phase 4: Maintenance & Culture (Ongoing)

Goals

Automated Testing (Every PR)

Manual Testing Sprints (Quarterly)

Conduct a quarterly deep-dive accessibility audit:

  1. Navigate every screen with VoiceOver/TalkBack
  2. Complete all core user flows with an external keyboard only
  3. Test at maximum Dynamic Type / font scale
  4. Test in both orientations (portrait and landscape)
  5. Test in dark mode and high contrast mode
  6. Record screen reader sessions for comparison with previous quarter

User Testing (Semi-annually)

Team Training

Platform-Specific Code Examples

iOS — SwiftUI

// Accessible button with label and hint
Button(action: submitForm) {
    HStack {
        Image(systemName: "paperplane.fill")
            .accessibilityHidden(true) // decorative icon
        Text("Submit Application")
    }
}
.accessibilityLabel("Submit Application")
.accessibilityHint("Double-tap to submit your completed application")

// Accessible image
Image("campus-photo")
    .accessibilityLabel("Old Main building on the UA campus at sunset")

// Decorative image
Image("decorative-border")
    .accessibilityHidden(true)

// Grouping related elements
VStack {
    Text("Dr. Maria Chen")
    Text("Associate Director, Digital Accessibility")
    Text("accessibility@arizona.edu")
}
.accessibilityElement(children: .combine)
// VoiceOver reads all three as one unit

// Dynamic Type support — use system text styles
Text("Welcome to UA")
    .font(.title) // automatically scales with Dynamic Type

// Respecting Reduce Motion
@Environment(\.accessibilityReduceMotion) var reduceMotion

withAnimation(reduceMotion ? nil : .easeInOut) {
    showContent.toggle()
}

// Announcing dynamic content changes
UIAccessibility.post(
    notification: .announcement,
    argument: "Application submitted successfully"
)

iOS — UIKit

// Accessible label on a custom control
let favoriteButton = UIButton(type: .custom)
favoriteButton.setImage(UIImage(systemName: "heart"), for: .normal)
favoriteButton.accessibilityLabel = "Add to favorites"
favoriteButton.accessibilityTraits = .button

// Toggle state
favoriteButton.accessibilityValue = isFavorited ? "Favorited" : "Not favorited"

// Custom accessibility action
let deleteAction = UIAccessibilityCustomAction(
    name: "Delete item",
    target: self,
    selector: #selector(deleteItem)
)
cell.accessibilityCustomActions = [deleteAction]

// Dynamic Type
label.font = UIFont.preferredFont(forTextStyle: .body)
label.adjustsFontForContentSizeCategory = true

Android — Jetpack Compose

// Accessible button
Button(
    onClick = { submitForm() },
    modifier = Modifier.semantics {
        contentDescription = "Submit Application"
    }
) {
    Icon(
        imageVector = Icons.Default.Send,
        contentDescription = null // decorative, text is the label
    )
    Text("Submit Application")
}

// Accessible image
Image(
    painter = painterResource(R.drawable.campus_photo),
    contentDescription = "Old Main building on the UA campus at sunset"
)

// Decorative image
Image(
    painter = painterResource(R.drawable.decorative_border),
    contentDescription = null // null = decorative in Compose
)

// Heading semantics
Text(
    text = "Application Status",
    modifier = Modifier.semantics { heading() },
    style = MaterialTheme.typography.headlineMedium
)

// Live region for dynamic updates
Text(
    text = statusMessage,
    modifier = Modifier.semantics {
        liveRegion = LiveRegionMode.Polite
    }
)

// Minimum touch target
IconButton(
    onClick = { toggleFavorite() },
    modifier = Modifier.size(48.dp) // meets 48dp minimum
) {
    Icon(
        imageVector = if (isFavorited) Icons.Filled.Favorite 
                      else Icons.Outlined.FavoriteBorder,
        contentDescription = if (isFavorited) "Remove from favorites" 
                             else "Add to favorites"
    )
}

// Respecting Reduce Motion
val reduceMotion = LocalReducedMotion.current
val animationSpec = if (reduceMotion) snap() else tween(300)

Android — XML Views

// Setting accessibility properties programmatically
imageView.contentDescription = "Old Main building on the UA campus at sunset"

// Making a custom view accessible
customControl.importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_YES
customControl.accessibilityDelegate = object : View.AccessibilityDelegate() {
    override fun onInitializeAccessibilityNodeInfo(
        host: View, info: AccessibilityNodeInfo
    ) {
        super.onInitializeAccessibilityNodeInfo(host, info)
        info.roleDescription = "Slider"
        info.text = "Volume: $currentValue percent"
        info.addAction(
            AccessibilityNodeInfo.AccessibilityAction(
                AccessibilityNodeInfo.ACTION_SCROLL_FORWARD,
                "Increase volume"
            )
        )
    }
}

// Announcing updates
announceForAccessibility("Application submitted successfully")

// Touch target in XML layout
<ImageButton
    android:layout_width="48dp"
    android:layout_height="48dp"
    android:contentDescription="@string/settings"
    android:src="@drawable/ic_settings" />

Testing Matrix

Required Device/AT Combinations

Test each core user flow on these combinations. The matrix covers the most common assistive technology pairings based on the WebAIM Screen Reader Survey and platform usage data.

Priority Device OS Screen Reader Browser / Context Notes
Primary iPhone (recent) iOS 17+ VoiceOver Safari / Native Most common iOS AT combo
Primary Android (Pixel or Samsung) Android 14+ TalkBack Chrome / Native Most common Android AT combo
Secondary iPad iPadOS 17+ VoiceOver + Keyboard Safari / Native Tests keyboard + touch combo
Secondary Any Android Android 14+ Switch Access Native Tests sequential navigation
Tertiary iPhone iOS 17+ Voice Control Native Tests voice-based interaction
Tertiary Any device Any External keyboard only Native Tests keyboard-only (no AT)

Core User Flows to Test

For each flow, document pass/fail across all required device/AT combinations:

Flow What to Verify
Authentication Login, MFA, password reset — all completable with each AT
Primary task The main thing users do (e.g., register for classes, check grades)
Search Find content, filter results, navigate to result
Settings/Profile Change preferences, update information
Notifications Receive, read, and act on notifications
Error recovery Encounter an error, understand it, correct it

Testing Checklist per Screen

Implementation Checklist

Phase 1 — Foundation (Weeks 1–4)

Phase 2 — Core Compliance (Weeks 5–12)

Phase 3 — Enhanced Experience (Weeks 13–20)

Phase 4 — Maintenance (Ongoing)

Resources

Platform Documentation

Testing Tools

On This Site

Need Help?

Contact accessibility@arizona.edu for guidance on mobile app accessibility, audit support, or questions about this roadmap.