Les cases à cocher et les boutons radio
Une case à cocher est un choix indépendant, un bouton radio un choix exclusif dans un groupe — et le clavier ne les traverse pas de la même façon. Démo, code, critères RGAA.
6 critères RGAA en jeu
Essayez-les
Ce que la tabulation traverse : 0 arrêts pour six options
Traversez le formulaire à la tabulation : les trois cases à cocher coûtent trois arrêts, le groupe de boutons radio un seul. Une fois dedans, ce sont les flèches qui choisissent — et le relevé sous le formulaire, lu dans le DOM à chaque changement, montre que l’arrêt du groupe suit l’option cochée.
Cette démo existe aussi en page autonome.
L’erreur courante
La case dessinée à la main, parce que la case native se stylait mal.
<!-- À ne pas faire -->
<div class="case" onclick="basculer(this)">
<span class="coche" aria-hidden="true"></span>
Navigation au clavier
</div> Le div ne prend pas le focus, n’écoute pas Espace, et n’annonce ni
sa nature ni son état : au clavier, l’option est simplement impossible à cocher.
Ajouter role="checkbox" ne suffit pas — le rôle promet un comportement que
personne n’a écrit, et il faut alors reconstruire tabindex="0", aria-checked, l’écoute d’Espace et le focus visible. Quatre
réimplémentations pour retrouver ce qu’<input type="checkbox"> donnait.
Trois variantes coûtent aussi cher :
- Des options sans regroupement. La question « Ce que l’audit doit couvrir » n’est plus qu’un texte au-dessus : le lecteur d’écran annonce « Navigation au clavier, case à cocher, non cochée » sans dire de quoi elle est la réponse.
- Des boutons radio sans
namecommun. Chacun devient un groupe d’un seul : ils se cochent tous en même temps, aucun ne se décoche, et chacun devient un arrêt de tabulation de plus. - La case native masquée par
display: none. Elle sort de l’ordre de tabulation et de l’arbre d’accessibilité en même temps que de l’écran : le faux carré reste visible, le vrai contrôle n’existe plus pour personne.
Le code, pas à pas
1. Choisir la nature avant l’apparence
Les deux éléments ne diffèrent pas par le dessin mais par la logique :
<!-- Indépendantes : chacune se coche et se décoche pour elle-même -->
<input type="checkbox" id="couverture-clavier" name="couverture" value="clavier" />
<input type="checkbox" id="couverture-contrastes" name="couverture" value="contrastes" />
<!-- Exclusifs : le name partagé fait le groupe, et un seul peut être choisi -->
<input type="radio" id="niveau-a" name="niveau" value="a" />
<input type="radio" id="niveau-aa" name="niveau" value="aa" /> Pour les boutons radio, le name est le groupe : c’est lui qui rend le
choix exclusif, qui fait des flèches un moyen de navigation, et qui réduit le
groupe à un seul arrêt de tabulation. Deux name différents, et il ne reste
que deux cases à cocher rondes.
Un bouton radio, une fois coché, ne se décoche plus autrement qu’en choisissant une autre option. Si le choix doit pouvoir être annulé, prévoyez une option « Sans préférence » explicite — ou une case à cocher.
2. Une étiquette par option
<input type="checkbox" id="couverture-clavier" name="couverture" value="clavier" />
<label for="couverture-clavier">Navigation au clavier</label> Le for porte l’id du contrôle, comme pour tout champ. Ce qui change ici,
c’est la place : le test 11.4.3 demande l’étiquette à droite ou au-dessous de la case, là où les autres champs la veulent au-dessus ou à gauche. La cible
de clic s’en trouve élargie à tout le texte, ce qui compte pour une case de
treize pixels de côté.
La legend du groupe ne remplace aucune étiquette : elle s’ajoute devant
chacune. Un groupe légendé dont les cases n’ont pas d’étiquette propre — un <label for>, ou à défaut aria-label ou aria-labelledby — échoue au critère
11.1 comme n’importe quel champ nu : le test 11.1.1 n’accepte pas la legend au
titre d’étiquette.
3. Le groupe : fieldset et legend
<fieldset>
<legend>Ce que l'audit doit couvrir</legend>
<input type="checkbox" id="couverture-clavier" name="couverture" value="clavier" />
<label for="couverture-clavier">Navigation au clavier</label>
…
</fieldset> Le lecteur d’écran rattache alors la question aux options — JAWS répète la
légende à chaque contrôle du groupe (« Ce que l’audit doit couvrir, Navigation
au clavier, case à cocher, non cochée »), NVDA et VoiceOver l’annoncent à
l’entrée du groupe, puis ne redisent que l’option. Le RGAA
accepte trois formes pour ce regroupement — <fieldset>, une balise avec role="group", ou role="radiogroup" pour un groupe de boutons radio — et <fieldset> reste la seule qui n’a rien à réécrire.
Le critère 11.5 dit « si nécessaire », et le glossaire précise quand : les
champs doivent être regroupés lorsque les intitulés de label ne sont pas
suffisants pour informer l’utilisateur que les champs font partie d’un
regroupement. Une case isolée — « J’accepte les conditions d’utilisation » —
se suffit à elle-même et n’appelle pas de fieldset. Un groupe d’options qui
répondent à une question en appelle un, parce que la question est justement ce
que l’étiquette de chaque option ne dit pas.
4. Une légende qui pose la question
<!-- Présente, et sans emploi -->
<legend>Choix</legend>
<!-- La question à laquelle les options répondent -->
<legend>Ce que l'audit doit couvrir</legend> Le critère 11.6 se contente d’une légende ; le 11.7 demande qu’elle soit
pertinente. La distinction pèse plus ici que sur un groupe de champs de saisie :
l’étiquette d’une option ne dit que sa valeur — « Niveau AA », « Navigation au
clavier » —, et rien d’autre dans le groupe ne dit de quoi elle est la réponse.
« Choix », « Options », « Groupe 1 » laissent donc la question sans énoncé, et
le fieldset qui les porte ne rattache les options qu’à un mot vide.
La longueur se raisonne dans l’autre sens, parce que JAWS répète la légende devant chaque option : « Niveau visé » se supporte trois fois, « Merci d’indiquer ci-dessous le niveau visé par l’audit » beaucoup moins. La légende est la question, courte — l’invite et la consigne se placent ailleurs.
5. Le clavier, sans une ligne de JavaScript
Les éléments natifs apportent deux comportements différents, et c’est ce qui rend leur apprentissage utile :
- Chaque case à cocher est un arrêt de tabulation, et Espace la bascule.
- Un groupe de boutons radio est un seul arrêt — sur l’option cochée, ou sur la première quand aucune ne l’est. Les flèches déplacent le choix et le focus ensemble, la tabulation suivante sort du groupe.
C’est le même contrat que les onglets, à cette différence
près qu’il n’y a rien à écrire : ni tabindex roulant, ni écoute des flèches,
ni preventDefault().
6. Styler sans détruire
Deux techniques gardent le contrôle natif sous le dessin :
/* La plus simple : on stylise l'input lui-même. */
input[type='checkbox'] {
appearance: none;
width: 1.15rem;
height: 1.15rem;
border: 2px solid;
border-radius: 0.2rem;
} appearance: none retire l’apparence du système sans toucher au focus, au
clavier ni au rôle. Si le dessin voulu dépasse ce que l’input accepte, la
seconde technique le superpose : opacity: 0 et position: absolute sur
l’input, un <span aria-hidden="true"> dessiné par-dessus, et un style de focus
piloté par input:focus-visible + .marque. Ce que ces deux techniques ont en
commun : l’input reste dans la page, focalisable et restitué.
Ce qui les distingue de display: none et de visibility: hidden, qui retirent
l’un et l’autre le contrôle de l’ordre de tabulation. Le focus visible, lui,
n’est pas une option : le critère 10.7 l’exige, et un dessin personnalisé le
supprime souvent sans qu’on s’en aperçoive à la souris.
7. Une case, ou deux boutons radio ?
Pour une question binaire, les deux se défendent, et le choix se raisonne :
- Une case à cocher quand ne rien faire est une réponse, et la bonne : « Recevoir le récapitulatif par courriel ». L’état par défaut est décoché, et cela veut dire non.
- Deux boutons radio quand la réponse doit être donnée, et qu’aucune valeur par défaut n’est légitime : « Le site est-il soumis à l’obligation légale ? Oui / Non ». Rien n’est coché au départ, et l’absence de réponse se distingue d’un non.
Ce que la case ne peut pas faire : signifier « non » explicitement. Ce que les radios ne peuvent pas faire : rester sans réponse une fois qu’une réponse a été donnée.
Au clavier
| Touche | Effet |
|---|---|
| Tab | Passe à la case à cocher suivante — chacune est un arrêt. Sur un groupe de boutons radio, entre une seule fois, sur l’option cochée ou sur la première. |
| Tab depuis un radio | Sort du groupe entier et va au contrôle suivant, sans passer par les autres options. |
| ↑ ↓ ← → | Dans un groupe de boutons radio : change l’option choisie et emmène le focus avec elle. Sans effet sur une case à cocher. |
| Espace | Coche ou décoche la case qui a le focus ; choisit le bouton radio qui a le focus, utile quand aucun ne l’était. |
| Entrée | Soumet le formulaire. Ne coche rien — c’est Espace qui coche. |
Ce que le lecteur d’écran restitue
À l’entrée du groupe : la légende, puis le nom, la nature et l’état de l’option — de l’ordre de « Ce que l’audit doit couvrir, Navigation au clavier, case à cocher, cochée ». Sur un bouton radio, le rang s’ajoute : « Niveau visé, Niveau AA, bouton radio, coché, 2 sur 3 ». La formulation varie d’un lecteur à l’autre, et la légende n’est répétée sur chaque option que par JAWS ; les informations disponibles, elles, sont les mêmes.
Si vous n’entendez la question à aucun moment, pas même en entrant dans le
groupe, le fieldset ou sa legend manquent. Si vous entendez « Choix,
Niveau AA, bouton radio », la légende est là et ne sert à rien : le balisage
est conforme au 11.6, la question reste à poser. Si chaque bouton radio s’annonce « 1 sur 1 », le name n’est pas
partagé — et le choix n’est exclusif que dans le CSS. Si vous n’entendez ni
« case à cocher » ni l’état, c’est un div : personne ne peut le cocher au
clavier.
Checklist
- Un
<input type="checkbox">outype="radio"natif, jamais undivstylé. - Un
namecommun à tous les boutons radio d’un même groupe. - Une
<label for>par option, placée à droite ou au-dessous du contrôle. fieldsetetlegenddès que la question ne tient pas dans les étiquettes.- Une
legendqui pose la question du groupe, pas « Choix » ni « Options ». - Styler par
appearance: none, jamais masquer pardisplay: none. - Une case pour un choix indépendant, des boutons radio pour un choix exclusif.
Les critères RGAA en jeu
- Critère 11.1 — Étiquette de champ de formulaire
Chaque case porte sa propre étiquette, liée par for et id. La legend nomme la question, pas les options : elle ne dispense aucune case de son étiquette.
- Critère 11.4 — Étiquette accolée à son champ
Le test 11.4.3 inverse la règle des autres champs : pour une case à cocher ou un bouton radio, l'étiquette se place à droite ou au-dessous, jamais au-dessus.
- Critère 11.5 — Regroupement des champs de même nature
Un ensemble d'options qui répondent à une même question est un regroupement de champs de même nature : fieldset ou role="group", et role="radiogroup" réservé aux boutons radio — mais seulement lorsque les étiquettes seules ne suffisent pas à dire l'appartenance.
- Critère 11.6 — Légende des regroupements de champs
Sans légende, le groupe existe pour le balisage et pour personne : la question posée n'est plus lue avec les options, et le choix se fait à l'aveugle.
- Critère 11.7 — Pertinence de la légende
L'étiquette d'une option ne dit que sa valeur — « Niveau AA » —, et rien d'autre ne dit de quoi elle est la réponse. Une légende « Choix » satisfait le 11.6 et laisse le groupe anonyme ; une légende bavarde, elle, est répétée devant chaque option.
- Critère 7.3 — Contrôle des scripts au clavier
Le div stylé en case à cocher ne prend pas le focus et ne répond pas à Espace : la fonction n'est plus contrôlable au clavier, et rien dans la page ne la remplace.
Vérifiez votre composant avec l'extension
Pour aller plus loin
- Les champs de formulaire : l’étiquette, l’aide et l’erreur, pour les champs qui se saisissent.
- Critère 11.5 — Regroupement des champs, 11.6 — Légende des regroupements et 11.7 — Pertinence de la légende : ce qu’un audit vérifie sur un groupe d’options.
- « Champs de même nature » au glossaire : la définition qui décide si le regroupement est nécessaire.
- Les onglets : le même clavier de groupe, mais entièrement à écrire à la main.
Sources
- ARIA APG — Checkbox Pattern — le contrat de comportement, quand le natif ne suffit pas
- MDN —
<input type="radio">— lenamepartagé et ce qu’il implique - WCAG — Understanding 1.3.1 Info and Relationships — pourquoi le regroupement doit être dans le balisage
- W3C WAI — Grouping Controls —
fieldset,legendet leurs alternatives ARIA - GOV.UK Design System — Checkboxes — quand employer des cases plutôt que des radios