RGAA Lab

Composants accessibles

Les limites de temps

Une limite de temps doit pouvoir être supprimée ou augmentée. Pour une session, prévenir ne suffit pas : le référentiel demande un moyen d'agir. Démo, code, critères RGAA.

4 critères RGAA en jeu

Essayez-les

Session non démarrée

La suppression de la limite est l'une des issues que le critère accepte.

Tapez quelque chose, puis laissez la session expirer : ce texte doit vous revenir intact.

Ce que la région d'annonce a dit : 0 annonce

Rien pour l'instant. Le décompte, lui, change à chaque seconde.

Lancez la session, tapez une phrase dans le champ, puis ne touchez plus à rien. Le décompte change quarante fois ; la région d’annonce, elle, ne parle que deux fois — le journal en bas le compte. Laissez expirer : le texte que vous avez tapé vous revient mot pour mot. Cochez « Ne pas limiter ma session » et le décompte s’arrête : c’est l’une des issues que le référentiel accepte.

Cette démo existe aussi en page autonome.

L’erreur courante

Une session qui tombe en silence, au milieu d’un formulaire long.

// À ne pas faire : rien n'est annoncé, rien ne peut être prolongé, rien n'est gardé
setTimeout(() => {
	window.location = '/deconnexion?raison=inactivite';
}, 15 * 60 * 1000);

Quinze minutes suffisent à quelqu’un qui remplit un formulaire à la souris. Elles ne suffisent pas à qui saisit au contacteur, relit chaque champ au lecteur d’écran, ou consulte un document à côté avant de répondre. Le compteur d’inactivité côté serveur ne voit d’ailleurs pas cette personne travailler : elle lit, elle tape hors ligne, elle réfléchit — aucune requête ne part.

Ce que ça coûte, dans l’ordre où l’utilisateur le découvre : la page change sous lui sans qu’il l’ait demandé, ce qui pour qui ne voit que quelques lignes à la fois se manifeste par un lecteur d’écran qui se met soudain à lire autre chose. Puis il constate que quarante minutes de saisie ont disparu. Puis il recommence, en sachant que ça peut recommencer.

Trois variantes, aussi répandues :

  • « Vous allez être redirigé dans 5 secondes » sans bouton pour arrêter. L’avertissement existe mais ne mène à rien : aucune des conditions du critère n’est remplie, et cinq secondes ne laissent le temps de rien lire.
  • Le décompte dans une région aria-live="assertive". Il est annoncé à chaque seconde, coupe la phrase en cours à chaque seconde, et rend le formulaire inutilisable au lecteur d’écran pendant tout le décompte.
  • La modale d’avertissement qui prend le focus pendant qu’on tape. La frappe se poursuit dans le vide, ou dans le bouton « Prolonger », qui finit par s’activer sur un appui d’Espace destiné au texte.

Le code, pas à pas

1. Vérifier de quelle limite on parle

Le critère 13.1 ne demande pas la même chose selon le procédé, et c’est le point qu’on rate le plus souvent.

Pour un rafraîchissement ou une redirection par script, quatre issues sont acceptées, et une seule suffit : pouvoir arrêter ou relancer le procédé, pouvoir multiplier la limite par dix au moins, être averti de son imminence et disposer de vingt secondes au moins pour l’augmenter, ou une limite d’au moins vingt heures.

Pour une limite de session, il n’y en a plus que trois : pouvoir supprimer la limite, pouvoir l’augmenter, ou une limite d’au moins vingt heures. L’avertissement n’y figure pas. Un bandeau « votre session expire dans deux minutes » sans moyen d’agir ne satisfait rien, quel que soit son préavis.

Une redirection par <meta>, elle, doit être immédiate — donc content="0". Toute autre valeur est en défaut par construction.

2. Un moyen de prolonger, disponible avant l’avertissement

<p class="session">
	Session : 12 minutes restantes.
	<button type="button" id="prolonger">Prolonger la session</button>
</p>

Le bouton est là dès le début, dans le flux, atteignable à la tabulation. C’est lui qui rend la limite conforme ; l’avertissement n’est que le moment où l’on rappelle qu’il existe.

Le placer uniquement dans la fenêtre d’avertissement inverse la logique : la conformité dépend alors d’avoir vu passer un message dans les deux dernières minutes. Quelqu’un revenu à son écran après l’expiration n’a jamais eu la possibilité d’agir.

3. Prévenir sans couper la lecture

<!-- La région existe dès le premier rendu, vide -->
<p id="avertissement" role="status"></p>
// Seulement aux paliers : le décompte visible change chaque seconde,
// l'annonce non.
if (restant === 120 || restant === 30) {
	avertissement.textContent =
		`Il reste ${restant} secondes de session. Vous pouvez la prolonger.`;
}

Deux règles se cachent là. La région doit préexister : un conteneur créé et inséré avec son rôle d’un seul coup n’est souvent pas annoncé. Et role="status" plutôt que role="alert" : l’alerte interrompt la lecture en cours, ce qui pour un avertissement à deux minutes d’échéance est disproportionné.

Le décompte visible, lui, reste du texte ordinaire, hors de toute région d’annonce. Il n’est pas masqué par aria-hidden pour autant : on peut vouloir aller le lire.

4. Ne rien perdre

// Le brouillon part avant la limite, pas au moment où elle tombe.
formulaire.addEventListener('input', () => {
	sessionStorage.setItem('brouillon', new FormData(formulaire).get('message'));
});

Sauvegarder l’état saisi et le restaurer après la reconnexion n’est pas une exigence du critère 13.1 — c’est la bonne pratique qui rend la limite supportable, et le seul recours quand la limite est imposée par la sécurité. La WCAG en fait un critère à part (2.2.6 Timeouts, niveau AAA), qui demande au minimum d’avertir de la durée d’inactivité au-delà de laquelle les données sont perdues.

Attention à ce qu’on stocke : sessionStorage n’est pas un endroit pour un numéro de carte ou un mot de passe.

5. Le rafraîchissement automatique

<!-- À ne pas faire : ni immédiat, ni supérieur à vingt heures -->
<meta http-equiv="refresh" content="900" />

Un tableau de bord qui se recharge tout seul remet le focus au début de la page et fait perdre la position de lecture. Rafraîchir la seule zone qui change, par un appel réseau, coûte moins cher et ne déplace rien — et un bouton « Actualiser » laisse l’utilisateur choisir le moment.

Pour une redirection permanente, la réponse est côté serveur : un 301 ou un 302 n’a aucune temporisation à contrôler, donc aucune limite de temps.

6. Les cas où la limite est essentielle

Le référentiel prévoit une exception « lorsque la limite de temps est essentielle, notamment lorsqu’elle ne pourrait pas être supprimée sans changer fondamentalement le contenu ou les fonctionnalités liées au contenu ». Une enchère qui se clôt à l’heure dite, un examen chronométré : la limite est la fonctionnalité. Le critère est alors non applicable.

L’exception est étroite, et elle se justifie dans le rapport d’audit. « Notre politique de sécurité impose quinze minutes » ne la déclenche pas : la limite reste une limite de temps, et il faut pouvoir la prolonger.

Au clavier

Il n’y a pas de motif d’interaction propre à ce composant : les touches sont celles d’un bouton et d’une case à cocher ordinaires. Ce qui se vérifie ici, c’est plutôt que rien ne se produit sans qu’on l’ait demandé.

ToucheEffet
TabAtteint le bouton « Prolonger la session », qui est dans le flux — pas seulement dans un avertissement passager.
Entrée / EspaceProlonge la session. Le focus reste sur le bouton : rien ne se déplace.
Aucune touchePendant le décompte, le focus ne bouge pas de lui-même. Si vous tapez dans un champ, vous continuez d’y taper.

Si l’avertissement prend la forme d’une fenêtre modale, tout le contrat de la modale s’applique en plus — focus conduit, Échap qui ferme, retour au point de départ. C’est un coût réel : un bandeau role="status" évite d’avoir à le payer.

Ce que le lecteur d’écran restitue

À l’échéance du palier, sans que le focus bouge : « Il reste 120 secondes de session. Vous pouvez la prolonger. » Puis, en tabulant, « Prolonger la session, bouton ». La formulation varie d’un lecteur à l’autre ; les deux informations, non — combien de temps, et quoi faire.

Le diagnostic inverse est simple. Si vous n’entendez rien jusqu’à ce que la page de connexion s’annonce, il n’y a ni région d’annonce ni avertissement. Si vous entendez le décompte à chaque seconde, il est dans une région d’annonce, et sans doute en assertive. Si l’annonce arrive une seule fois puis plus rien alors que le texte change, la région a été créée en même temps que son contenu. Et si le focus quitte le champ que vous étiez en train de remplir, c’est une modale qui s’est ouverte toute seule.

Checklist

  • Identifier chaque limite : session, rafraîchissement, redirection différée.
  • Pour une session : pouvoir supprimer ou augmenter la limite — avertir ne suffit pas.
  • Le moyen de prolonger présent dans le flux, avant l’avertissement.
  • L’avertissement dans une région role="status" préexistante, pas un décompte annoncé chaque seconde.
  • Aucun déplacement de focus non demandé pendant le décompte.
  • Les données saisies conservées, et l’utilisateur prévenu de ce qui se perd.
  • Une redirection par <meta> immédiate, ou pas de <meta> du tout.

Les critères RGAA en jeu

  • Critère 13.1 — Contrôle des limites de temps

    Session qui expire, page qui se recharge, redirection différée : ce sont trois limites de temps, et pour une session le référentiel n'accepte que trois issues — supprimer la limite, l'augmenter, ou la porter à vingt heures. Avertir n'en fait pas partie.

  • Critère 7.4 — Changement de contexte annoncé

    Une déconnexion automatique déplace l'utilisateur vers une autre page sans qu'il l'ait demandé : c'est un changement de contexte, et il doit être annoncé par un texte avant son déclenchement.

  • Critère 7.5 — Messages de statut

    L'avertissement s'affiche sans déplacer le focus : hors d'une région d'annonce préexistante, personne ne l'entend — et un décompte placé dans une région assertive se fait lire à chaque seconde.

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

    Le moyen de prolonger doit s'atteindre et s'activer au clavier, et l'avertissement ne doit pas retirer le focus du champ en cours de saisie.

Vérifiez votre composant avec l'extension

Pour aller plus loin

Sources