@gauravsinghss/rn-a11y-check
v1.0.3
Published
React Native Accessibility Code Validation & Duplication Checker CLI
Maintainers
Readme
React Native Accessibility Code Validation & Duplication Checker (rn-a11y-check)
A standalone TypeScript CLI tool and AST-based validation engine for React Native codebases.
Objective & Philosophy
When implementing accessibility in large enterprise applications, accessibility work must follow a strict principle:
Accessibility work should primarily add the required accessibility properties and helpers without unnecessarily altering existing business logic, UI layout, styling, architecture, or behavior.
rn-a11y-check automatically inspects your React Native codebase and Git changes to detect:
- Duplicate accessibility properties on JSX elements.
- Conflicting accessibility implementations (e.g.
accessibilityElementsHiddencombined withaccessible). - Duplicate accessibility values across equivalent properties.
- Duplicate accessibility logic and helper functions across components.
- Unauthorized modifications to existing non-accessibility code in Git pull requests / branches.
- Violations of project-configured accessibility rules.
🔒 Read-Only Safety Guarantee
rn-a11y-check is 100% read-only against your application code.
- It never modifies
.ts,.tsx,.js, or.jsxfiles. - It never executes automatic fixes, reformatting, Prettier, or ESLint fixes.
- It never changes Git state, commits, stashes, or resets code.
- It only reads files, parses ASTs, analyzes Git diffs, and reports findings.
Installation & Setup
# Clone or navigate to the project
cd rn-a11y-check
# Install dependencies
npm install
# Build the TypeScript project
npm run build
# Run tests
npm testTo run directly via CLI:
# Using npm script
npm run a11y-check
# Using npx (when linked or installed in a project)
npx rn-a11y-checkCLI Usage
1. Scan Entire Project
Scans all configured files in the current repository:
npx rn-a11y-check2. Scan a Specific Directory or File
npx rn-a11y-check ./src/components
npx rn-a11y-check ./src/screens/AccountScreen.tsx3. Git Diff Mode
Validates changes in your current branch against a base branch (e.g. origin/develop or main):
npx rn-a11y-check --diff origin/developIn Diff mode, the tool:
- Inspects
git diff <base>...HEAD. - Verifies that all added/modified lines in JSX elements or statements are configured accessibility properties or helpers.
- Flags any modifications or deletions of existing business logic, styles, or non-accessibility props (Rule A11Y005).
- Runs AST accessibility rules on the modified files.
4. Baseline Mode
Generates .a11y-baseline.json recording the current accessibility state:
npx rn-a11y-check --baselineThe baseline records:
- Files containing accessibility implementations
- Configured accessibility properties used
- Component counts
Privacy Guarantee: The baseline contains no sensitive application data (no proprietary strings, no mock data, no secrets). It is deterministic and designed to be committed to Git.
5. Custom Configuration File
Specify a custom path to a configuration file:
npx rn-a11y-check --config ./custom-rn-a11y.config.json6. Output Formats
Human-readable terminal output (default):
npx rn-a11y-check --format textMachine-readable JSON output for CI pipelines or tooling:
npx rn-a11y-check --format json7. Interactive HTML Dashboard Report (index.html)
Every time rn-a11y-check completes, it automatically generates a modern, self-contained HTML dashboard report at ./a11y-report/index.html (and prints the full file path in the terminal):
# Generated automatically on every run
npx rn-a11y-check
# Customize the HTML report output path
npx rn-a11y-check --html-path ./custom-reports/index.html
# Disable HTML report generation if desired
npx rn-a11y-check --no-htmlDashboard Features:
- 📊 Overview Stats: Total files scanned, total issues, errors, warnings, and overall status.
- 🟡 Yellow Warning Cards: Prominently highlights accessibility warnings in yellow with exact filepath (
path:line:col), copyable file path, rule badges, and suggestions. - 🔴 Red Error Cards: Highlights errors in bold red with filepath, rule badges, code diffs (
+/-), and remediation suggestions. - 🔍 Interactive Filters: Instant filtering by severity (All, Errors Only, Warnings Only), rule selection dropdown, and live text search across filepaths, rules, messages, and properties.
- 🔒 100% Offline: Embedded CSS and JavaScript with zero external CDN dependencies.
Exit Codes
rn-a11y-check uses standard CI-friendly exit codes:
| Exit Code | Meaning |
| :--- | :--- |
| 0 | PASS — All checks passed without errors. |
| 1 | FAIL — Validation errors detected (e.g. A11Y001, A11Y005, or rules configured as error). |
| 2 | TOOL ERROR — Configuration error or file not found. |
Note: Rules configured as
warningwill report warnings in the console but will not fail CI (exit code 0), unless configured aserror.
Rules Reference
Rule A11Y001 — Duplicate Accessibility Props
- Severity:
error - Description: Detects duplicate accessibility properties on the same JSX element.
- Example violation:
<View accessible accessibilityLabel="Account" accessibilityLabel="Account" />
Rule A11Y002 — Conflicting Accessibility Properties
- Severity:
warning(configurable) - Description: Detects accessibility properties that conflict with each other on the same element.
- Default conflicting pairs:
accessibilityElementsHidden+accessibleimportantForAccessibility+accessibilityElementsHidden
- Example violation:
<View accessible={true} accessibilityElementsHidden={true} />
Rule A11Y003 — Duplicate Accessibility Values
- Severity:
warning(configurable) - Description: Detects duplicate accessibility values across configured equivalent properties on the same JSX component.
- Example violation:
<XbText a11yLabel="Transaction amount" accessibilityLabel="Transaction amount" />
Rule A11Y004 — Duplicate Accessibility Logic
- Severity:
warning - Description: Detects duplicated accessibility helper logic across different files using AST normalization and structural similarity analysis.
- Example violation:
// AccountCard.tsx const getAccessibilityLabel = () => `${title}, ${value}`; // PortfolioCard.tsx const getAccountA11y = () => `${heading}, ${amount}`;
Rule A11Y005 — Non-Accessibility Code Modification
- Severity:
error - Description: Used in Git Diff mode (
--diff). Ensures developers do not modify or delete existing non-accessibility code while implementing accessibility. - Scenarios:
- Scenario 1 (PASS): Pure accessibility additions:
<XbText style={styles.title} text={title} + a11yLabel={title} + accessibilityRole="text" /> - Scenario 2 (FAIL): Existing non-accessibility property modified:
<XbText - style={styles.title} + style={styles.headerTitle} text={title} + a11yLabel={title} /> - Scenario 3 (FAIL): Existing business logic modified:
- const title = getAccountTitle(account); + const title = getFormattedAccountTitle(account); - Scenario 4 (PASS): Accessibility helper function modified.
- Scenario 1 (PASS): Pure accessibility additions:
Rule RN001 — No Inline Styling
- Severity:
warning(configurable) - Description: Prevents inline style objects on JSX elements (
style={{ ... }}orstyle={[..., { ... }]}), which create new object references on every render and harm performance. - Example violation:
<View style={{ margin: 16, padding: 12 }} /> - Recommended fix: Extract styles to
StyleSheet.create(...).
Rule RN002 — Safe Property & Array Access (Optional Chaining)
- Severity:
warning(configurable) - Description: Enforces null/undefined safety using optional chaining (
?.) on data model objects and array index accesses to prevent runtimeTypeError: Cannot read property of undefinedcrashes. - Example violation:
const name = user.name; const first = items[0]; - Recommended fix:
const name = user?.name; const first = items?.[0];
Rule RN003 — No Static Text in JSX
- Severity:
warning(configurable) - Description: Detects hardcoded static text in JSX elements and text attributes, encouraging internationalization/localization (i18n) keys or variables.
- Example violation:
<Text>Total Portfolio Value</Text> <TextInput placeholder="Enter account number" /> - Recommended fix: Use localized string functions or variables:
<Text>{t('portfolio_value')}</Text> <TextInput placeholder={t('enter_account_number')} />
Configuration (rn-a11y.config.json)
Place rn-a11y.config.json at the root of your React Native project:
{
"projectName": "My_App",
"include": [
"src/**/*.{ts,tsx,js,jsx}",
"app/**/*.{ts,tsx,js,jsx}"
],
"exclude": [
"node_modules",
"android",
"ios",
"build",
"dist"
],
"accessibilityProps": [
"accessible",
"accessibilityLabel",
"accessibilityRole",
"accessibilityState",
"accessibilityValue",
"accessibilityHint",
"accessibilityActions",
"onAccessibilityTap",
"onAccessibilityMagicTap",
"onAccessibilityEscape",
"accessibilityViewIsModal",
"accessibilityElementsHidden",
"importantForAccessibility"
],
"customProps": [
"a11yLabel",
"a11yRole",
"a11yState",
"disableA11yScaling"
],
"equivalentProps": [
["a11yLabel", "accessibilityLabel"],
["a11yRole", "accessibilityRole"]
],
"conflictingProps": [
["accessibilityElementsHidden", "accessible"]
],
"rules": {
"A11Y001": "error",
"A11Y002": "warning",
"A11Y003": "warning",
"A11Y004": "warning",
"A11Y005": "error"
},
"similarityThreshold": 0.85
}CI / CD Integration Examples
GitHub Actions
name: Accessibility PR Check
on:
pull_request:
branches: [main, develop]
jobs:
a11y-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # Full history needed for git diff
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Validate Accessibility Diff
run: npx rn-a11y-check --diff origin/${{ github.base_ref }}GitLab CI
stages:
- test
a11y_validation:
stage: test
image: node:20
before_script:
- git fetch origin $CI_MERGE_REQUEST_TARGET_BRANCH_NAME
script:
- npm ci
- npx rn-a11y-check --diff origin/$CI_MERGE_REQUEST_TARGET_BRANCH_NAME
only:
- merge_requestsArchitecture & Future Extensibility
The tool decouples the CLI interface, AST parser, rule engine, Git analyzer, and reporter:
CLI (cli.ts)
│
▼
Scanner (scanner.ts)
├── AST Parser (parser.ts - Babel)
├── Git Diff Engine (git/diffChecker.ts)
├── Rule Registry (rules/registry.ts)
│ ├── A11Y001 (duplicateProps)
│ ├── A11Y002 (conflictingProps)
│ ├── A11Y003 (duplicateValues)
│ ├── A11Y004 (duplicateLogic)
│ └── A11Y005 (nonA11yChanges)
└── Reporters
├── Text (reporter/textReporter.ts)
└── JSON (reporter/jsonReporter.ts)Adding New Rules
Future rules (such as A11Y006 Missing accessibilityLabel, A11Y007 Incorrect accessibilityRole, etc.) can be registered by creating a rule implementing the Rule interface:
import { Rule, RuleContext } from './types';
export const missingLabelRule: Rule = {
id: 'A11Y006',
name: 'Missing accessibilityLabel',
description: 'Flags accessible touchable elements missing accessibilityLabel.',
defaultSeverity: 'warning',
runOnFile(context: RuleContext) {
// AST traversal logic here
return [];
},
};And registering it with registry.registerRule(missingLabelRule).
