RGAA Lab

Composants accessibles

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 l'audit doit couvrir
Niveau visé

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 name commun. 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

ToucheEffet
TabPasse à 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 radioSort 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.
EspaceCoche ou décoche la case qui a le focus ; choisit le bouton radio qui a le focus, utile quand aucun ne l’était.
EntréeSoumet 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"> ou type="radio" natif, jamais un div stylé.
  • Un name commun à tous les boutons radio d’un même groupe.
  • Une <label for> par option, placée à droite ou au-dessous du contrôle.
  • fieldset et legend dès que la question ne tient pas dans les étiquettes.
  • Une legend qui pose la question du groupe, pas « Choix » ni « Options ».
  • Styler par appearance: none, jamais masquer par display: none.
  • Une case pour un choix indépendant, des boutons radio pour un choix exclusif.

Les critères RGAA en jeu

Vérifiez votre composant avec l'extension

Pour aller plus loin

Sources