What CI checks reliably catch, and where manual testing remains essential
Automated accessibility checks reliably catch missing labels, undersized touch targets, and contrast errors, and can be wired directly into the CI pipeline so a regression surfaces before it reaches the user. What they cannot catch is whether a screen reader announcement actually makes sense, or whether a navigation order is logical for a blind user. This article covers which problems can be detected automatically in React Native, how to integrate accessibility checks into the CI pipeline, and where the line to manual screen reader testing needs to be deliberately drawn.
Table of Contents
- 1. What Automated Accessibility Tests Can and Cannot Do
- 2. Which Problems Can Be Reliably Detected Automatically
- 3. Static Analysis with eslint-plugin-react-native-a11y
- 4. Runtime Checks with Detox and Accessibility Snapshots
- 5. Automatically Checking Color Contrast
- 6. Integrating Accessibility Checks into the CI Pipeline
- 7. Where Automated Tests Hit Their Limits
- 8. Manual Screen Reader Testing as a Necessary Complement
- 9. Building an Accessibility Culture in the Team
- 10. Summary
- 11. FAQ
1. What Automated Accessibility Tests Can and Cannot Do
In many teams, accessibility only gets checked late in the development process, usually manually and sporadically right before a release, which means problems often surface very late and are expensive to fix. Automated accessibility tests structurally move that check earlier: they run on every pull request, catch structural problems that can be formally derived from the component tree, and prevent already-fixed bugs from silently regressing.
Setting realistic expectations matters here: automated tools reliably detect whether an interactive element has an accessibilityLabel, whether a touch target falls below the minimum size, or whether a color contrast sits below the WCAG threshold. Whether the order in which a screen reader navigates through a screen makes sense to an actual user, or whether an announcement is worded understandably, remains a question no automated tool can answer, because that requires genuine human language understanding.
2. Which Problems Can Be Reliably Detected Automatically
Missing accessibilityLabel values on interactive elements like TouchableOpacity or Pressable are the most common and most easily automatable error, since it can be formally checked whether an element has an onPress prop but neither a label nor an accessible name derivable from text children. Touch target size can be checked just as reliably: an interactive element with a rendered area below the forty-four by forty-four or forty-eight by forty-eight density-independent pixels recommended by Apple and Google can be determined purely geometrically from the layout tree.
Color contrast between text and background can also be fully automated, provided both color values are statically known at test time, for instance via a WCAG contrast formula that produces a numeric contrast value and checks it against the 4.5-to-1 threshold for normal text or 3-to-1 for large text. It is also possible to automatically check whether images with semantic meaning have an accessibilityLabel and whether purely decorative images are correctly excluded from screen reader navigation via accessibilityElementsHidden or importantForAccessibility="no".
3. Static Analysis with eslint-plugin-react-native-a11y
The simplest entry point for automated accessibility checking is an ESLint plugin that statically analyzes the component tree already at development time, without the app ever needing to render. eslint-plugin-react-native-a11y checks, among other things, whether TouchableOpacity and Pressable elements have an accessibilityLabel, whether images with an onPress handler are additionally marked as accessible, and whether accessibilityRole is set correctly when an element carries a particular interaction semantic.
The big advantage of this layer is speed: ESLint rules run in milliseconds and can be wired in as a pre-commit hook or as live feedback directly in the editor, so a developer often sees accessibility problems while writing the code, not just in a separate CI step later. The downside is that purely static analysis has no knowledge of actual rendered layout dimensions and therefore cannot reliably check touch target sizes or actual contrast values that depend on dynamic styles.
npm install --save-dev eslint-plugin-react-native-a11y
# .eslintrc.js
module.exports = {
plugins: ["react-native-a11y"],
extends: ["plugin:react-native-a11y/all"],
rules: {
"react-native-a11y/has-accessibility-hint": "warn",
"react-native-a11y/has-valid-accessibility-role": "error",
},
};
4. Runtime Checks with Detox and Accessibility Snapshots
Static analysis alone does not cover problems that only arise at runtime, say a dynamically computed label that stays empty for certain data combinations, or a touch target whose actual rendered size is only known after the layout pass. Detox exposes actually rendered accessibility properties of an element at test runtime via getAttributes(), making it possible to write targeted assertions that check whether an element in the real app state actually has a non-empty label and a sufficient area.
For touch target size, the actual rendered width and height of an element can be read via getAttributes() and checked against the minimum size of 44 by 44 points on iOS or 48 by 48 density-independent pixels on Android. This kind of test can be systematically integrated as its own test case for every interactive screen of the app into the existing E2E suite, without building a separate accessibility test infrastructure.
// e2e/accessibility/checkoutButton.a11y.test.ts
import { device, element, by } from "detox";
describe("Checkout Button - Accessibility", () => {
beforeEach(async () => {
await device.launchApp({ newInstance: true });
});
it("has a non-empty accessibility label and a valid touch target", async () => {
const button = element(by.id("checkout-submit-button"));
const attributes = await button.getAttributes();
expect(attributes.label).toBeTruthy();
expect(attributes.label.length).toBeGreaterThan(0);
// iOS: minimum size 44x44pt, Android: 48x48dp
const minSize = device.getPlatform() === "ios" ? 44 : 48;
expect(attributes.frame.width).toBeGreaterThanOrEqual(minSize);
expect(attributes.frame.height).toBeGreaterThanOrEqual(minSize);
});
});
5. Automatically Checking Color Contrast
Color contrast is most reliably checked directly in the design system, before it is even used in components: a simple script that checks every defined text-background combination in the color token system against the WCAG contrast formula catches violations already at the design token level, long before a single component gets rendered. This is far more efficient than checking contrast per rendered screen, since a token-level error would otherwise potentially repeat itself across dozens of screens simultaneously.
For cases where colors are computed dynamically at runtime, say user-specific theme colors or status indicators, a pure token check is not sufficient. A complementary runtime check that extracts actually rendered color values via a snapshot test and checks them against the contrast formula is worth adding here, though at noticeably higher implementation cost than the token layer, which is why this effort usually only pays off for areas with genuinely dynamic colors.
6. Integrating Accessibility Checks into the CI Pipeline
A sensible CI setup tiers accessibility checks by speed and reliability: ESLint rules run as the fastest step on every push and already block a merge for obvious errors like missing labels. Detox-based runtime checks run as part of the existing E2E suite, typically somewhat less often, say only on pull requests instead of on every single commit, since they need an actual simulator or emulator launch and correspondingly cost more time.
Failed accessibility checks should, like other test failures, block the merge rather than just produce a warning, since warnings in practice tend to get ignored fairly often. For existing projects with many historically accumulated violations, a gradual rollout is worth considering: new and changed screens get checked strictly right away, while unchanged legacy code initially only produces a warning until it gets addressed as part of regular maintenance work.
7. Where Automated Tests Hit Their Limits
An element can have a technically correct, non-empty accessibilityLabel and still be useless to a screen reader user if the label just says something like "Button" instead of a concrete description of the action, like "Go to checkout". No automated tool can judge whether a label is understandable in content and makes sense in context, because that requires genuine language comprehension and knowledge of user expectations, not just a formal check for the presence of a string.
Just as little can the logical reading and navigation order of a screen be automatically evaluated for a screen reader, particularly for complex layouts with multiple side-by-side regions, where the visual arrangement does not necessarily match a sensible announcement order. Dynamic behavior such as live regions, meant to inform users of asynchronous state changes, say a success message after submitting a form, can only be partially checked automatically, since the timing and actual screen reader announcement are heavily platform- and version-dependent.
8. Manual Screen Reader Testing as a Necessary Complement
Manual testing with VoiceOver on iOS and TalkBack on Android therefore remains an indispensable part of any serious accessibility process, particularly for new, complex screens and for critical flows like checkout or registration. A sensible test procedure navigates the entire screen exclusively via screen reader gestures, without visually looking at the display, to experience whether the order and the announcements actually add up to a coherent flow.
In practice, a fixed combination works well: automated checks as a safety net against regressions on every commit, complemented by manual screen reader review as a fixed part of the review process for every new or substantially changed screen, ideally done by someone who regularly works with assistive technologies or at least follows a structured checklist. Where possible, feedback from actual screen reader users provides the most reliable insight, something neither automation nor internal manual testing can fully substitute for.
9. Building an Accessibility Culture in the Team
Automated checks reach their full value only once embedded in a broader team practice: a short accessibility checklist in the pull request template, a regular session where new screens get walked through together with a screen reader, and design reviews that check contrast values and touch target sizes before implementation rather than fixing them in code afterward. This organizational embedding often decides long-term success more strongly than the choice of a specific test tool.
A team that treats accessibility purely as an automated CI step will prevent obvious regressions, but rarely build a fundamentally well-accessible product, because the genuinely hard decisions, say how a complex data filter should be sensibly structured for screen reader users, require human judgment. Automation and manual testing are therefore not alternatives to each other, but two necessary layers of the same process.
| Problem | Automatically Detectable? | Tool | Manual Review Needed? |
|---|---|---|---|
| Missing accessibilityLabel | Yes, reliably | ESLint plugin, Detox getAttributes() | No, except content quality |
| Touch target too small | Yes, geometrically measurable | Detox getAttributes() at runtime | No |
| Color contrast below WCAG threshold | Yes, for static colors | Token check script, contrast formula | Only additionally for dynamic colors |
| Unclear label text | No | No automated tool suitable | Yes, mandatory |
| Sensible navigation order | No | No automated tool suitable | Yes, mandatory |
| Live region announcements on state change | Partially | Detox with limitations | Yes, recommended |
Mironsoft
React Native app development and Magento integration
A mobile app for the Magento shop that actually runs smoothly?
We build React Native apps cleanly connected to the Magento REST or GraphQL API, from the first line of code to publishing on the App Store and Google Play.
App Concept
Plan the architecture and feature scope of a Magento-connected app together.
Magento API Integration
Cleanly connect product catalog, cart, and checkout to the shop API.
Store Publishing
Guide the App Store and Google Play release process without pitfalls.
10. Summary
Automated Accessibility Testing: The Essentials at a Glance
Reliably automatable
Missing labels, undersized touch targets, and color contrast can be checked formally and geometrically.
Two-tier toolchain
ESLint plugin for fast static checks, Detox getAttributes() for actually rendered values at runtime.
Clear boundary
Content clarity of labels and logical navigation order remain a matter of human judgment.
Team practice
Automated CI checks and regular manual screen reader testing complement each other, they do not replace one another.