RGAA Lab

Composants accessibles

Le champ de mot de passe

Un champ dont on ne peut ni relire ce qu'on a tapé, ni coller ce qu'un gestionnaire a généré. Trois attributs et un bouton suffisent à lever les deux obstacles. Démo, code, critères RGAA.

6 critères RGAA en jeu

Essayez-le

12 caractères au minimum. Aucune autre contrainte : une phrase que vous retenez vaut mieux qu'une suite de symboles que vous noterez sur un carnet.

Ce que portent les éléments

Type du champ
Finalité déclarée
Nom accessible du bouton
Caractères dans le champ
0

Collez une longue phrase depuis votre gestionnaire de mots de passe : le compteur la reçoit entière. Rien n'intercepte le collage, rien ne tronque.

Tabulez jusqu’au bouton et pressez Entrée : son intitulé passe de « Afficher » à « Masquer », et le relevé du bas montre le type du champ qui suit. Puis collez une phrase longue depuis votre gestionnaire de mots de passe — le compteur la reçoit entière, rien n’est intercepté ni tronqué.

Cette démo existe aussi en page autonome.

L’erreur courante

Deux fautes voyagent presque toujours ensemble : l’œil sans nom, et le collage interdit.

<!-- À ne pas faire -->
<input type="password" name="mdp" onpaste="return false" maxlength="16" />
<i class="icone-oeil" onclick="basculer()"></i>

Le <i> ne prend pas le focus, ne répond ni à Entrée ni à

Espace, n'annonce ni sa nature de bouton ni son état. Un lecteur d'écran ne dit rien du tout, ou lit le caractère de zone privée de la police d'icônes — une syllabe sans rapport. Et le manque coûte plus cher ici que partout ailleurs : dans un champ masqué, personne ne peut relire sa saisie, ni à l'œil ni au lecteur d'écran, qui annonce « étoile » à la place des caractères. La bascule est le seul moyen de vérifier ce qu'on a tapé, et elle est hors d'atteinte au clavier.

onpaste="return false" casse les gestionnaires de mots de passe. Celui qui copie-colle un secret de trente signes le fait parce qu’il ne peut pas le taper — ou pas sans erreur. L’interdiction ne laisse qu’une issue : un mot de passe court, réutilisé, retenu de tête. Le RGAA 4.1, aligné sur WCAG 2.1, ne porte aucun critère là-dessus ; WCAG 2.2 en a ajouté un depuis (3.3.8). Ce n’est donc pas une non-conformité chez nous, et c’est quand même un obstacle réel.

maxlength="16", enfin, tronque en silence : le gestionnaire colle vingt-quatre signes, le champ en garde seize, le compte est créé avec un mot de passe que personne ne possède plus.

Trois variantes, aussi répandues :

  • autocomplete="off" posé sur tout le formulaire « pour la sécurité » : il prive le champ de sa finalité, et c’est exactement ce que le critère 11.13 relève.
  • Les règles de composition affichées seulement dans le message d’erreur : on saisit, on valide, on apprend, on recommence.
  • Une bascule qui change l’icône mais garde le même nom accessible : l’état est visible et reste inaudible.

Le code, pas à pas

1. Le champ, son étiquette, sa finalité

<label for="mdp">Mot de passe</label>
<input id="mdp" type="password" autocomplete="current-password" />

type="password" masque la saisie et fait annoncer un champ protégé. La <label for> est la même exigence que partout — mais elle pèse plus ici, puisque le contenu du champ ne peut pas servir de rappel.

Le choix entre les deux valeurs d’autocomplete n’est pas cosmétique :

  • current-password sur un formulaire de connexion — le gestionnaire propose le mot de passe enregistré pour ce compte ;
  • new-password à la création et au changement — le gestionnaire propose d’en générer un, et se garde bien d’y remettre l’ancien.

Les deux figurent dans la liste normalisée des valeurs d’autocomplete que le référentiel reprend de WCAG 2.1 : l’attribut n’accepte pas un mot libre.

2. Les règles, avant la saisie

<label for="mdp">Mot de passe</label>
<p id="mdp-regles">12 caractères au minimum.</p>

<input id="mdp" type="password" autocomplete="new-password" aria-describedby="mdp-regles" />

Le test 11.10.5 accepte deux conditions au choix, l’une comme l’autre préalables à la validation : une instruction visible qui identifie nommément le champ concerné, ou une instruction visible située dans l’étiquette ou dans le passage de texte associé au champ.

aria-describedby satisfait la seconde et fait restituer la règle en même temps que l’étiquette, à l’arrivée dans le champ. Le test ne l’impose pas — il impose la visibilité avant la validation.

Le message d’erreur, ensuite, reprend la règle mot pour mot plutôt que d’en donner une reformulation : lire « 12 caractères au minimum » au même endroit qu’avant évite de se demander si la contrainte a changé.

Une remarque qui n’est pas du RGAA mais qui allège la liste : le NIST, dans sa publication SP 800-63B, ne recommande plus les règles de composition — une majuscule, un chiffre, un symbole. Moins de règles, c’est moins à annoncer, moins à vérifier, et moins d’erreurs à rattraper.

3. La bascule afficher/masquer

<button type="button" id="bascule-mdp" aria-controls="mdp">
	Afficher le mot de passe
</button>
bascule.addEventListener('click', () => {
	const masque = champ.type === 'password';
	champ.type = masque ? 'text' : 'password';
	bascule.textContent = masque ? 'Masquer le mot de passe' : 'Afficher le mot de passe';
	statut.textContent = masque ? 'Votre mot de passe est visible.' : 'Votre mot de passe est masqué.';
});

Un <button type="button"> : sans le type, il soumettrait le formulaire — et la moitié des bascules cassées le sont pour cette seule raison.

L’état se dit de deux façons, et il faut en choisir une.

  • Le nom change : « Afficher le mot de passe » devient « Masquer le mot de passe ». C’est la solution la plus simple, et la plus sûrement restituée.
  • aria-pressed : le nom reste « Afficher le mot de passe », et l’état bascule. Le lecteur d’écran annonce alors « bouton bascule, activé ».

Les cumuler donne « Masquer le mot de passe, bouton bascule, activé », qui dit deux fois la même chose en se contredisant à moitié.

Le message de statut n’est pas un luxe : quand l’intitulé d’un bouton change alors qu’il a le focus, le changement n’est pas réannoncé par tous les lecteurs d’écran. Une région role="status", présente dès le premier rendu et vide, le redit sans voler le focus.

Si la place manque et que le bouton n’affiche que « Afficher », le nom accessible doit contenir l’intitulé visible — c’est le test 11.9.2. Un complément masqué suffit, et c’est ce que fait la démo :

<button type="button">Afficher<span class="sr-only"> le mot de passe</span></button>

4. Ne rien intercepter

<input id="mdp" type="password" autocomplete="current-password" />

Il n’y a rien à écrire : le bon comportement est celui du navigateur. Ce qui se retire, en revanche :

  • tout écouteur de paste, copy ou contextmenu sur le champ ;
  • maxlength, sauf à une valeur qu’aucun gestionnaire n’atteindra ;
  • un pattern qui refuse l’espace ou les accents — il exclut les phrases de passe, précisément ce qu’on veut encourager ;
  • autocomplete="off" sur le champ comme sur le formulaire.

Un masquage maison — un type="text" dont on remplace les caractères par des points en JavaScript — cumule tous les défauts : le gestionnaire ne reconnaît plus le champ, et le lecteur d’écran lit la vraie valeur à voix haute.

5. Un seul champ, plutôt que deux

« Confirmez votre mot de passe » existe parce qu’on ne pouvait pas relire ce qu’on venait de taper. La bascule répond mieux au même besoin, et pour la moitié du travail : un champ de moins à remplir, à annoncer, à valider — et un champ de moins où interdire le collage par distraction.

Au clavier

ToucheEffet
TabVa au champ, puis au bouton afficher/masquer, placé juste après lui dans le code.
Ctrl+V / Cmd+VColle le mot de passe. Rien ne doit l’intercepter.
Entrée / EspaceSur le bouton : bascule l’affichage. Le focus reste sur le bouton, la valeur reste dans le champ.
EntréeDans le champ : soumet le formulaire.

Ce que le lecteur d’écran restitue

À l’arrivée dans le champ, le nom, la nature protégée et la règle liée — de l’ordre de « Mot de passe, saisie de texte sécurisée, 12 caractères au minimum ». La formulation varie d’un lecteur d’écran à l’autre, les informations non.

Pendant la frappe, en revanche, rien n’est épelé : un champ masqué fait dire « étoile », ou ne fait rien dire. C’est la raison d’être de la bascule, et ce qui rend son inaccessibilité si coûteuse.

Sur le bouton : « Afficher le mot de passe, bouton ». Après activation, le statut suit — « Votre mot de passe est visible » — et le bouton se nomme désormais « Masquer le mot de passe ».

Si vous n’entendez que « bouton », l’icône n’a pas de nom accessible. Si le nom ne change pas et qu’aucun « activé » ne suit, l’état n’est pas exposé. Et si vous n’entendez la règle des douze caractères qu’après avoir validé, c’est qu’elle n’est reliée à rien.

Checklist

  • type="password" et une <label for> visible — jamais un placeholder seul.
  • autocomplete="current-password" à la connexion, new-password à la création.
  • Les règles de saisie visibles avant la validation — aria-describedby les rattache au champ.
  • La bascule en <button type="button">, jamais une icône cliquable.
  • L’état porté par un nom qui change ou par aria-pressed — pas les deux.
  • Une région role="status" pour redire le changement, présente dès le départ.
  • Rien qui intercepte le collage, aucun maxlength qui tronque.

Les critères RGAA en jeu

  • Critère 11.13 — Remplissage automatique des champs

    C'est le seul champ qu'on ne saisit presque jamais de tête. Sans autocomplete current-password ou new-password, le gestionnaire ne reconnaît plus le champ, et l'utilisateur retombe sur ce qu'il retient.

  • Critère 11.10 — Contrôle de saisie

    Les contraintes du mot de passe — longueur, caractères acceptés — doivent être visibles avant la validation : dans l'étiquette, dans le texte associé au champ, ou dans une instruction qui nomme le champ. Les découvrir dans le message d'erreur, c'est les découvrir trop tard.

  • Critère 11.9 — Intitulé des boutons

    La bascule afficher/masquer est un bouton du formulaire : une icône en forme d'œil sans intitulé ne dit ni ce qu'elle fait, ni sur quoi elle agit.

  • Critère 7.1 — Compatibilité des scripts

    La bascule est scriptée, donc son changement d'état doit être exposé — par un nom qui change ou par aria-pressed. Un œil qui se barre ne parle qu'à l'œil.

  • Critère 7.3 — Contrôle des scripts au clavier

    Une icône qui écoute le clic sans être un bouton ne prend pas le focus : relire ce qu'on vient de taper devient impossible sans souris, dans le seul champ où le navigateur ne le montre pas.

  • Critère 11.1 — Étiquette de champ de formulaire

    Le champ est masqué et sa valeur illisible : quand l'étiquette n'est qu'un placeholder, elle disparaît à la première frappe et plus rien, pas même la saisie, ne rappelle de quel champ il s'agit.

Vérifiez votre composant avec l'extension

Pour aller plus loin

Sources