Un formulaire de devis en cinq étapes (type de projet, surface, budget, coordonnées, confirmation) avait été signalé comme « pénible à remplir au clavier » par un testeur externe mandaté pour un audit RGAA. La description initiale restait vague : « on doit tout refaire à chaque étape ». Ce billet documente le diagnostic complet de ce défaut, sans revenir sur le traitement des messages d’erreur de validation du même formulaire, déjà couvert dans un article distinct.
Symptôme : retour systématique en haut de page à chaque étape
En reproduisant le parcours au clavier, le défaut est apparu dès la première transition : après avoir rempli l’étape 1 et activé le bouton « Suivant », le contenu de l’étape 2 s’affichait correctement à l’écran, mais le focus clavier, lui, restait positionné sur l’ancien bouton « Suivant », désormais absent du DOM puisque toute la zone du formulaire avait été remplacée. Le navigateur renvoyait alors le focus sur <body>, ce qui obligeait à retraverser l’en-tête et le menu de navigation avec Tab avant d’atteindre le premier champ de la nouvelle étape — à chacune des cinq étapes du parcours.
Diagnostic : suivre le focus dans les outils de développement
Le diagnostic a consisté à observer document.activeElement dans la console juste avant et juste après le clic sur « Suivant ». Avant le clic, l’élément actif était bien le bouton de l’étape en cours. Juste après le remplacement du contenu, document.activeElement affichait <body>, confirmant que le focus n’était jamais explicitement redirigé vers la nouvelle étape.

En examinant le code JavaScript du formulaire, la fonction de transition entre étapes se limitait à un remplacement de contenu et à un défilement automatique vers le haut du conteneur, sans aucune instruction dédiée à la gestion du focus :
function allerEtapeSuivante(nouvelleEtapeHTML) {
conteneurFormulaire.innerHTML = nouvelleEtapeHTML;
conteneurFormulaire.scrollIntoView({ behavior: 'smooth' });
// Rien ici ne s'occupe du focus clavier.
}
Correctif : déplacer le focus vers le titre de la nouvelle étape
La correction retenue s’inspire d’un motif courant dans les applications à page unique accessibles : chaque étape reçoit un titre <h2> avec un attribut tabindex="-1", qui le rend focalisable par script sans l’ajouter à l’ordre de tabulation naturel. Après chaque transition, le focus est explicitement déplacé vers ce titre :
function allerEtapeSuivante(nouvelleEtapeHTML) {
conteneurFormulaire.innerHTML = nouvelleEtapeHTML;
const titreEtape = conteneurFormulaire.querySelector('h2[data-titre-etape]');
if (titreEtape) {
titreEtape.setAttribute('tabindex', '-1');
titreEtape.focus();
}
conteneurFormulaire.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
Ce choix présente deux avantages : le focus ne repart jamais en haut de la page entière, seulement en haut du formulaire, et une personne utilisant un lecteur d’écran entend immédiatement le titre de la nouvelle étape annoncé (« Étape 2 sur 5 : surface du projet »), ce qui confirme sans ambiguïté le changement d’étape.
Prévention : un test systématique sur chaque formulaire multi-étapes
Pour éviter que ce défaut ne réapparaisse sur un futur formulaire multi-étapes du même type, deux mesures ont été adoptées :
- Un composant de formulaire multi-étapes réutilisable, intégrant nativement la gestion du focus décrite ci-dessus, plutôt que de laisser chaque nouveau formulaire réinventer sa propre transition.
- Un test manuel systématique ajouté à la checklist de recette : parcourir chaque étape au clavier uniquement, en vérifiant après chaque transition que le focus n’est ni sur
body, ni sur un élément disparu du DOM.
Le formulaire fonctionnait parfaitement à la souris, ce qui explique qu’il soit passé inaperçu pendant plusieurs mois. C’est un rappel utile : un défaut de focus clavier ne se voit jamais dans un test purement visuel, il faut littéralement lâcher la souris pour le rencontrer.
En résumé
Ce défaut, signalé de façon vague, avait une cause précise et récurrente : aucune gestion explicite du focus lors du remplacement du contenu à chaque étape du formulaire. La correction — déplacer le focus vers le titre de la nouvelle étape via un tabindex="-1" temporaire — est un motif réutilisable sur tout composant qui remplace une portion de page en JavaScript, bien au-delà de ce seul formulaire de devis.