Mobile Accessibility skill for Codex

Every core task, eyes closed, text at 200%. Codex audits core user tasks with the screen reader and the largest text size rather than only running a scanner, then fixes semantics in the stack's own API: SwiftUI, UIKit, Compose, Views, React Native or Flutter. It groups cards into single focus stops with custom actions, enforces 44pt and 48dp targets, wires performAccessibilityAudit and Espresso checks into tests, and reports findings against WCAG 2.2.

  • Mobile & Desktop
  • $mobile-accessibility
  • 165 lines
  • 2 files
  • ~2,050 tokens
  • MIT
  • Updated

Install

curl -fsSL codexskills.dev/install.sh \  | sh -s -- mobile-accessibility

Installs to ~/.agents/skills/mobile-accessibility/ for every project on this machine.

104-line POSIX script. No sudo, no telemetry. Read install.sh

Use it in Codex

Start a prompt with $mobile-accessibility, or pick it from /skills. You can also just describe the task: Codex reads the description below and loads the playbook when your request matches.

What Codex reads before loading it

Audits and fixes accessibility in iOS, Android, React Native and Flutter apps - VoiceOver and TalkBack labels, traits and focus order, Dynamic Type and Android font scaling to 200 percent, touch target sizes, color contrast, Reduce Motion, and automated checks with XCUITest performAccessibilityAudit, Accessibility Scanner and Espresso checks. Maps findings to WCAG 2.2 for compliance work such as the European Accessibility Act. Use when asked for an accessibility audit, when screen reader users report problems, or before a release that must meet WCAG.

Use it when

  • Running an accessibility audit before a release or for EAA or WCAG compliance
  • Screen reader users report unlabeled or unreachable controls
  • Text scaling truncates or overlaps content
  • Adding accessibility checks to UI test suites

Use something else when

How to check the result

performAccessibilityAudit and Espresso or Compose accessibility checks pass, and a manual VoiceOver and TalkBack walk of each core task succeeds at the largest text size.

The mistake it prevents

Fixing only what an automated scanner reports, while focus order, grouping and announcements that make a flow usable go untested.

~/.agents/skills/mobile-accessibility/SKILL.md

--- name: mobile-accessibility description: Audits and fixes accessibility in iOS, Android, React Native and Flutter apps - VoiceOver and TalkBack labels, traits and focus order, Dynamic Type… license: MIT ---

Done well means: a screen reader user can complete every core task without sighted help, text scales to the largest setting without truncating meaning, every control is at least 44x44 pt on iOS and 48x48 dp on Android, contrast meets WCAG AA, and automated checks run in CI so regressions fail the build.

Before changing anything

  1. Read AGENTS.md, then identify the UI stack per screen: SwiftUI, UIKit, Jetpack Compose, Android Views, React Native or Flutter. APIs differ by stack; fixes must use the stack’s own semantics, not overlays.
  2. List the core tasks (sign in, search, purchase, settings) and audit those first.
  3. Check whether the design system defines text styles and color tokens; most fixes belong there, not in individual screens.

Audit workflow

  • Run automated checks (below) to clear the mechanical issues
  • Walk each core task with VoiceOver (iOS) and TalkBack (Android), eyes off the screen
  • Repeat at the largest text size (iOS Accessibility sizes, Android font scale 2.0)
  • Check contrast of text and icons against their actual backgrounds, both themes
  • Turn on Reduce Motion and confirm animations calm down
  • Use Switch Control or a hardware keyboard to confirm focus reaches every control
  • Record each finding with screen, element, WCAG criterion, severity and fix

Thresholds

CheckTargetSource
Touch target44x44 pt iOS, 48x48 dp Android (visual can be smaller if the hit area is not)Apple HIG, Material
Minimum target, WCAG24x24 CSS px or adequate spacingWCAG 2.2 SC 2.5.8 (AA)
Text contrast4.5:1 normal, 3:1 large (18 pt, or 14 pt bold)SC 1.4.3
Icons, borders, focus indicators3:1 against adjacent colorsSC 1.4.11
Text resizeUsable at 200 percentSC 1.4.4
MotionRespect the OS reduce-motion settingSC 2.3.3 (AAA), platform guidance

Screen reader semantics

Every interactive element needs a name, a role and a state, announced in that order.

NeedSwiftUIUIKitComposeViewsReact NativeFlutter
Name.accessibilityLabel("Delete")accessibilityLabelcontentDescription in semanticscontentDescriptionaccessibilityLabelSemantics(label:)
Role.accessibilityAddTraits(.isButton)accessibilityTraitsrole = Role.ButtonAccessibilityDelegateCompat / widget typeaccessibilityRoleSemantics(button: true)
State.accessibilityValue, .isSelected traitaccessibilityValuestateDescription, selectedstateDescriptionaccessibilityStateSemantics(checked:, selected:)
Group.accessibilityElement(children: .combine)container with one elementModifier.semantics(mergeDescendants = true) {}focusable parent + screenReaderFocusableaccessible={true} on parentMergeSemantics
Hide decoration.accessibilityHidden(true)isAccessibilityElement = falseModifier.clearAndSetSemantics {}importantForAccessibility="no"accessibilityElementsHidden / importantForAccessibilityExcludeSemantics
Heading.accessibilityAddTraits(.isHeader).header traitheading()accessibilityHeadingaccessibilityRole="header"Semantics(header: true)

Rules:

  • Labels describe purpose, not appearance: “Delete message”, not “trash icon”. Do not include the role (“button”) in the label; the screen reader adds it.
  • Group a card into one element with a combined label and expose secondary actions as custom actions (.accessibilityAction(named:), Compose customActions) instead of several tiny focus stops.
  • Announce async changes: iOS AccessibilityNotification.Announcement("Saved").post(), Compose liveRegion = LiveRegionMode.Polite, RN AccessibilityInfo.announceForAccessibility.
  • Move focus to new content after navigation or when a modal opens, and return it on close.
  • Images that convey information get a label; decorative images are hidden.
  • Custom gestures (swipe to delete, long press) need an equivalent accessible action.
HStack {
    AsyncImage(url: item.thumbnail).accessibilityHidden(true)
    VStack(alignment: .leading) {
        Text(item.title).font(.headline)
        Text(item.price, format: .currency(code: "EUR")).font(.subheadline)
    }
}
.accessibilityElement(children: .combine)
.accessibilityAction(named: "Add to cart") { cart.add(item) }
Row(
    Modifier
        .semantics(mergeDescendants = true) {
            customActions = listOf(CustomAccessibilityAction("Add to cart") { cart.add(item); true })
        }
        .clickable(onClickLabel = "Open product") { open(item) }
) { /* image with contentDescription = null, title, price */ }

Text scaling

  • iOS: use text styles (.font(.body), UIFont.preferredFont(forTextStyle:) with adjustsFontForContentSizeCategory = true). Scale custom sizes and spacing with @ScaledMetric. Switch horizontal layouts to vertical when dynamicTypeSize.isAccessibilitySize.
  • Android: text in sp, never dp; Android 14 scales nonlinearly up to 200 percent. Avoid fixed-height containers around text; let rows grow.
  • React Native: keep allowFontScaling on; cap extremes with maxFontSizeMultiplier only where layout truly cannot adapt. Flutter: respect MediaQuery.textScalerOf(context).
  • Never truncate essential text to one line at large sizes; wrap or reflow.

Touch targets and contrast

  • Expand hit areas without growing visuals: SwiftUI .contentShape(Rectangle()) with a .frame(minWidth: 44, minHeight: 44); Compose Modifier.minimumInteractiveComponentSize() (Material components apply it already); Views TouchDelegate or padding; RN hitSlop.
  • Compute contrast on the rendered colors, including text over images and gradients (add a scrim). Placeholder text and disabled-looking active controls are common failures.
  • Never use color alone to convey state; add an icon, text or shape.

Automated checks

// XCUITest (Xcode 15+ / iOS 17+)
func testCheckoutAccessibility() throws {
    let app = XCUIApplication(); app.launch()
    app.buttons["Checkout"].tap()
    try app.performAccessibilityAudit()
}
// Espresso with Android Views: enable once in the test class
@Before fun setUp() { AccessibilityChecks.enable().setRunChecksFromRootView(true) }
  • Android: Accessibility Scanner app for manual sweeps; Compose UI tests can run the same accessibility checks in recent ui-test versions (check the release notes for the enabling API in your version). Android Lint flags missing contentDescription.
  • React Native: eslint-plugin-react-native-a11y; Flutter: meetsGuideline(androidTapTargetGuideline), textContrastGuideline and labeledTapTargetGuideline in widget tests.
  • Automated checks catch perhaps a third of issues. Manual screen reader passes are required.

Useful device commands:

adb shell settings put system font_scale 2.0            # restore with 1.0
xcrun simctl ui booted content_size accessibility-extra-extra-extra-large

Gotchas

  • accessibilityIdentifier / testTag is for tests and is never read aloud.
  • A SwiftUI Button with only an Image(systemName:) reads the symbol name; always give it a label.
  • Custom tab bars and bottom sheets often trap or lose focus; test them explicitly.
  • Web content in a WebView follows web rules (ARIA); audit it separately with wcag-accessibility-audit.
  • Compliance (EAA since June 2025, ADA, Section 508) is a legal judgment; report WCAG conformance findings and let counsel decide on claims.

Output

## Accessibility audit: <app> <version>, <platforms>
| # | Screen | Element | Issue | WCAG | Severity | Fix |
|---|---|---|---|---|---|---|
**Automated:** <tools run, issue counts before/after>
**Manual:** <VoiceOver/TalkBack task walk results, largest text size result>
**Not tested:** <screens, devices, assistive tech>

Severity: blocker (task impossible), major (task possible with significant effort), minor.

What the zip contains

SKILL.md plus the interface file below. Nothing runs on install.

agents/openai.yamlgenerated from this page

Gives the Codex app a display name, a one-line description and a starter prompt for this skill.

interface:
  display_name: "Mobile Accessibility"
  short_description: "Every core task, eyes closed, text at 200%"
  default_prompt: "$mobile-accessibility Audit the checkout flow for VoiceOver and TalkBack and fix what you find"

About this skill

Written and maintained by the Codex Skills editors to the same editorial standard as every skill here: a selective description, a real workflow, known pitfalls and a way to verify the work. First published , last updated . MIT licensed. Something wrong or out of date? Tell us.

esc
Just launched

Codex Skills Pro is live

79 premium skills for Codex, each with scripts, references and an evidence report that proves the result.

Bought one by one$882.21
All 79 skills$49
94% off

That's $0.62 a skill, paid once.

Get all 79 for $49Or pick single skills from $4.99 →

Secure checkout by Creem