# Basic accessibility — VoiceOver/TalkBack labels, Dynamic Type, sufficient contrast

- **Pattern:** `ab-000457` (`app-store-policy-compliance.platform-standards.accessibility`)
- **Severity:** medium
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-000457
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-000457)

## Why it matters

Apple has tightened accessibility enforcement since iOS 17, and WCAG 2.2 SC 1.1.1 (non-text content) and SC 1.4.4 (resize text) apply to mobile apps under Section 508 §502.3.1 for any app serving government contracts or enterprise clients. `allowFontScaling={false}` is the single most common AI-generated accessibility anti-pattern: the AI produces it to prevent layout overflow, but it globally disables iOS Dynamic Type, making the app unusable for the 20% of iOS users who increase their system font size. VoiceOver users with zero `accessibilityLabel` props encounter an app where the screen reader reads raw component type names — 'button', 'button', 'image' — with no navigable context.

## Severity rationale

Medium because total absence of accessibility labels and disabled font scaling make the app functionally unusable for screen reader users, which Apple increasingly flags in review, and which creates legal exposure under ADA and Section 508 for enterprise distribution.

## Remediation

Add `accessibilityLabel` to every tappable element and replace all `allowFontScaling={false}` instances:

```tsx
// Before — screen reader reads "button"
<TouchableOpacity onPress={openSettings}>
  <Image source={icons.gear} allowFontScaling={false} />
</TouchableOpacity>

// After — screen reader reads "Open settings"
<TouchableOpacity
  onPress={openSettings}
  accessibilityLabel="Open settings"
  accessibilityRole="button"
>
  <Image source={icons.gear} />
</TouchableOpacity>
```

To find all `allowFontScaling={false}` instances: `grep -r 'allowFontScaling={false}' src/`. Remove them all — if a layout breaks with large fonts, fix the layout with `flexWrap` or `numberOfLines` instead of disabling scaling.

## Detection

- **ID:** `accessibility`
- **Severity:** `medium`
- **What to look for:** Examine interactive and content UI elements for accessibility implementation: (1) **VoiceOver/TalkBack labels** — In React Native, search for `accessibilityLabel`, `accessibilityHint`, and `accessibilityRole` props on `TouchableOpacity`, `Pressable`, `Button`, `Image`, and `TextInput` components. A pass requires that all tappable elements and images have meaningful `accessibilityLabel` props — not just the tappable root element, but also image-only buttons where the label is the only source of context. In Flutter, check `Semantics` widgets around tappable elements and images. In Swift/SwiftUI, check `.accessibilityLabel()` and `.accessibilityHint()` modifiers. In Kotlin Compose, check `semantics { contentDescription = "..." }` blocks. (2) **Dynamic Type / Font Scaling** — In React Native, search for hardcoded `fontSize` values not using `PixelRatio.getFontScale()` or system font size preferences. Also check if `allowFontScaling={false}` appears on any `Text` components — this disables Dynamic Type and is a common AI-generated pattern. In Flutter, check if `textScaleFactor` is hardcoded to `1.0`. (3) **Color contrast** — Look for hardcoded color combinations that may fail WCAG 2.1 AA contrast (4.5:1 for normal text, 3:1 for large text). Flag: light gray text on white backgrounds (`color: '#999'` on `backgroundColor: '#fff'`), white text on light-colored backgrounds. Count the number of `allowFontScaling={false}` instances as a severity signal.
- **Pass criteria:** All tappable elements and images have meaningful `accessibilityLabel` props. No `allowFontScaling={false}` on user-visible `Text` components. No obviously failing color contrast combinations. At least 70% of tappable elements have explicit accessibility labels.
- **Fail criteria:** Zero accessibility labels on interactive elements (total absence); `allowFontScaling={false}` on the majority of Text components; clearly failing contrast ratios throughout the app (e.g., #999 on #fff for body text).
- **Skip (N/A) when:** Never — basic accessibility applies to all apps.
- **Detail on fail:** `"No accessibilityLabel props found on any TouchableOpacity or Pressable components across 12 screens — VoiceOver users cannot navigate the app"` or `"allowFontScaling={false} found on 23 Text components — Dynamic Type is disabled throughout the app"`
- **Remediation:** Apple has become increasingly strict about accessibility since iOS 17, and Google Play requires accessibility compliance for certain Play Store badges and corporate procurement.
  1. Add `accessibilityLabel` to every `TouchableOpacity`, `Pressable`, and image-only button: `<TouchableOpacity accessibilityLabel="Open settings menu">`
  2. Remove all `allowFontScaling={false}` props — let the OS control font scaling
  3. For color contrast, use the Accessibility Inspector (Xcode) or Android Accessibility Scanner to identify failing combinations
  4. For icons used as buttons, add `accessibilityRole="button"` to clarify the element's purpose to screen reader users

  Review the configuration in `src/` or `app/` directory for implementation patterns.

## External references

- wcag 1.1.1
- wcag 1.4.3
- wcag 1.4.4
- section-508 502.3.1
- external apple-accessibility-guidelines — https://developer.apple.com/design/human-interface-guidelines/accessibility

Taxons: accessibility, regulatory-conformance

HTML version: https://auditbuffet.com/patterns/ab-000457
