Guides React

Accessibilité avec React : guide complet pour des composants a11y conformes au RGAA

React fait beaucoup de choses bien. L'accessibilité par défaut n'en fait pas partie.

Soyons honnêtes : React est partout. Et son approche composant par composant, c'est une vraie mine d'or pour l'accessibilité — en théorie. On encapsule les bonnes pratiques une fois dans un composant, on le réutilise partout, et tout le monde est content. Sauf que dans la pratique, j'ai bossé sur suffisamment de projets React pour savoir que ça ne se passe presque jamais comme ça.

Le truc c'est que le JSX ressemble à du HTML, mais ce n'est pas du HTML. Le nommage en camelCase, les fragments, les portails... tout ça crée des pièges spécifiques pour l'accessibilité. Et le nombre de fois où j'ai vu des devs mettre un onClick sur un <div> en pensant que c'était OK... (Spoiler alert : ça ne l'est pas.)

Les bases : ARIA en JSX

Attributs ARIA dans React

Bonne nouvelle quand même : React supporte nativement tous les attributs ARIA dans le JSX. Et contrairement aux attributs HTML classiques (qui passent en camelCase dans React), les attributs ARIA gardent leur format avec tirets. Petit détail qui facilite la vie :

<button aria-expanded={isOpen} aria-controls="menu-list">Menu</button>

Les attributs ARIA que vous allez utiliser tout le temps en React :

  • aria-label : label invisible pour les lecteurs d'écran (votre meilleur ami)
  • aria-labelledby : référence à un élément contenant le label
  • aria-describedby : référence à une description complémentaire
  • aria-expanded : état ouvert/fermé d'un élément dépliable
  • aria-hidden : masquer un élément des technologies d'assistance
  • aria-live : annoncer les changements dynamiques (on y revient plus bas, c'est crucial)
  • role : définir le rôle sémantique d'un élément

Éléments HTML sémantiques — la règle numéro 1

Avant même de parler d'ARIA, parlons franchement : la première règle d'accessibilité en React, c'est d'utiliser les éléments HTML natifs. Point. Pas de <div> déguisé avec des rôles ARIA quand un élément natif fait le job :

  • <button> au lieu de <div onClick> (le nombre de fois où j'ai vu ça en code review...)
  • <a href> au lieu de <span onClick>
  • <nav>, <main>, <aside>, <header>, <footer> pour les landmarks
  • <input type="checkbox"> au lieu d'un toggle DIV personnalisé avec 50 lignes de JS

React Aria : la bibliothèque qui change tout

Entre nous, si vous ne connaissez pas encore React Aria (par Adobe), arrêtez tout et allez voir. C'est la bibliothèque la plus complète pour créer des composants accessibles dans React. Elle fournit des hooks qui encapsulent toute la logique d'accessibilité pour vous :

  • useButton : gestion clavier/souris unifiée pour les boutons
  • useTextField : champ texte avec label, description et erreur associés
  • useComboBox : autocomplétion accessible (essayez d'en coder une from scratch, vous comprendrez la valeur de ce hook)
  • useDialog : modale avec focus trap et gestion Escape
  • useTable : tableau interactif avec navigation clavier
  • useDatePicker : sélecteur de date accessible
  • useMenu : menu déroulant conforme au pattern ARIA

Le gros avantage ? React Aria est headless (sans style). Vous gardez un contrôle total sur le design tout en bénéficiant d'une accessibilité robuste. Plus d'excuse pour choisir entre beau et accessible.

Hooks personnalisés pour l'accessibilité

useAnnounce : annonces pour lecteurs d'écran

Un hook que je mets dans quasiment tous mes projets React maintenant. L'idée : annoncer les changements dynamiques via une live region. Simple et redoutablement efficace :

const useAnnounce = () => { const [message, setMessage] = useState(''); return { announce: setMessage, AnnouncerRegion: () => <div aria-live="polite" className="sr-only">{message}</div> }; };

useFocusTrap : piège à focus pour les modales

Le focus trap maintient le focus à l'intérieur d'une modale ou d'un menu. React Aria le fournit nativement, mais vous pouvez aussi utiliser focus-trap-react.

useReducedMotion : respect des préférences utilisateur

Détectez la préférence de réduction de mouvement :

const useReducedMotion = () => { const [reduced, setReduced] = useState(false); useEffect(() => { const mq = window.matchMedia('(prefers-reduced-motion: reduce)'); setReduced(mq.matches); }, []); return reduced; };

Gestion du focus dans React

La gestion du focus est cruciale dans les SPA React :

  • Navigation entre pages : lors d'un changement de route (React Router), déplacez le focus vers le <h1> de la nouvelle page ou vers le <main>
  • Modales : le focus doit aller dans la modale à l'ouverture et revenir à l'élément déclencheur à la fermeture
  • Chargement dynamique : après un chargement AJAX, annoncez le résultat et déplacez le focus si nécessaire
  • useRef + focus() : utilisez useRef pour cibler des éléments et .focus() pour y déplacer le focus programmatiquement

Tests d'accessibilité en React

jest-axe pour les tests unitaires

Intégrez les tests d'accessibilité dans vos tests Jest avec jest-axe :

import { axe, toHaveNoViolations } from 'jest-axe'; expect.extend(toHaveNoViolations); test('composant accessible', async () => { const { container } = render(<MonComposant />); const results = await axe(container); expect(results).toHaveNoViolations(); });

Testing Library

@testing-library/react encourage naturellement les tests accessibles en ciblant les éléments par leur rôle, label ou texte visible plutôt que par des classes CSS ou des data-testid :

  • getByRole('button', { name: 'Envoyer' })
  • getByLabelText('Adresse email')
  • getByText('Résultat de la recherche')

eslint-plugin-jsx-a11y

Ce plugin ESLint détecte les erreurs d'accessibilité au moment du développement : images sans alt, éléments non interactifs avec onClick, autocomplete manquant, etc.

Checklist React a11y pour le RGAA

  1. Utiliser les éléments HTML sémantiques natifs
  2. Ajouter des attributs ARIA quand le HTML natif ne suffit pas
  3. Gérer le focus lors des navigations et interactions
  4. Annoncer les changements dynamiques via aria-live
  5. Tester avec jest-axe et Testing Library
  6. Utiliser eslint-plugin-jsx-a11y
  7. Tester manuellement avec NVDA/VoiceOver
  8. Vérifier la navigation clavier complète
React fournit les outils pour créer des interfaces accessibles, mais c'est au développeur de les utiliser correctement. L'accessibilité doit être intégrée dès le premier composant, pas ajoutée en fin de projet.
Non, React n'est pas accessible par défaut. Il fournit les outils (support ARIA natif, JSX sémantique) mais c'est au développeur d'écrire du code accessible. L'utilisation de bibliothèques comme React Aria et d'outils comme eslint-plugin-jsx-a11y aide à systématiser les bonnes pratiques.
Utilisez un useEffect dans votre composant de layout pour déplacer le focus vers le h1 ou le main de la nouvelle page après chaque navigation. Avec React Router, écoutez les changements de location. Des bibliothèques comme @reach/router gèrent cela automatiquement.
React Aria (Adobe) est la référence en matière d'accessibilité. Radix UI et Headless UI offrent aussi une excellente accessibilité. Pour les bibliothèques stylées, Chakra UI et MUI (Material UI) ont fait des efforts significatifs. Évitez les bibliothèques sans documentation d'accessibilité.
Combinez trois niveaux : 1) eslint-plugin-jsx-a11y pour la détection au développement, 2) jest-axe + Testing Library pour les tests automatisés (getByRole, getByLabelText), 3) tests manuels avec NVDA/VoiceOver et navigation clavier. Les tests auto détectent environ 30-40 % des erreurs.

Testez la conformité de votre site

Scannez votre site et obtenez un rapport détaillé avec recommandations IA.

Scanner mon site - 15€