Skip to Content
AccessibilitéPartie 9 — Formulaires accessibles9.5 — Atelier : un formulaire de A à Z

Chapitre 9.5 — Atelier : un formulaire de A à Z

⏱️ TL;DR — On applique tout. On reprend le formulaire d’inscription d’A11yLearn dans sa version d’origine — inaccessible : placeholders en guise de labels, radios sans groupe, erreurs en rouge sans texte, e-mail à ressaisir — et on le reconstruit entièrement accessible : labels visibles et liés, <fieldset> + <legend>, erreurs identifiées en texte, liées et annoncées, autocomplete partout. Code complet et commenté, puis une checklist réutilisable sur n’importe quel formulaire.

🎯 Objectifs

  • Diagnostiquer un formulaire réel et lister ses défauts d’accessibilité.
  • Reconstruire un formulaire complet en appliquant les chapitres 9.1 à 9.4.
  • Disposer d’une checklist de contrôle réutilisable.

Le point de départ : la version AVANT

Voici le formulaire d’inscription d’A11yLearn tel qu’il existait. Il « marche » à la souris pour un utilisateur voyant sans trouble de la couleur. Pour tous les autres, il est cassé.

<!-- ❌ VERSION AVANT — cumul de fautes d'accessibilité --> <form> <h1>Inscription</h1> <!-- Placeholder en guise de label : nom accessible fragile, indice qui disparaît --> <input type="text" placeholder="Nom complet" /> <input type="text" placeholder="E-mail" /> <input type="text" placeholder="Confirmez l'e-mail" /> <!-- saisie redondante --> <input type="password" placeholder="Mot de passe" /> <!-- Radios sans fieldset/legend : la question « rôle ? » n'est jamais annoncée --> <p>Je m'inscris comme</p> <label><input type="radio" name="role" value="app" /> Apprenant</label> <label><input type="radio" name="role" value="form" /> Formateur</label> <!-- Case sans association fiable, astérisque coloré seul pour le requis --> <label><input type="checkbox" /> J'accepte les conditions <span style="color:red">*</span></label> <div>S'inscrire</div> <!-- un bouton fait en div : ni focusable, ni annoncé --> </form>

Les défauts, un par un :

  • Aucun label associé : quatre champs annoncés « zone de saisie, vide » (chap. 9.1).
  • Saisie redondante : un champ « Confirmez l’e-mail » inutile (chap. 9.4, critère 3.3.7).
  • Radios sans groupe : la question « rôle » n’est pas annoncée (chap. 9.2).
  • Requis en couleur seule : l’astérisque rouge ne transmet rien à qui ne le voit pas (chap. 9.2, critère 1.4.1).
  • Faux bouton : une <div> ni focalisable, ni annoncée « bouton », ni activable au clavier.
  • Aucun type, aucun autocomplete, aucune gestion d’erreur.

La version APRÈS

On reconstruit tout. Chaque correctif renvoie au chapitre qui l’explique.

<!-- ✅ VERSION APRÈS — accessible --> <form novalidate> <h1>Inscription</h1> <!-- Résumé d'erreurs : focalisable, annoncé, injecté au submit (chap. 9.3) --> <div id="resume-erreurs" role="alert" tabindex="-1" hidden></div> <!-- Labels visibles et associés + autocomplete + type (chap. 9.1 et 9.4) --> <p><label for="nom">Nom complet (obligatoire)</label></p> <input id="nom" name="nom" type="text" autocomplete="name" required /> <p><label for="email">Adresse e-mail (obligatoire)</label></p> <input id="email" name="email" type="email" autocomplete="email" aria-describedby="email-aide" required /> <p id="email-aide">On t'enverra un lien de confirmation à cette adresse.</p> <!-- Plus de champ « confirmez l'e-mail » : saisie redondante supprimée (chap. 9.4) --> <p><label for="pwd">Mot de passe (obligatoire)</label></p> <input id="pwd" name="pwd" type="password" autocomplete="new-password" aria-describedby="pwd-aide" required /> <p id="pwd-aide">12 caractères minimum, avec au moins un chiffre.</p> <!-- Groupe de radios structuré (chap. 9.2) --> <fieldset> <legend>Je m'inscris comme</legend> <label><input type="radio" name="role" value="app" required /> Apprenant</label> <label><input type="radio" name="role" value="form" /> Formateur</label> </fieldset> <!-- Requis signalé par le texte, pas par la couleur seule (chap. 9.2) --> <p> <label> <input type="checkbox" name="cgu" required /> J'accepte les conditions d'utilisation (obligatoire) </label> </p> <!-- Vrai bouton : focusable, annoncé « bouton », activable au clavier --> <button type="submit">S'inscrire</button> </form>

Et le champ e-mail en état d’erreur, après une validation au submit (chap. 9.3) :

<p><label for="email">Adresse e-mail (obligatoire)</label></p> <input id="email" name="email" type="email" autocomplete="email" aria-invalid="true" aria-describedby="email-aide email-erreur" required /> <p id="email-aide">On t'enverra un lien de confirmation à cette adresse.</p> <p id="email-erreur" class="erreur"> <span aria-hidden="true">⚠️ </span> Entre une adresse e-mail valide, par exemple prenom@exemple.fr. </p>

Note que aria-describedby liste deux id (email-aide email-erreur) : le lecteur d’écran annonce l’aide puis l’erreur. On garde l’aide et on ajoute l’erreur, sans écraser l’une par l’autre.

Côté logique de soumission, en JavaScript, on remplit le résumé d’erreurs et on lui donne le focus :

// Au submit : construire la liste, l'afficher, déplacer le focus (chap. 9.3) const resume = document.getElementById("resume-erreurs"); resume.hidden = false; resume.innerHTML = ` <h2>Il y a 1 erreur</h2> <ul><li><a href="#email">Adresse e-mail : format invalide</a></li></ul> `; resume.focus(); // tabindex="-1" le rend focalisable par script

Côté utilisateur — La version APRÈS, à l’oreille : « Inscription, titre niveau 1 », puis « Nom complet, obligatoire, zone de saisie », « Adresse e-mail, obligatoire, zone de saisie, on t’enverra un lien de confirmation », etc. Le groupe annonce « Je m’inscris comme, Apprenant, bouton radio, 1 sur 2 ». Le bouton est « S’inscrire, bouton ». En cas d’erreur, le focus saute sur « Il y a 1 erreur : Adresse e-mail, format invalide », d’où un lien mène droit au champ. Le même formulaire, entièrement navigable sans voir l’écran.

La checklist formulaire

Passe chaque formulaire à cette grille avant de le livrer.

ContrôleCritèreChapitre
Chaque champ a un <label> associé (for/id ou englobant)3.3.29.1
Le placeholder n’est jamais le seul label3.3.29.1
Les radios/cases liées sont dans <fieldset> + <legend>1.3.19.2
Les champs requis ont required + un repère textuel (pas la couleur seule)1.4.1 · 3.3.29.2
Les consignes/aides sont reliées via aria-describedby3.3.29.2
Les erreurs sont identifiées en texte3.3.19.3
Les erreurs suggèrent une correction3.3.39.3
Les champs fautifs ont aria-invalid + message lié4.1.29.3
Les erreurs sont annoncées (résumé focalisable ou live region)3.3.19.3
autocomplete sur les champs d’identité1.3.59.4
type/inputmode adaptés à chaque champ9.4
Pas de saisie redondante3.3.79.4
Authentification sans test cognitif (collage autorisé)3.3.89.4
Le bouton d’envoi est un vrai <button>, utilisable au clavier4.1.29.1

🧭 Sur A11yLearn — Cet atelier est le tournant d’A11yLearn côté formulaires. On est passé d’un formulaire d’inscription que 100 % des testeurs au lecteur d’écran abandonnaient à un formulaire franchissable de bout en bout : un utilisateur de NVDA a créé son compte sans aide, du premier « Nom complet, zone de saisie » jusqu’au « S’inscrire, bouton », en corrigeant une erreur d’e-mail grâce au résumé focalisé. La leçon transversale : aucune de ces corrections n’a demandé de framework ni de bibliothèque — juste du HTML correct et une poignée d’attributs ARIA bien placés. C’est le cœur du métier.

✏️ Exercices

Exercice 1 — Audit express. Reprends la version AVANT et écris, en une ligne chacun, les cinq défauts les plus graves avec leur correctif.

✅ Solution

(1) Placeholders au lieu de labels → ajouter un <label> visible et associé à chaque champ. (2) Radios sans groupe → envelopper dans <fieldset> + <legend>. (3) Faux bouton en <div> → un vrai <button type="submit">. (4) Aucune gestion d’erreur → messages en texte, liés (aria-describedby), champs aria-invalid, erreurs annoncées via un résumé focalisable. (5) Champ « confirmez l’e-mail » → le supprimer (saisie redondante), s’appuyer sur autocomplete="email".

Exercice 2 — Étends la checklist. A11yLearn ajoute un champ « Date de naissance » et un champ « Pays ». Quels attributs d’aide à la saisie mets-tu, et quel piège évites-tu sur le champ pays ?

✅ Solution

Date de naissance : un <label> associé et autocomplete="bday" (ou les sous-champs bday-day/bday-month/bday-year si tu éclates la date). Pays : un <select> avec <label> et autocomplete="country" (ou country-name). Le piège du champ pays : ne pas en faire un <div> custom non accessible avec une fausse liste — un <select> natif est focusable, annoncé et navigable au clavier gratuitement. Si un composant custom est imposé, il faudra reconstruire tout le comportement (voir la partie composants).

🧠 Quiz de révision

1. Pourquoi le champ « Confirmez l’e-mail » a-t-il été supprimé ?

Parce qu’il impose une saisie redondante (critère 3.3.7, WCAG 2.2) : l’utilisateur retape une information déjà donnée. Un autocomplete="email" et la relecture du champ rendent cette confirmation inutile et pénible, surtout en cas de handicap moteur ou cognitif.

2. Pourquoi aria-describedby peut-il lister deux identifiants ?

Pour associer plusieurs textes au même champ — ici l’aide (email-aide) et le message d’erreur (email-erreur). Le lecteur d’écran les annonce dans l’ordre, ce qui permet de garder l’aide tout en ajoutant l’erreur, sans que l’une écrase l’autre.

3. Qu’apporte le vrai <button type="submit"> par rapport à la <div> d’origine ?

Le rôle « bouton » annoncé, le focus clavier et l’activation par Entrée/Espace, et la soumission native du formulaire — le tout gratuitement. La <div> n’était ni focalisable, ni annoncée, ni activable au clavier : invisible pour un utilisateur de lecteur d’écran.

4. Comment le formulaire signale-t-il un champ requis sans se reposer sur la couleur ?

Par un repère textuel (« obligatoire ») dans le label et l’attribut required (annoncé « requis » par les technologies d’assistance). L’astérisque coloré seul est abandonné, car la couleur seule ne transmet pas l’information (critère 1.4.1).

5. Quelle est la leçon transversale de l’atelier ?

Qu’un formulaire pleinement accessible ne demande ni framework ni bibliothèque : du HTML correct (labels, fieldset, vrais boutons, type/autocomplete) plus quelques attributs ARIA bien placés (aria-describedby, aria-invalid, résumé d’erreurs focalisable). La maîtrise est dans les fondations, pas dans l’outillage.


Partie suivante : Tester l’accessibilité — maintenant que tu sais construire un formulaire accessible, apprends à le vérifier : outils automatiques, navigation clavier, lecteur d’écran.

Last updated on