| name | mobile-accessibility-testing |
|---|---|
| description | Automated and manual accessibility auditing, WCAG compliance, and screen reader testing for mobile apps |
| standards-version | 1.6.3 |
Use this skill when the developer asks about:
- Accessibility (a11y) testing or auditing
- WCAG 2.1 AA compliance for mobile
- Screen reader testing with VoiceOver or TalkBack
- Missing accessibility labels, roles, or hints
- Touch target sizing requirements
- Dynamic type / font scaling support
- Reduced motion preferences
- Color contrast ratio validation
- CI integration for accessibility checks
| Input | Description |
|---|---|
| Framework | expo (React Native) or flutter |
| Scope | Full audit, specific screen, or CI setup |
Use mobile_auditAccessibility for a static analysis scan:
Use MCP tool: mobile_auditAccessibility
framework: "expo"
The tool scans source files for:
- Missing
accessibilityLabel/Semanticson interactive elements - Small touch targets (below 44px)
- Images without alt text
- Color-only state indicators
- Missing
accessibilityRoleannotations
// Bad
<TouchableOpacity onPress={onDelete}>
<Icon name="trash" />
</TouchableOpacity>
// Good
<TouchableOpacity
onPress={onDelete}
accessibilityLabel="Delete item"
accessibilityRole="button"
accessibilityHint="Removes this item from your list"
>
<Icon name="trash" />
</TouchableOpacity>// Minimum 44x44pt
<Pressable
onPress={onPress}
style={{ minWidth: 44, minHeight: 44 }}
hitSlop={8}
>
<Icon name="settings" size={24} />
</Pressable>import { PixelRatio } from "react-native";
const fontScale = PixelRatio.getFontScale();
// Ensure layouts don't break at 2x font scale
// Use maxFontSizeMultiplier on Text to cap if layout breaks
<Text maxFontSizeMultiplier={1.5}>Content</Text>import { AccessibilityInfo } from "react-native";
const [reduceMotion, setReduceMotion] = useState(false);
useEffect(() => {
AccessibilityInfo.isReduceMotionEnabled().then(setReduceMotion);
const sub = AccessibilityInfo.addEventListener(
"reduceMotionChanged",
setReduceMotion
);
return () => sub.remove();
}, []);import { AccessibilityInfo } from "react-native";
AccessibilityInfo.announceForAccessibility("Item deleted");// Bad
GestureDetector(
onTap: onDelete,
child: Icon(Icons.delete),
)
// Good
Semantics(
label: 'Delete item',
button: true,
hint: 'Removes this item from your list',
child: GestureDetector(
onTap: onDelete,
child: const Icon(Icons.delete),
),
)// Material guidelines: 48x48 logical pixels minimum
SizedBox(
width: 48,
height: 48,
child: IconButton(
onPressed: onSettings,
icon: const Icon(Icons.settings),
),
)MediaQuery(
data: MediaQuery.of(context).copyWith(textScaler: TextScaler.linear(2.0)),
child: child,
)final disableAnimations = MediaQuery.of(context).disableAnimations;SemanticsService.announce('Item deleted', TextDirection.ltr);- Settings → Accessibility → VoiceOver → On
- Swipe right to move to the next element
- Double-tap to activate the focused element
- Verify every interactive element is reachable and announced
- Verify images have descriptions or are hidden from VoiceOver
- Settings → Accessibility → TalkBack → On
- Swipe right to navigate forward
- Double-tap to activate
- Verify focus order follows visual layout
- Test all form inputs and custom controls
| Test | Pass Criteria |
|---|---|
| All buttons announced | Label read aloud, role stated |
| Images described | Alt text read or skipped if decorative |
| Touch targets | ≥ 44pt (iOS) / ≥ 48dp (Android) |
| Contrast ratio | ≥ 4.5:1 normal text, ≥ 3:1 large text |
| Font scaling | Layout intact at 200% font size |
| Focus order | Logical top-to-bottom, left-to-right |
| Reduced motion | Animations skipped when enabled |
import { render } from "@testing-library/react-native";
test("delete button has accessibility label", () => {
const { getByRole } = render(<DeleteButton />);
const btn = getByRole("button", { name: "Delete item" });
expect(btn).toBeTruthy();
});testWidgets('delete button has semantics', (tester) async {
await tester.pumpWidget(const MaterialApp(home: DeleteButton()));
expect(
find.bySemanticsLabel('Delete item'),
findsOneWidget,
);
});| Resource | URL |
|---|---|
| WCAG 2.1 (W3C) | https://www.w3.org/TR/WCAG21/ |
| RN Accessibility | https://reactnative.dev/docs/accessibility |
| Flutter Accessibility | https://docs.flutter.dev/accessibility-and-internationalization/accessibility |
| axe DevTools Mobile | https://www.deque.com/axe/devtools/mobile/ |
| iOS HIG Accessibility | https://developer.apple.com/design/human-interface-guidelines/accessibility |
User: "Run an accessibility audit on my app and fix any violations"
Assistant:
- Runs
mobile_auditAccessibilityto scan all source files - Reports findings grouped by severity (critical → minor) with WCAG references
- Fixes critical issues: adds
accessibilityLabelandaccessibilityRoleto interactive elements - Fixes serious issues: increases touch targets to 44px minimum, adds alt text to images
- Suggests manual testing protocol with VoiceOver/TalkBack
- Generates a11y test file with
mobile_generateTestFileto prevent regressions
| Tool | When |
|---|---|
mobile_auditAccessibility |
Run automated a11y scan on the project |
mobile_generateTestFile |
Generate a11y-focused test file |
- Decorative images not excluded - set
accessible={false}(RN) orexcludeFromSemantics: true(Flutter). - Custom components missing semantics - every interactive custom widget needs labels.
- Focus traps - modals and bottom sheets must trap focus and release it on close.
- Skipping platform testing - VoiceOver and TalkBack behavior differs; test on both.
- Hardcoded font sizes without scaling - use relative sizes or test at max scale.
- Color-only errors - form validation errors must include text or icons, not just red borders.
mobile-theming- semantic color tokens for consistent contrastmobile-component-patterns- accessible component architecturemobile-forms-validation- accessible form error handling