Chapitre 9.2 — Groupes & structure
⏱️ TL;DR — Un label par champ suffit pour les champs isolés, mais pas pour les groupes : une série de boutons radio ou de cases à cocher a besoin d’une question commune. C’est le rôle de
<fieldset>+<legend>: le lecteur d’écran annonce la légende avec chaque option, donc l’utilisateur sait toujours à quelle question il répond. On voit aussi comment signaler les champs requis (texte +required, pas juste un astérisque coloré), donner des instructions reliées au champ viaaria-describedby, et ordonner logiquement le formulaire.
🎯 Objectifs
- Grouper les champs liés (radios, cases) avec
<fieldset>et<legend>. - Signaler les champs requis de façon perceptible par tous.
- Relier une instruction ou une aide à un champ avec
aria-describedby. - Ordonner et regrouper les champs de manière logique.
Le problème des groupes
Prends une question à choix unique : « Formule d’abonnement : Gratuite / Standard / Pro ». Chaque bouton radio a bien son propre label (« Gratuite », « Standard », « Pro »), mais rien ne relie ces trois options à la question. À la tabulation, un utilisateur de lecteur d’écran entend « Gratuite, bouton radio » sans savoir de quoi on parle. Il manque le contexte du groupe.
<fieldset> regroupe les champs liés, et <legend> porte la question commune. Le lecteur d’écran annonce alors la légende avec chaque option : « Formule d’abonnement, Gratuite, bouton radio, 1 sur 3 ».
<!-- ❌ Trois radios orphelines : la question « Formule ? » n'est jamais annoncée -->
<p>Formule d'abonnement</p>
<label><input type="radio" name="plan" value="free" /> Gratuite</label>
<label><input type="radio" name="plan" value="std" /> Standard</label>
<label><input type="radio" name="plan" value="pro" /> Pro</label>
<!-- ✅ fieldset + legend : la légende est annoncée avec chaque option -->
<fieldset>
<legend>Formule d'abonnement</legend>
<label><input type="radio" name="plan" value="free" /> Gratuite</label>
<label><input type="radio" name="plan" value="std" /> Standard</label>
<label><input type="radio" name="plan" value="pro" /> Pro</label>
</fieldset>La même règle vaut pour un groupe de cases à cocher (« Centres d’intérêt : Design / Code / Data ») et pour des champs liés comme un bloc d’adresse.
♿ Côté utilisateur — Sans
<legend>, une personne qui navigue au lecteur d’écran arrive sur « Standard, bouton radio, 2 sur 3 » et doit deviner la question — souvent en remontant chercher un titre au-dessus. Avec la légende, la question est répétée à chaque option : jamais de doute, jamais de retour en arrière. Le<fieldset>est aussi ce qui permet d’annoncer « 1 sur 3 », qui situe l’utilisateur dans le groupe.
Signaler les champs requis
Un astérisque rouge seul pose deux problèmes : la couleur seule ne transmet pas l’information (daltonisme, contraste), et un * isolé n’est pas toujours explicite ni annoncé clairement. Combine plusieurs signaux :
- L’attribut
required(ouaria-required="true"si tu ne peux pas utiliserrequired) : la technologie d’assistance annonce alors « requis ». - Un repère textuel visible : le mot « (obligatoire) », ou un astérisque accompagné d’une légende en début de formulaire expliquant ce qu’il signifie.
<!-- ✅ Requis signalé par le texte ET par l'attribut required -->
<label for="email">Adresse e-mail (obligatoire)</label>
<input type="email" id="email" name="email" required />Si tu tiens à l’astérisque, garde-le mais explicite-le : « Les champs marqués d’un astérisque (*) sont obligatoires », placé avant les champs concernés. Et n’oublie jamais l’attribut required, qui porte l’information jusqu’au lecteur d’écran.
📚 Aller plus loin — Ne pas se reposer sur la couleur seule relève du critère 1.4.1 Utilisation de la couleur (niveau A). L’exigence d’étiquettes et instructions relève du 3.3.2 (niveau A). Signaler « requis » via
required/aria-requiredsert le 4.1.2 Nom, rôle et valeur.
Instructions et aide : aria-describedby
Un champ a souvent besoin d’une consigne : format attendu, règle de mot de passe, exemple. Si tu la mets juste à côté visuellement, elle existe pour les voyants mais reste déconnectée du champ pour le lecteur d’écran. aria-describedby fait le lien : il pointe vers l’id du texte d’aide, qui est alors annoncé après le nom du champ.
<label for="pwd">Mot de passe</label>
<input
type="password"
id="pwd"
name="pwd"
aria-describedby="pwd-aide"
required
/>
<p id="pwd-aide">12 caractères minimum, avec au moins un chiffre.</p>Le lecteur d’écran annonce : « Mot de passe, zone de saisie, 12 caractères minimum, avec au moins un chiffre ». La consigne est liée au champ, plus seulement posée à côté. On réutilisera exactement ce mécanisme au chapitre suivant pour relier un message d’erreur à son champ.
💡 Réflexe — Dès qu’un champ porte une consigne visible (format, règle, exemple), relie-la avec
aria-describedby. La règle mentale : « si un voyant a besoin de cette info pour remplir le champ, un utilisateur de lecteur d’écran en a besoin au même moment » — donc au niveau du champ, pas perdue trois paragraphes plus haut.
Ordre logique et regroupement
La structure aide tout le monde, pas seulement les technologies d’assistance :
- Ordre de saisie logique et cohérent avec l’ordre du DOM (donc l’ordre de tabulation) : on ne fait pas sauter le focus de l’e-mail à la ville puis au prénom.
- Regroupe ce qui va ensemble : identité, coordonnées, préférences. Des titres de section (
<h2>,<h3>) et/ou des<fieldset>par bloc rendent le formulaire lisible et navigable par titres. - Un champ par ligne en général : plus clair au zoom et en navigation clavier que des champs serrés côte à côte.
🧭 Sur A11yLearn — Le formulaire d’inscription d’A11yLearn proposait de choisir un rôle (« Apprenant / Formateur / Admin ») via trois boutons radio, mais sans
<fieldset>. Un testeur au lecteur d’écran nous a dit tabuler sur « Formateur, bouton radio » sans jamais entendre le mot « rôle » — il croyait que c’était une case d’option isolée. On a enveloppé les trois radios dans un<fieldset>avec<legend>Je m'inscris comme</legend>. Désormais chaque option est annoncée avec sa question. Petit changement de balisage, gros changement d’expérience.
✏️ Exercices
Exercice 1 — Structure ce groupe. Voici trois cases à cocher pour des centres d’intérêt, sans regroupement. Rends l’ensemble accessible.
<p>Centres d'intérêt</p>
<label><input type="checkbox" name="tags" value="design" /> Design</label>
<label><input type="checkbox" name="tags" value="code" /> Code</label>
<label><input type="checkbox" name="tags" value="data" /> Data</label>✅ Solution
Enveloppe les cases dans un <fieldset> et transforme le titre en <legend> :
<fieldset>
<legend>Centres d'intérêt</legend>
<label><input type="checkbox" name="tags" value="design" /> Design</label>
<label><input type="checkbox" name="tags" value="code" /> Code</label>
<label><input type="checkbox" name="tags" value="data" /> Data</label>
</fieldset>Maintenant, chaque case est annoncée avec la question « Centres d’intérêt », et l’utilisateur sait toujours à quoi il répond.
Exercice 2 — Relie l’aide au champ. Ce champ affiche une consigne à côté, mais elle n’est pas reliée. Corrige-le.
<label for="tel">Téléphone</label>
<input type="tel" id="tel" name="tel" />
<span>Format attendu : 06 12 34 56 78</span>✅ Solution
Donne un id à la consigne et pointe dessus avec aria-describedby :
<label for="tel">Téléphone</label>
<input type="tel" id="tel" name="tel" aria-describedby="tel-aide" />
<span id="tel-aide">Format attendu : 06 12 34 56 78</span>Le lecteur d’écran annonce alors la consigne juste après le nom du champ, au moment où l’utilisateur en a besoin.
🧠 Quiz de révision
1. À quoi servent <fieldset> et <legend> ?
<fieldset> et <legend> ?À grouper des champs liés (boutons radio, cases à cocher, bloc d’adresse) et à leur donner une question commune. Le lecteur d’écran annonce la <legend> avec chaque option, de sorte que l’utilisateur sait toujours à quelle question il répond.
2. Pourquoi un astérisque rouge seul ne suffit-il pas à marquer un champ requis ?
Parce qu’il repose sur la couleur seule (problème de daltonisme et de contraste, critère 1.4.1) et n’est pas toujours annoncé clairement. Il faut combiner un repère textuel (« obligatoire ») et l’attribut required/aria-required, et expliquer l’astérisque s’il est utilisé.
3. Comment relier une consigne d’aide à son champ pour le lecteur d’écran ?
Avec aria-describedby sur le champ, pointant vers l’id du texte d’aide. Ce texte est alors annoncé après le nom du champ, au bon moment, au lieu de rester déconnecté visuellement à côté.
4. Que gagne l’utilisateur avec l’annonce « 2 sur 3 » dans un groupe de radios ?
Il se situe dans le groupe : il sait combien d’options existent et où il se trouve. Cette annonce de position vient précisément du regroupement par <fieldset>, qui structure les options en un ensemble cohérent.
5. Pourquoi l’ordre du DOM compte-t-il pour un formulaire ?
Parce que l’ordre de tabulation suit l’ordre du DOM. Un ordre de saisie logique et cohérent (identité, puis coordonnées, puis préférences) évite au focus de sauter de façon déroutante et rend le formulaire prévisible pour la navigation clavier.
Chapitre suivant : Erreurs & validation — le point le plus difficile : identifier, lier, suggérer et annoncer les erreurs.