Écrire du code accessible : De la théorie à l'expérience utilisateur
En Bref : L'essentiel à retenir
- La première règle ARIA : ne pas utiliser ARIA si le HTML natif suffit. Les pages avec ARIA ont 41% d'erreurs en plus.
- Le code accessible repose sur trois piliers : HTML sémantique, gestion du focus clavier, et états ARIA pour le contenu dynamique.
- Tester avec un lecteur d'écran est indispensable : les outils automatiques ne détectent que 30-50% des problèmes.
Un code techniquement conforme peut créer une expérience utilisateur désastreuse. À l'inverse, un code qui comprend les besoins réels des utilisateurs de technologies d'assistance offre une navigation fluide et intuitive.
Ce guide vous aide à passer de la conformité technique à l'expérience utilisateur réelle.
La première règle : HTML sémantique avant tout
Pourquoi le HTML natif prime
La « première règle d'ARIA » établie par le W3C est claire : n'utilisez pas ARIA si le HTML natif suffit.
Un <button> natif offre gratuitement :
- Rôle « button » pour les lecteurs d'écran
- Activation par Entrée et Espace
- Focus clavier automatique
- États disabled gérés
Un <div onclick> nécessite de tout réimplémenter manuellement, avec risque d'erreurs.
Les statistiques parlent
L'étude WebAIM sur un million de pages montre que les sites utilisant ARIA présentent 41% d'erreurs supplémentaires par rapport à ceux sans ARIA.
ARIA mal utilisé est pire qu'absent. Le HTML sémantique est votre fondation la plus solide.
Note
En 2025, l'accessibilité n'est plus optionnelle. Les standards WCAG 2.2 et l'EAA imposent une conformité qui passe d'abord par un HTML propre.
Structure sémantique d'une page
Les landmarks essentiels
Les landmarks permettent aux utilisateurs de lecteurs d'écran de naviguer rapidement entre les grandes sections :
<header>
<nav aria-label="Navigation principale">...</nav>
</header>
<main>
<article>
<h1>Titre principal</h1>
...
</article>
</main>
<aside aria-label="Articles connexes">...</aside>
<footer>...</footer>
Ces balises créent automatiquement des régions navigables. Pas besoin de role="banner" sur un <header> : c'est redondant.
Hiérarchie des titres
Les titres structurent le contenu et permettent la navigation par raccourci (touche H) :
<h1>Titre de la page (unique)</h1>
<h2>Section principale</h2>
<h3>Sous-section</h3>
<h3>Autre sous-section</h3>
<h2>Autre section principale</h2>
Erreurs fréquentes :
- Sauter des niveaux (h1 puis h3)
- Utiliser les titres pour le style plutôt que la structure
- Plusieurs h1 par page (acceptable en HTML5, déconseillé pour l'accessibilité)
Astuce
Vérifiez la structure de vos titres avec notre outil d'audit qui détecte les sauts de niveaux et les titres vides.
Éléments interactifs accessibles
Boutons
Utilisez <button> pour les actions, <a> pour la navigation :
<!-- Action (ouvre modal, soumet formulaire) -->
<button type="button" onclick="openModal()">
Ouvrir les paramètres
</button>
<!-- Navigation (change de page) -->
<a href="/parametres">
Aller aux paramètres
</a>
Pour un bouton avec icône seule :
<button type="button" aria-label="Fermer">
<svg aria-hidden="true">...</svg>
</button>
L'icône est masquée pour les lecteurs d'écran (aria-hidden), le label est fourni par aria-label.
Liens
Les liens doivent être explicites hors contexte :
<!-- Mauvais -->
<a href="/rapport.pdf">Cliquez ici</a>
<!-- Bon -->
<a href="/rapport.pdf">
Télécharger le rapport annuel 2026 (PDF, 2.3 Mo)
</a>
Pour les liens qui ouvrent un nouvel onglet :
<a href="https://externe.com" target="_blank" rel="noopener">
Site externe (s'ouvre dans un nouvel onglet)
</a>
Formulaires accessibles
Association label-champ
Chaque champ doit avoir un label associé programmatiquement :
<label for="email">Adresse email</label>
<input type="email" id="email" name="email" autocomplete="email">
Pour les champs avec instructions supplémentaires :
<label for="password">Mot de passe</label>
<input
type="password"
id="password"
aria-describedby="password-help"
>
<span id="password-help">
8 caractères minimum, 1 majuscule, 1 chiffre
</span>
Gestion des erreurs
Les erreurs doivent être liées aux champs et annoncées :
<label for="email">Adresse email</label>
<input
type="email"
id="email"
aria-invalid="true"
aria-describedby="email-error"
>
<span id="email-error" role="alert">
L'adresse email n'est pas valide
</span>
Le role="alert" déclenche une annonce immédiate par le lecteur d'écran.
Groupes de champs
Les boutons radio et checkboxes doivent être groupés :
<fieldset>
<legend>Mode de paiement</legend>
<input type="radio" id="cb" name="paiement" value="cb">
<label for="cb">Carte bancaire</label>
<input type="radio" id="paypal" name="paiement" value="paypal">
<label for="paypal">PayPal</label>
</fieldset>
Contenus dynamiques avec ARIA
États pour les composants interactifs
Pour un accordéon :
<button
aria-expanded="false"
aria-controls="section1-content"
>
Section 1
</button>
<div id="section1-content" hidden>
Contenu de la section...
</div>
Quand l'accordéon s'ouvre, JavaScript doit :
- Passer
aria-expandedàtrue - Retirer l'attribut
hidden
Régions dynamiques (live regions)
Pour les notifications qui apparaissent sans action utilisateur :
<div aria-live="polite" aria-atomic="true">
<!-- Le contenu injecté ici sera annoncé -->
</div>
aria-live="polite": Annonce après la fin de la phrase courantearia-live="assertive": Interrompt immédiatement (à utiliser avec parcimonie)
Modales accessibles
Une modale correctement implémentée :
<div
role="dialog"
aria-modal="true"
aria-labelledby="modal-title"
>
<h2 id="modal-title">Confirmer la suppression</h2>
<p>Cette action est irréversible.</p>
<button>Annuler</button>
<button>Supprimer</button>
</div>
Le JavaScript doit :
- Déplacer le focus dans la modale à l'ouverture
- Piéger le focus dans la modale (Tab ne sort pas)
- Fermer avec Échap
- Restaurer le focus à l'élément déclencheur à la fermeture
Navigation au clavier
Focus visible
Ne supprimez jamais l'outline sans alternative :
/* Mauvais */
:focus {
outline: none;
}
/* Bon */
:focus {
outline: 2px solid #005fcc;
outline-offset: 2px;
}
/* Encore mieux : focus-visible pour les utilisateurs souris */
:focus-visible {
outline: 2px solid #005fcc;
outline-offset: 2px;
}
Ordre de tabulation
L'ordre de focus doit suivre l'ordre visuel logique. Évitez tabindex positif :
<!-- Mauvais : ordre imprévisible -->
<input tabindex="3">
<input tabindex="1">
<input tabindex="2">
<!-- Bon : ordre naturel du DOM -->
<input>
<input>
<input>
Utilisez tabindex="0" pour rendre focusable un élément non-interactif, tabindex="-1" pour permettre le focus programmatique sans Tab.
Note
Consultez notre guide ARIA pour approfondir les attributs ARIA et leur usage correct.
Tests indispensables
Tests automatisés
Intégrez axe-core dans votre CI/CD :
import AxeBuilder from '@axe-core/playwright';
test('Page accessible', async ({ page }) => {
await page.goto('/');
const results = await new AxeBuilder({ page }).analyze();
expect(results.violations).toHaveLength(0);
});
Ces tests détectent 30-50% des problèmes.
Tests manuels obligatoires
Pour les 50-70% restants :
- Navigation clavier complète : Parcourez toute la page avec Tab
- Test lecteur d'écran : NVDA sur Windows, VoiceOver sur Mac
- Zoom 200% : Le contenu reste-t-il utilisable ?
- Désactivation CSS : La page reste-t-elle compréhensible ?
L'expérience utilisateur, objectif final
Le code accessible n'est pas une fin en soi. L'objectif est que des personnes réelles puissent utiliser votre site efficacement.
Un formulaire techniquement conforme mais avec des messages d'erreur incompréhensibles reste inutilisable. Un menu accessible au clavier mais avec un ordre de focus illogique reste frustrant.
Mettez-vous à la place de vos utilisateurs. Testez avec de vrais lecteurs d'écran. Écoutez les retours des personnes concernées.
Le code accessible de qualité ne se mesure pas en conformité WCAG mais en expérience utilisateur réussie.
Questions fréquentes
Sur quoi repose du code accessible ?
Sur trois piliers : un HTML sémantique qui donne aux éléments leur rôle natif, une gestion explicite du focus clavier à chaque changement d'état, et des attributs ARIA réservés à ce que le HTML ne sait pas exprimer, principalement les contenus qui changent sans rechargement.
Pourquoi préférer un bouton natif à un div cliquable ?
Parce que l'élément button apporte gratuitement quatre choses : le rôle annoncé aux technologies d'assistance, la prise de focus au clavier, l'activation par Entrée et par Espace, et la gestion de l'état désactivé. Un div doté d'un gestionnaire de clic n'en apporte aucune, il faut les réécrire une par une.
Comment vérifier qu'un composant est réellement accessible ?
En le parcourant au clavier sans souris, puis en l'écoutant avec un lecteur d'écran. Ces deux passages révèlent ce qu'aucune revue de code ne montre : un focus qui disparaît, un état annoncé à contretemps, un composant dont on ne peut plus sortir.
Guides RGAA associés
Pour aller plus loin sur les sujets abordés dans cet article, consultez nos fiches techniques :
Chaque champ de formulaire doit avoir une étiquette (label) qui lui est liée explicitement.
Chaque image porteuse d'information doit avoir une alternative textuelle pertinente via l'attribut alt. Les images décoratives doivent avoir un attribut alt vide.
L'ordre de tabulation clavier doit être cohérent avec l'ordre de lecture visuel (l'absence de piège au clavier relève du critère 12.9).
Vidéos RGAA sur ce thème
Pour voir ces sujets en action, regardez les épisodes vidéo correspondants :
Articles similaires
Votre site est-il conforme ?
Ne prenez pas de risques avec l'accessibilité. Lancez un audit complet de votre site en quelques minutes et obtenez un rapport détaillé des corrections à apporter.