← Volver al blog

March 25, 2026·10 min read

Accessibility labels that help instead of annoy

Icon-only buttons are invisible to screen readers unless you label them. Good labels describe action (“Save draft”) not control type (“Button”). Hints are for non-obvious outcomes—use sparingly. Accessibility is part of the component API, not a QA pass at the end.

Every interactive element

If you can tap it, it needs a name. Decorative images should be hidden from accessibility trees.

Don’t duplicate the visible text in the label unless it adds meaning.

Focus order

Modals should trap focus; toasts should announce politely. Test with real screen readers, not just props that “look right.”

Dynamic content updates may need `accessibilityLiveRegion`.

Custom sliders and gestures

Complex widgets might need roles and value callbacks. Follow platform patterns users already know.

Regression-test a11y when you redesign navigation.

Pair labels with design system reviews

When a new icon button ships, require `accessibilityLabel` in the same PR as the visual. For charts and maps, provide a text summary or data table path—not everyone can parse color gradients.

Rotate platform testing: VoiceOver on iOS quarterly, TalkBack on Android quarterly; they do not behave identically.

Building an accessibility layer into your component API

Accessibility is not a prop you sprinkle at the end; it is part of the contract between your design system and product teams. Start by defining defaults: every pressable gets a label strategy—visible text, `accessibilityLabel`, or `accessibilityLabelledBy` pointing to a heading. Icon-only buttons must never ship without labels; if design insists on minimalism, negotiate `accessibilityHint` for the non-obvious outcomes. Document these rules in Storybook or your internal gallery with screen-reader-only examples. When you wrap third-party components, forward accessibility props explicitly; do not swallow them in spread props accidentally. For dynamic content—live scores, timers—decide between `accessibilityLiveRegion` polite versus assertive; assertive announcements interrupt users and should be rare. Test tab order on Android TalkBack and iOS VoiceOver separately; focus order is not identical. Complex components like carousels need custom roving tabindex patterns or roles that match platform expectations—copy patterns from native apps users already know. Regression-test accessibility on every navigation redesign: moving a tab bar breaks muscle memory for blind users more than sighted users because audio UX changes. Pair engineering verification with occasional sessions with assistive-tech users; synthetic testing misses nuance. Finally, track accessibility bugs with the same SLA as crashes—exclusion is a defect, not a polish item.

Hints, traits, and when silence is better

`accessibilityHint` should explain the consequence of an action—‘Sends the message’—not restate the control name. Over-hinting feels patronizing and slows navigation. Traits communicate role: button, header, link. Combine traits carefully; conflicting traits confuse assistive tech. For toggles, expose selected state and label changes when switching. Sliders need current value and range. Avoid animating accessibility frames without updating values—screen readers may read stale numbers during rapid gestures. Custom drawers and bottom sheets should move focus into the sheet on open and restore focus on close; failure here strands users in an invisible layer. For web views inside RN, bridge accessible summaries when possible; otherwise mark decorative web content hidden and provide a native alternative for critical tasks. When localizing, hints may need reordering for grammar—do not concatenate strings blindly. Review each release with Dynamic Type or font scaling at largest sizes: clipped hints indicate layout debt. Remember that accessibility services vary by OS version; test on n-1 OS releases when your audience lags upgrades.

Focus management in stacks, tabs, and overlays

Modal flows should trap focus until dismissed—users should not land ‘behind’ a modal via swipe gestures unexpectedly. For nested navigators, ensure back gestures return focus to predictable anchors. When replacing screens, move focus to the primary heading or first field intentionally—random focus feels like the app is broken. Toast notifications should use live regions sparingly; repeated toasts spam users. Loading overlays need `accessibilityState.busy` where applicable and should not steal focus from partially filled forms unless blocking is necessary. Screen transitions should not drop focus to the root on every push; maintain context. For split views on tablets, verify focus moves sensibly between panes. Game-like gestures may need alternative accessible paths—offer list-based equivalents for critical flows. Document focus behaviors in QA scripts so regressions are caught before release. When debugging focus loops, log focus changes in dev builds only—never noisy logs in production. Coordinate with animation libraries: some reanimated transitions confuse focus order if opacity hits zero before unmount—delay unmount until animations complete when needed.

Operationalizing accessibility in shipping processes

Add accessibility to your definition of done: checklist items for labels, contrast, scaling, and screen reader passes. Train designers to deliver annotated specs with accessibility notes—role, label source, hint rules. In code review, block PRs that introduce unlabeled interactives. Track accessibility defects in analytics separately to see trends. Support teams should have scripts for common questions about large text or VoiceOver issues—reduce friction for disabled users seeking help. For regulated industries, document conformance targets (WCAG-inspired) even if mobile mapping is imperfect—intent matters to auditors. Budget quarterly audits with external experts if you can—internal teams go blind to patterns. Celebrate improvements publicly; it encourages ownership. Accessibility work compounds: early investment prevents expensive retrofitting when you expand internationally or face legal scrutiny. Treat it as core product quality, because for many users it is the whole experience.

Shipping and reliability habits (1)

Internationalization is a product feature, not a string swap. Plural rules, RTL layout, and locale-aware formatting change behavior—not just copy length. Pseudolocale helps find clipping early, but real Arabic and German QA catches nuance. Avoid concatenating translated fragments; context matters. Document glossary terms so translators do not invent inconsistent product names.

Monorepos amplify both leverage and failure modes: duplicate React versions cause mysterious hook errors, and Metro misconfiguration blocks local packages from resolving. Invest in workspace discipline—single React version, documented `watchFolders`, and lint rules preventing packages from importing app navigators accidentally. CI must mirror local installs; ‘works on my laptop’ with different package managers is a time bomb.

Platform differences worth rehearsing (2)

WebViews are untrusted browsers inside your app. Validate `postMessage` payloads, lock navigation to expected hosts, and prefer system-browser auth flows when OAuth security demands it. Third-party JavaScript can change without your deploy—treat XSS in web as bridge compromise risk. Clear storage on logout and rate-limit message handlers.

E2E tests should protect revenue paths, not every permutation. Stable selectors (`testID`) beat text that marketing rewrites weekly. Flake management is a feature: quarantine, fix root causes, and keep smoke suites green on CI devices. Five reliable tests beat fifty flaky ones that everyone ignores.

Security, privacy, and data handling (3)

In-app purchases require server validation, restore flows, and support tooling that respects privacy. Sandbox quirks are normal—budget QA time. Subscriptions interact with family sharing, regional pricing, and refunds; engineering must stay aligned with finance and legal narratives users see in receipts.

Privacy nutrition labels and Play Data Safety forms should reflect actual SDK behavior—inventory dependencies each release and remove dead code. Drift between claims and telemetry is legal and store risk, not just embarrassment. Involve legal early when adding analytics or ads.

Performance and measurement discipline (4)

Design tokens and semantic colors make dark mode and rebrands feasible. Mixing three styling systems doubles migration cost—pick a primary approach and draw boundaries. Runtime CSS-in-JS can cost frame time on hot screens—profile before adopting wholesale.

ScrollView versus FlatList is a data-volume question. Small static content belongs in ScrollView; long feeds belong in virtualized lists. Nested scrollables need explicit height contracts—redesign beats fighting physics. Document intentional choices so future refactors do not ‘optimize’ blindly.

Team process and long-term maintenance (5)

Security and privacy expectations move faster than roadmaps. Treat analytics, crash, and attribution SDKs as part of your threat model: initialize them deliberately, document data flows, and verify ‘off’ truly stops network calls. Client-side secrets are public secrets—anything shipped in an APK or IPA should be assumed extractable. Pair mobile changes with backend policies so authorization remains consistent across platforms.

Accessibility is compatibility. Labels, focus order, and dynamic type are not polish—they determine whether users can complete tasks at all. Test with VoiceOver and TalkBack on hardware; simulators miss focus bugs. When designs prioritize minimalism, negotiate text alternatives for icon-only controls. Accessibility regressions often follow navigation redesigns—add checklist items to those PRs specifically.

Shipping and reliability habits (6)

Error boundaries catch render failures, not native crashes or async mistakes. Pair them with platform crash reporting and structured client logs. Fallback UI should include build identifiers and humane copy—never raw stack traces for end users. Test fallbacks with screen readers; a broken error screen is still broken UX.

Storage is not a database. AsyncStorage and MMKV excel at key-value preferences; SQLite or remote APIs belong elsewhere for relational data. Migrations should be incremental, logged, and non-blocking for UI. Secure tokens need secure storage when your model demands it—speed is not a substitute for correctness on auth material.

Platform differences worth rehearsing (7)

Splash screens and launch gates should reflect honest readiness: fonts, theme, session, critical remote config—not every SDK under the sun. Infinite splash is worse than a slightly longer branded hold. Match native and JS background colors to avoid flashes; respect reduced motion for animations.

JWT and session refresh flows need single-flight refresh, clear logout semantics, and secure storage for refresh tokens when appropriate. Parallel 401s should not stampede refresh endpoints. Clock skew and biometrics policies belong in explicit product decisions, not accidental implementation details.

Security, privacy, and data handling (8)

Testing onboarding changes with funnel metrics beats debating opinions. Segment by acquisition channel and platform; back behavior differs. Skip paths must be genuine—dark patterns may win short metrics and destroy brand trust. Localization length tests prevent clipped CTAs in verbose languages.

Background execution policies change with OS updates—revalidate after major iOS and Android releases. Misused background modes invite rejection. Persist user work frequently; the OS can kill you anytime after backgrounding. Uploads and timers should tolerate pause and resume without corrupting state.

Performance and measurement discipline (9)

Shipping React Native features is less about any single API and more about the system around it: typed boundaries, predictable navigation, and telemetry that tells you what broke in production. Prefer boring, explicit modules over clever metaprogramming that the next hire cannot grep. When platform vendors change behavior in point releases, your defense is automated smoke tests on real devices and a short internal changelog of native assumptions you rely on.

Performance work should start with measurement, not instinct. Watch JS thread versus UI thread separately; they bottleneck differently. Lists, images, and animations dominate most regressions—optimize those before micro-optimizing pure functions. Hermes, JSC, and bridge internals evolve; re-profile after every major upgrade instead of trusting last year’s numbers. Battery and thermal throttling on mid devices reveal issues flagship phones hide.

Team process and long-term maintenance (10)

Push notifications walk a line between helpful and intrusive. Prime users with context, respect notification channels on Android, and measure opt-outs after campaigns—spikes mean copy or frequency problems. Payload design affects background behavior; test killed and locked-device states. Tokens belong server-side with rotation strategies; never treat the client as authoritative for subscription state.

Images and maps look simple in mocks and expensive in production. Decode sizes should match display sizes; static map thumbnails are not interchangeable with interactive MapView gestures. Quotas, API keys, and offline behavior need explicit fallbacks—addresses as text, retry buttons, and calm error copy. Monitor vendor dashboards for spikes that indicate bugs or abuse.

Shipping and reliability habits (11)

Hermes versus JSC is not a lifestyle choice—profile your app. Hermes usually wins on startup; some libraries still assume JSC quirks. Engine toggles are not substitutes for fixing quadratic renders in your own code. Upgrade notes matter: Intl support and debugging tooling evolve.

Reanimated and gesture libraries earn their place when profiling proves UI-thread work and your team can maintain native upgrades. Worklets have constraints—read errors carefully. Respect reduced motion and test Android timing differences—identical JS does not guarantee identical feel.

Platform differences worth rehearsing (12)

ScrollView versus FlatList is a data-volume question. Small static content belongs in ScrollView; long feeds belong in virtualized lists. Nested scrollables need explicit height contracts—redesign beats fighting physics. Document intentional choices so future refactors do not ‘optimize’ blindly.

Metro cache issues masquerade as logic bugs. Establish a documented reset ladder: dev server restart, cache flags, Watchman, derived data, then dependency reinstalls. Compare platforms when only one breaks—native steps diverge. Keep CI caches deterministic with lockfiles and pinned toolchains.

Patrocinado

Promoción breve