Treated as an engineering discipline, mobile accessibility makes the apparent gap between iOS and Android fade fast: both lean on the same standard, WCAG. This guide gathers, in one place, how WCAG 2.2's success criteria map to VoiceOver on iOS and TalkBack on Android, measurable criteria like touch target size and contrast, an automated+manual testing protocol, and the legal obligation in Europe.
💡 Pro Tip: Don't leave accessibility to the last sprint — touch target size and focus order get fixed with a rewrite, not a patch, if they aren't set up correctly during the UI component design stage.
Table of Contents
- WCAG's mobile equivalent: which criterion maps to what
- Why WCAG, and why it applies on mobile too
- Screen readers: VoiceOver and TalkBack semantics
- The practical difference: rotor vs. menu
- Touch target size, focus order, and navigation
- Why focus order is a separate issue
- Contrast, dynamic type, and reduced motion
- Accessibility in forms and error messages
- Automated testing: axe-core, Accessibility Scanner, and Accessibility Inspector
- The limits of automated testing
- Manual testing protocol
- Legal framework: from WCAG to the EAA
- FAQ
- How do WCAG criteria apply to a mobile app?
- What's the minimum touch target size in px?
- How do you test with VoiceOver and TalkBack?
- Is accessibility a legal requirement?
- Update (September 2026)
- Conclusion
- Sources
WCAG's mobile equivalent: which criterion maps to what
WCAG (Web Content Accessibility Guidelines) is W3C's official Recommendation and is also adopted as ISO/IEC 40500. WCAG 2.2 first became a Recommendation on October 5, 2023; an updated Recommendation followed on December 12, 2024 (W3C). Despite the name "Web," W3C's own introductory page notes that small-screen phone and smartwatch users also benefit from accessibility; WCAG itself is written platform-agnostic, so it applies directly to native mobile apps too.
The criteria that map most directly onto mobile interfaces are these:
WCAG 2.2 Criterion | Level | Mobile equivalent |
|---|---|---|
2.5.8 Target Size (Minimum) | AA | Touch targets at least 24×24 CSS pixels (with exceptions) |
1.4.3 Contrast (Minimum) | AA | At least 4.5:1 contrast ratio for normal text, 3:1 for large text |
2.3.3 Animation from Interactions | AAA | Motion triggered by interaction must be dismissible unless essential |
2.4.11 Focus Not Obscured (Minimum) | AA | The focused element must not be entirely hidden by author content |
2.4.13 Focus Appearance | AAA | The focus indicator must be sufficiently visible and distinguishable |
2.5.7 Dragging Movements | AA | Every function requiring dragging must have a non-dragging alternative |
3.2.6 Consistent Help | A | Help mechanisms must stay in a consistent location across pages |
Five of these seven criteria (2.4.11, 2.4.13, 2.5.7, 2.5.8, 3.2.6) are part of the nine new success criteria that arrived with WCAG 2.2; two (1.4.3, 2.3.3) were carried over from earlier versions. Auditing your app against this table reduces a vague goal like "accessibility" to concrete, testable checklist items.
Why WCAG, and why it applies on mobile too
WCAG is written platform-agnostic: terms like "content" and "user interface component" apply to both web and native apps. W3C's official position is clear: web accessibility is a legal requirement in many situations — and, via the European Accessibility Act covered below, that requirement extends to native mobile apps too.
Screen readers: VoiceOver and TalkBack semantics
Both platforms' screen readers work with touch gestures, but their navigation logic doesn't overlap one-to-one.
Per Apple's official support documentation, VoiceOver is a "gesture-based screen reader" that lets someone operate an iPhone without seeing the screen. On moving to a new screen, VoiceOver plays a sound, then selects and reads out the first item — every screen transition gets an automatic "entry announcement."
Google's TalkBack page centers on controlling the device "eyes closed." Since TalkBack 9.1 (March 2021), multi-finger gestures are supported, adding quick access to global and local context menus alongside single-finger swipe navigation.
The practical difference: rotor vs. menu
VoiceOver users switch navigation mode (headings, links, form fields) with a circular control called the "rotor." On TalkBack, local/global context menus take over this role. That's why marking the same semantic information (heading, button, link) correctly is equally critical on both platforms: wrong role info means the user can't switch to the right navigation mode.
1// SwiftUI: give a button a meaningful, short accessibility label2Button(action: addToCart) {3 Image(systemName: "cart.badge.plus")4}5.accessibilityLabel("Sepete ekle")6.accessibilityHint("Ürünü sepete ekler")1// Jetpack Compose: describe the RESULT in contentDescription, not the visual detail2// (developer.android.com/guide/topics/ui/accessibility/apps recommendation:3// write "Submit", not "Submit button" — the system already reads the role)4IconButton(onClick = { addToCart() }) {5 Icon(6 imageVector = Icons.Filled.AddShoppingCart,7 contentDescription = "Sepete ekle"8 )9}You don't need to give text components (plain Text composables, say) a separate contentDescription — TalkBack already reads visible text automatically; the official Android documentation states this explicitly. For those who want to go deeper on iOS VoiceOver, there's a dedicated resource: the existing iOS Accessibility guide covers VoiceOver and Dynamic Type line by line; rather than repeat it, this article focuses on the two-platform WCAG map.
Touch target size, focus order, and navigation
Touch target size was tied to a measurable minimum by WCAG 2.2 SC 2.5.8: for pointer inputs, targets must be at least 24×24 CSS pixels (with specific exceptions, such as adjacent targets and inline text links).
On Android, the platform guideline goes above this minimum. The official Android developer documentation states: every interactive UI element's focusable area should be at least 48×48dp — noting "bigger is better." So Android's own Material Design recommendation sets a target noticeably above WCAG's 24×24 CSS pixel minimum.
Platform / standard | Minimum touch target | Source |
|---|---|---|
WCAG 2.2 SC 2.5.8 (AA) | 24×24 CSS pixels | W3C WCAG 2.2 Recommendation |
Android (Material Design recommendation) | 48×48dp | developer.android.com official guide |
We're not listing Apple's own numeric threshold in this table without a source; use Android's 48×48dp recommendation as the common, safe minimum across both platforms.
Why focus order is a separate issue
If touch target size is a "size" question, focus order is a "sequence" question. WCAG 2.2's SC 2.4.11 (Focus Not Obscured, AA) requires that, for users navigating by keyboard or an external switch control, the focused element not be entirely hidden by author content; SC 2.4.13 (Focus Appearance, AAA) goes further, requiring the focus indicator to be even more visible and distinguishable. On mobile, this specifically needs testing on screens with sticky headers/footers and in modal dialogs: can focus "escape" to an element left behind the modal?
Contrast, dynamic type, and reduced motion
Contrast and motion criteria are platform-independent, so they form common ground for both iOS and Android.
WCAG 2.2 SC 1.4.3 requires a contrast ratio of at least 4.5:1 for normal text and at least 3:1 for large text. This formula hasn't changed since WCAG 2.0 (2008) and is preserved as-is in 2.2. Verifying the contrast ratio in code instead of calculating it by hand prevents design tokens from drifting:
1// WCAG contrast ratio calculation — SC 1.4.3 formula2function relativeLuminance([r, g, b]) {3 const toLinear = (c) => {4 const s = c / 255;5 return s <= 0.03928 ? s / 12.92 : Math.pow((s + 0.055) / 1.055, 2.4);6 };7 const [R, G, B] = [r, g, b].map(toLinear);8 return 0.2126 * R + 0.7152 * G + 0.0722 * B;9}10 11function contrastRatio(hex1, hex2) {12 const parse = (hex) =>13 [1, 3, 5].map((i) => parseInt(hex.slice(i, i + 2), 16));14 const L1 = relativeLuminance(parse(hex1));15 const L2 = relativeLuminance(parse(hex2));16 const [lighter, darker] = L1 > L2 ? [L1, L2] : [L2, L1];17 return (lighter + 0.05) / (darker + 0.05);18}19 20console.log(contrastRatio("#333333", "#FFFFFF").toFixed(2));21// -> 12.63 (comfortably clears the WCAG 4.5:1 minimum)On motion, SC 2.3.3 Animation from Interactions (AAA), in effect since WCAG 2.1 (2018), requires that interaction-triggered motion be dismissible unless essential. That means designing an app that respects system-level preferences like iOS's "Reduce Motion" and Android's "Remove animations" — e.g., zeroing out transition duration or falling back to a motion-free transition.
Accessibility in forms and error messages
Form errors are the information most often lost on screen reader users — an error shown only visually (a red border, an edge icon) goes unnoticed by someone using VoiceOver or TalkBack. WCAG 2.2's SC 3.2.6 Consistent Help doesn't target form errors directly, but it generalizes the same principle: help mechanisms (here, the error description and fix suggestion) must appear in a consistent location and manner across pages.
- Label binding: every form field must be programmatically associated with a visible label (a placeholder alone doesn't substitute for a label).
- Error announcement: error text must be presented as text, not just via color, and focus must move to the error field.
- Field groups: related fields (e.g., address lines) should be read as a logical group, not as disconnected individual sentences.
- Required field marker: the "required" information must reach the screen reader as text, not just as a visual asterisk.
The same principle applies on the SwiftUI side: giving a text field only a placeholder isn't enough — the field itself, and its error state if any, must be defined as accessibility properties.
1// SwiftUI: programmatic label + error state announcement for a form field2TextField("", text: $email)3 .accessibilityLabel("E-posta adresi")4 .accessibilityValue(emailError ?? "")5 .accessibilityHint(emailError != nil ? "Hata: \(emailError!)" : "")Here, accessibilityValue reports the field's current state (valid/invalid) as a value; accessibilityHint adds a short hint guiding the user toward a fix. Showing the error only via a visible Text component may go unannounced unless focus order happens to land on it — which is why binding the error directly to the field's accessibility value is more reliable.
Automated testing: axe-core, Accessibility Scanner, and Accessibility Inspector
Automated testing catches a large portion of accessibility errors (contrast, missing labels, semantic role) without human intervention — but it doesn't replace manual testing.
axe-core (Deque Labs) describes itself as "an accessibility testing engine for websites and other HTML-based user interfaces" and states that it's designed to integrate seamlessly into existing test environments.
1// axe-core: basic scan over the page/screen2import axe from "axe-core";3 4axe.run(document, { runOnly: ["wcag2a", "wcag2aa"] }).then((results) => {5 console.log(`${results.violations.length} ihlal bulundu`);6 results.violations.forEach((v) => console.log(v.id, "-", v.help));7});On the Android side, the official testing documentation recommends four approaches together: manual testing, analysis tools (Accessibility Scanner), automated UI tests, and user testing. On iOS, the Accessibility Inspector built into Xcode checks labels and contrast on a live simulator/device.
Tool | Platform | What it catches |
|---|---|---|
axe-core | Web / hybrid views | Missing labels, contrast, ARIA role errors |
Android Accessibility Scanner | Android (native) | Touch target size, contrast, missing contentDescription |
Xcode Accessibility Inspector | iOS (native) | Label, trait, contrast, VoiceOver order |
ADB can be used to automatically turn TalkBack on and off in CI for end-to-end scenarios:
1# Enable / disable TalkBack on the CI device2adb shell settings put secure enabled_accessibility_services \3 com.google.android.marvin.talkback/com.google.android.marvin.talkback.TalkBackService4adb shell settings put secure accessibility_enabled 15 6# disable after the test7adb shell settings put secure accessibility_enabled 0This command can be sensitive to real-device differences; for a reliable result, back the CI automation up with a manual verification pass on a real device.
The limits of automated testing
axe-core, Accessibility Scanner, and Accessibility Inspector all reliably catch static semantic errors (missing labels, low contrast, wrong roles). But none can answer: can a user actually complete this flow with a screen reader, does focus order make sense, is a dynamic notification announced at the right moment? By nature, only a human turning on the screen reader and using it end to end can answer those three — which is why automated tools belong as the "first filter" and manual testing as the "acceptance criterion."
Manual testing protocol
Automated tools catch semantic errors but can't answer "is this flow actually usable?" The manual protocol's skeleton rests on each platform's own official screen reader flow:
- Turn it on via Settings: on iOS, Settings > Accessibility > VoiceOver; on Android, Settings > Accessibility > TalkBack. Both offer a tutorial first-launch flow — completing that flow yourself is necessary to get familiar with its gesture set.
- Navigate with the screen reader alone: without turning off the screen (watching the visual feedback), complete the entire primary flow (e.g., filling out and submitting a form) using only finger swipes and double taps.
- Verify focus order: on every screen, check that focus advances in a logical order (top to bottom, left to right) and that no interactive element is skipped.
- Check dynamic content announcement: verify whether the screen reader automatically announces an error message or loading state when it appears on screen.
- Test gesture conflicts: check whether the app's custom swipe/drag gestures conflict with the screen reader's own gestures (e.g., swiping left/right to change items).
Legal framework: from WCAG to the EAA
W3C's own accessibility introduction page opens with a clear sentence: web accessibility is a legal requirement in many situations. In Europe, this was tied to a concrete date: under the European Accessibility Act (EAA), member states had to transpose the directive into national law by June 2022. The European Commission's official EAA page lists the scope as including computers and operating systems, smartphones, e-books, and e-commerce. Directive 2019/882, underlying the EAA, also fixes that the measures apply from June 28, 2025 (EUR-Lex, Article 31) — an obligation with a set calendar.
- Transposition: member states were required to transpose the EAA into national law by June 2022.
- Scope: a broad list of products/services including computers/operating systems, smartphones, banking services, e-commerce, and e-books.
- Relationship: the EAA's technical requirements largely overlap with WCAG's success criteria — meaning the table in this article can be read as both a "best practice" and a "legal checklist."
GOLDEN TIP
The most valuable insight in this article
This tip holds the article's most important takeaway.
Easter Egg
You found a hidden gem!
There's a hidden detail in this section. Want to uncover it?
Reader Reward
The checklist in this section condenses the automated + manual test steps covered in the article into a single-page pre-delivery checklist; you can paste it as-is into a PR template or QA protocol.
FAQ
How do WCAG criteria apply to a mobile app?
Because WCAG is written platform-agnostic, it applies directly: you can map SC 2.5.8 touch target size, SC 1.4.3 text contrast, SC 2.4.11/2.4.13 focus visibility, SC 2.5.7 dragging alternative, and SC 3.2.6 consistent help one-to-one onto native iOS/Android components — the table above summarizes this mapping.
What's the minimum touch target size in px?
Per WCAG 2.2 SC 2.5.8, pointer targets must be at least 24×24 CSS pixels (with exceptions). Android's own official developer documentation goes above that and recommends at least 48×48dp; in practice, this value can be used as a common, safe target across both platforms.
How do you test with VoiceOver and TalkBack?
Both are turned on from device settings and start with their own tutorial flow. For manual testing, while visually following the screen, complete the primary flow start to finish using only the screen reader's gestures (swipe, double tap), and verify that focus order advances logically and dynamic content changes are announced.
Is accessibility a legal requirement?
In W3C's own words, yes — web accessibility is a legal requirement in many situations. In Europe, this is made concrete by the European Accessibility Act (EAA): member states had to transpose the directive into national law by June 2022, and the measures have applied since June 28, 2025; the scope includes smartphones and e-commerce.
Update (September 2026)
This article's body is based on version and tooling information as of March 5, 2025. What's changed since:
The EAA obligation has actually taken effect. The June 28, 2025 date mentioned in the body has arrived, and the measures the directive prescribes are now in actual force (covering smartphones, computers/operating systems, banking, and e-commerce, among others). For mobile apps operating in the EU or serving EU users, accessibility is no longer an optional improvement — it's an active obligation.
WCAG is still 2.2 — no major version break. W3C's current Recommendation is still WCAG 2.2 (the updated Recommendation dated December 12, 2024); the criteria table above remains current.
The platforms have shipped version upgrades. Apple's platform naming reached iOS 27 after iOS 26, and VoiceOver keeps working across these versions. On Android, the official AOSP version table lists Android 17 (API level 37), with TalkBack available in its current version (17.0). These upgrades haven't changed the WCAG-to-mobile mapping (touch target, contrast, focus order); the mapping above remains current.
Conclusion
Mobile accessibility becomes manageable when iOS and Android are treated not as two separate worlds to learn independently, but as two native implementations of WCAG 2.2's shared criteria. Embed these four — touch target, contrast, focus order, and automated testing — into CI and your PR template, and screen reader testing becomes a normal acceptance criterion instead of "extra work."
To go deeper: for iOS-specific VoiceOver and Dynamic Type details, the iOS Accessibility guide; for the production impact — accessibility included — of cross-platform architecture choices, the Flutter vs SwiftUI production comparison and the Compose Multiplatform production guide; to broaden your testing culture, the Flutter testing guide; and for the performance+accessibility balance on Android, the Jetpack Compose 1.7 performance guide.
Sources
- W3C WAI — Accessibility Fundamentals — WCAG's ISO/IEC 40500 status, legal requirement, and mobile scope statement
- W3C — WCAG 2.2 Recommendation — the primary text for all success criteria used in this article
- Android Developers — Make apps more accessible — touch target and contentDescription recommendations
- Android Developers — Test your app's accessibility — Accessibility Scanner and automated check recommendations
- Apple Support — Turn on and practice VoiceOver — VoiceOver's navigation/announcement behavior
- Google — TalkBack accessibility service — TalkBack gesture history and release notes
- Deque Labs — axe-core (GitHub) — automated accessibility testing engine
- European Commission — European Accessibility Act — EAA scope and transposition timeline
- EUR-Lex — Directive (EU) 2019/882 (ELI) — the EAA's primary text, Article 31: the measures apply from June 28, 2025
- AOSP — Codenames, tags, and build numbers — the Android 17 / API level 37 row
- Google Play — Android Accessibility Suite (TalkBack) — "What's new: TalkBack 17.0"
Tags
iOS Development News
Weekly Swift tips, SwiftUI tricks and iOS best practices. No spam, only valuable content.
We respect your privacy. You can unsubscribe at any time.

