mobile-accessibility
Skill409 starsChanged 6 months agoMITView on GitHub
Audit React Native, Expo, iOS, Android for accessibility. Review accessibilityLabel, accessibilityRole, accessibilityHint, touch targets (44x44pt min), screen reader support, and platform semantics.
- Installs packages
2 other projects use a version of this.
Adopted by
Most recently taken up by
- wrsilva/reis-mobiletheir own version, first seen Nov 8, 2026
- simiancraft/simiancraft-skillstheir own version, first seen Jul 1, 2026
Other versions2 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 2
- simiancraft/simiancraft-skills★ 8same name
- wrsilva/reis-mobile★ 8same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository31 other files it gives its agents.
- AGENTS.mdAGENTS.md
- .claude/AGENTS.mdAGENTS.md
- claude-code-plugin/AGENTS.mdAGENTS.md
- claude-code-plugin/CLAUDE.mdCLAUDE.md
- CLAUDE.mdCLAUDE.md
- .github/copilot-instructions.mdCopilot instructions
- accessibility-rulesSkill
- ci-integrationSkill
- cognitive-accessibilitySkill
- data-visualization-accessibilitySkill
- design-systemSkill
- document-scanningSkill
- email-accessibilitySkill
- framework-accessibilitySkill
- github-a11y-scannerSkill
- github-analytics-scoringSkill
- github-scanningSkill
- github-workflow-standardsSkill
- help-url-referenceSkill
- legal-compliance-mappingSkill
- lighthouse-scannerSkill
- markdown-accessibilitySkill
- media-accessibilitySkill
- office-remediationSkill
- playwright-testingSkill
- python-developmentSkill
- report-generationSkill
- severity-mappingSkill
- testing-strategySkill
- web-scanningSkill
- web-severity-scoringSkill
The file
Files
Mobile Accessibility Skill
This skill provides mobile accessibility reference data for React Native, Expo, iOS, and Android auditing. Used by mobile-accessibility.agent.md.
React Native Accessibility Props - Full Reference
| Prop | Type | Values / Notes | WCAG SC | Required |
accessible | boolean | true marks the view as an accessibility node | 4.1.2 | Conditional |
accessibilityLabel | string | Human-readable name - overrides all child text | 1.1.1, 4.1.2 | Yes (all interactive + image elements) |
accessibilityLabelledBy | string / string[] | References ID(s) of labelling element | 1.3.1 | For form inputs |
accessibilityRole | string (see roles table) | Communicates element type to AT | 4.1.2 | Yes (all interactive elements) |
accessibilityHint | string | Additional context spoken after label + role | 1.3.3 | When action isn't obvious |
accessibilityState | object | {checked, disabled, expanded, selected, busy} | 4.1.2 | State-bearing elements |
accessibilityValue | object | {min, max, now, text} | 1.3.1 | Sliders, steppers, progress bars |
accessibilityActions | array | [{name, label}] - defines custom actions | 4.1.3 | Context menus, long-press alternatives |
onAccessibilityAction | function | Handles custom action triggers | 4.1.3 | Paired with accessibilityActions |
accessibilityLiveRegion | string | 'none' / 'polite' / 'assertive' | 4.1.3 | Dynamic content updates |
accessibilityViewIsModal | boolean | true traps VoiceOver focus inside modal | 1.3.4 | Modals, drawers, sheets |
accessibilityElementsHidden | boolean | iOS - hides element and children from VoiceOver | 1.1.1 | Decorative elements |
importantForAccessibility | string | Android - 'auto' / 'yes' / 'no' / 'no-hide-descendants' | 1.1.1 | Decorative / grouped elements |
accessibilityIgnoresInvertColors | boolean | iOS - preserves colors in Inverted Colors mode | - | Images, video |
aria-label | string | RN 0.73+ alias for accessibilityLabel | 1.1.1, 4.1.2 | Preferred in new code |
aria-labelledby | string | RN 0.73+ alias for accessibilityLabelledBy | 1.3.1 | Form inputs |
aria-describedby | string | RN 0.73+ alias for accessibilityHint | 1.3.3 | Additional description |
aria-role | string | RN 0.73+ alias for accessibilityRole | 4.1.2 | All interactive elements |
aria-checked | boolean / 'mixed' | RN 0.73+ alias for accessibilityState.checked | 4.1.2 | Checkboxes |
aria-disabled | boolean | RN 0.73+ alias for accessibilityState.disabled | 4.1.2 | Disabled elements |
aria-expanded | boolean | RN 0.73+ alias for accessibilityState.expanded | 4.1.2 | Accordions, dropdowns |
aria-selected | boolean | RN 0.73+ alias for accessibilityState.selected | 4.1.2 | Tabs, list items |
aria-busy | boolean | RN 0.73+ alias for accessibilityState.busy | 4.1.3 | Loading elements |
aria-hidden | boolean | RN 0.73+ - maps to importantForAccessibility / accessibilityElementsHidden | 1.1.1 | Decorative content |
aria-live | string | RN 0.73+ alias for accessibilityLiveRegion | 4.1.3 | Dynamic content |
aria-modal | boolean | RN 0.73+ alias for accessibilityViewIsModal | 1.3.4 | Modals |
Accessibility Role Values
| Role | Maps to (iOS) | Maps to (Android) | Use For |
'button' | UIAccessibilityTraitButton | AccessibilityNodeInfo.ROLE_BUTTON | Buttons, submission triggers |
'link' | UIAccessibilityTraitLink | AccessibilityNodeInfo.ROLE_LINK | Navigation links, external URLs |
'search' | - | ROLE_SEARCH | Search bars |
'image' | UIAccessibilityTraitImage | ROLE_IMAGE | Images (when accessible=true) |
'imagebutton' | UIAccessibilityTraitImage+Button | ROLE_BUTTON | Icon buttons |
'header' | UIAccessibilityTraitHeader | ROLE_HEADING | Headings |
'text' | UIAccessibilityTraitStaticText | ROLE_LABEL | Static text |
'adjustable' | UIAccessibilityTraitAdjustable | ROLE_SCROLL_VIEW | Sliders |
'checkbox' | - | ROLE_CHECKBOX | Checkboxes |
'combobox' | - | ROLE_DROP_DOWN_LIST | Dropdowns |
'menu' | - | ROLE_MENU | Menus |
'menuitem' | - | ROLE_MENU_ITEM | Menu items |
'menubar' | - | ROLE_MENU_BAR | Menu bars |
'progressbar' | UIAccessibilityTraitUpdatesFrequently | ROLE_PROGRESS_BAR | Progress indicators |
'radio' | - | ROLE_RADIO_BUTTON | Radio buttons |
'radiogroup' | - | - | Radio button groups |
'scrollbar' | - | ROLE_SCROLL_BAR | Scrollbars |
'spinbutton' | - | ROLE_SCROLL_VIEW | Steppers, number inputs |
'switch' | - | ROLE_SWITCH | Toggle switches |
'tab' | - | ROLE_TAB | Tab elements |
'tablist' | - | ROLE_TAB_LIST | Tab containers |
'timer' | UIAccessibilityTraitUpdatesFrequently | - | Countdown timers |
'toolbar' | - | ROLE_TOOL_BAR | Toolbars |
'grid' | - | ROLE_GRID | Data grids |
'list' | - | ROLE_LIST | Lists |
'listitem' | - | ROLE_LIST_ITEM | List items |
'summary' | UIAccessibilityTraitSummaryElement | - | Summary/status views |
'alert' | UIAccessibilityTraitCausesPageTurn | ROLE_ALERT | Alert dialogs |
'none' | UIAccessibilityTraitNone | ROLE_NONE | Suppress role |
Touch Target Size Requirements
| Platform | Minimum Size | Recommended | Standard |
| iOS | 44 x 44 pt | 44 x 44 pt | HIG |
| Android | 48 x 48 dp | 48 x 48 dp | Material Design |
| Web mobile | 44 x 44 CSS px (AAA) | 44 x 44 CSS px | WCAG 2.5.5 |
| Web mobile (AA, 2.2) | 24 x 24 CSS px with spacing | 44 x 44 CSS px | WCAG 2.5.8 |
Detection Pattern (React Native)
// Violation: TouchableOpacity below minimum
const styles = StyleSheet.create({
closeBtn: { width: 24, height: 24 }, // FAIL - below 44pt
iconBtn: { width: 32, height: 32 }, // FAIL - below 44pt
navBtn: { padding: 4 }, // CONDITIONAL - depends on content size
compliant: { width: 44, height: 44 }, // PASS
compliantWithPadding: { padding: 12 }, // PASS if content >= 20pt
});iOS UIAccessibility - Quick Reference
SwiftUI Modifiers
| Modifier | Purpose |
.accessibilityLabel("...") | Overrides spoken name |
.accessibilityHint("...") | Spoken usage hint (after pause) |
.accessibilityValue("...") | Spoken current value |
.accessibilityHidden(true) | Removes from VoiceOver tree |
.accessibilityElement(children: .combine) | Merges child elements into one node |
.accessibilityElement(children: .contain) | Groups children as sub-elements |
.accessibilityElement(children: .ignore) | Container becomes accessible, children hidden |
.accessibilityAddTraits(.isButton) | Adds role trait |
.accessibilityRemoveTraits(.isImage) | Removes wrong role trait |
.accessibilityInputLabels(["..."]) | Voice Control activation labels |
.accessibilitySortPriority(n) | Overrides VoiceOver reading order (higher = earlier) |
.accessibilityAction(named: "...", {}) | Custom action in the Actions rotor |
.accessibilityActivationPoint(CGPoint) | Override activation tap point |
.accessibilityCustomContent("label", "value") | Extra info in Accessibility Inspector |
SwiftUI Trait Values
isButton, isHeader, isLink, isImage, isStaticText, isSelected, isKeyboardKey, isSearchField, playsSound, isModal, updatesFrequently, startsMediaSession, allowsDirectInteraction, causesPageTurn, isTabBar, isSummaryElement
UIKit Properties
| Property | Type | Notes |
isAccessibilityElement | Bool | Set true on custom views |
accessibilityLabel | String? | Overrides spoken name |
accessibilityHint | String? | Spoken after pause |
accessibilityValue | String? | Current value (sliders, progress) |
accessibilityTraits | UIAccessibilityTraits | Bitfield of traits (.button, .header, etc.) |
accessibilityFrame | CGRect | Determines VoiceOver focus rect |
accessibilityActivate() | func | Override activation behavior |
accessibilityElements | [Any]? | Set container's VoiceOver child order |
shouldGroupAccessibilityChildren | Bool | Groups all children into single node |
accessibilityViewIsModal | Bool | true = VoiceOver trapped inside |
accessibilityElementsHidden | Bool | Hides all children from VoiceOver |
Android Jetpack Compose Semantics - Quick Reference
| Modifier / Property | Purpose |
semantics { contentDescription = "..." } | Accessible name |
semantics { role = Role.Button } | Element role |
semantics { stateDescription = "..." } | Current state text |
semantics { heading() } | Marks as heading |
semantics { selected = true/false } | Selected state |
semantics { toggleableState = ToggleableState.On } | Toggle state |
semantics { onClick(label = "...", action = {...}) } | Click action with label |
semantics { disabled() } | Disabled state |
semantics { focused = true } | Force focus |
semantics { liveRegion = LiveRegion.Polite } | Live region announcements |
semantics { invisibleToUser() } | Hide from TalkBack |
semantics { mergeDescendants = true } | Merge child semantics into one node |
clearAndSetSemantics { ... } | Replace all descendant semantics |
Modifier.semantics(mergeDescendants = true) { } | Short merge pattern |
Role Values
Role.Button, Role.Checkbox, Role.DropdownList, Role.Image, Role.RadioButton, Role.Switch, Role.Tab
Common Violation Patterns and Fixes
RN-001: Missing accessibilityLabel on icon button
// VIOLATION
<TouchableOpacity onPress={close}>
<Icon name="x" size={20} />
</TouchableOpacity>
// FIX
<TouchableOpacity
onPress={close}
accessibilityRole="button"
accessibilityLabel="Close"
>
<Icon name="x" size={20} aria-hidden />
</TouchableOpacity>RN-002: Image missing label
// VIOLATION
<Image source={productImage} style={styles.product} />
// FIX - informational image
<Image
source={productImage}
style={styles.product}
accessibilityLabel="Blue suede shoes, size 10"
/>
// FIX - decorative image
<Image
source={decorativeBackground}
style={styles.bg}
accessible={false}
importantForAccessibility="no"
/>RN-003: TextInput missing label
// VIOLATION - placeholder is not a label
<TextInput placeholder="Email" value={email} onChangeText={setEmail} />
// FIX
<View>
<Text nativeID="emailLabel">Email address</Text>
<TextInput
value={email}
onChangeText={setEmail}
accessibilityLabelledBy="emailLabel"
accessibilityHint="Enter your email address"
keyboardType="email-address"
autoComplete="email"
/>
</View>RN-004: Checkbox missing state
// VIOLATION
<TouchableOpacity onPress={toggle}>
<Image source={checked ? checkedIcon : uncheckedIcon} />
<Text>Accept terms</Text>
</TouchableOpacity>
// FIX
<TouchableOpacity
onPress={toggle}
accessibilityRole="checkbox"
accessibilityState={{ checked }}
accessibilityLabel="Accept terms and conditions"
>
<Image source={checked ? checkedIcon : uncheckedIcon} accessible={false} />
<Text>Accept terms</Text>
</TouchableOpacity>RN-005: Modal not trapping focus
// VIOLATION - custom modal without VoiceOver trap
<View style={styles.modal}>
<Text>Are you sure?</Text>
<Button title="Confirm" onPress={confirm} />
</View>
// FIX - use Modal component (traps focus automatically) or set accessibilityViewIsModal
<Modal
visible={visible}
transparent
accessibilityViewIsModal={true} // traps VoiceOver
onRequestClose={close} // Android back button
>
<View style={styles.overlay}>
<Text>Are you sure?</Text>
<Button title="Confirm" onPress={confirm} />
<Button title="Cancel" onPress={close} />
</View>
</Modal>AND-001: Compose image missing content description
// VIOLATION
Image(
painter = painterResource(id = R.drawable.product),
contentDescription = null // null = decorative, but wrong for informational image
)
// FIX - informational
Image(
painter = painterResource(id = R.drawable.product),
contentDescription = stringResource(R.string.product_image_description)
)
// FIX - truly decorative
Image(
painter = painterResource(id = R.drawable.divider),
contentDescription = null,
modifier = Modifier.semantics { invisibleToUser() }
)Testing Tool Commands
iOS Accessibility Inspector (Xcode)
Xcode -> Xcode menu -> Open Developer Tool -> Accessibility Inspector
- Run audit: Click "Audit" tab -> "Run Audit" button
- Inspect: "Inspection" tab -> hover over elements in Simulator
- Keyboard: Use +F7 to toggle VoiceOver in SimulatorAndroid Accessibility Scanner
# Install (Play Store or adb)
adb install com.google.android.apps.accessibility.auditor
# Enable TalkBack via ADB (for CI)
adb shell settings put secure enabled_accessibility_services \
com.google.android.marvin.talkback/.TalkBackService
# Check accessibility node tree
adb shell uiautomator dump /sdcard/ui_dump.xml
adb pull /sdcard/ui_dump.xmlReact Native Testing Library
npm install --save-dev @testing-library/react-nativeimport { render, screen, fireEvent } from '@testing-library/react-native';
test('button has accessible name and role', () => {
render(<SubmitButton onPress={jest.fn()} />);
const btn = screen.getByRole('button', { name: /submit/i });
expect(btn).toBeTruthy();
});
test('checkbox updates state', () => {
render(<TermsCheckbox />);
const checkbox = screen.getByRole('checkbox', { name: /accept terms/i });
expect(checkbox).toHaveAccessibilityState({ checked: false });
fireEvent.press(checkbox);
expect(checkbox).toHaveAccessibilityState({ checked: true });
});Maestro (E2E)
# .maestro/accessibility-checks.yaml
appId: com.example.myapp
---
- launchApp
- assertVisible:
label: "Submit form"
- tapOn:
label: "Close"
- assertNotVisible:
label: "Are you sure?"Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

