Les boîtes de dialogue 1/2

Thématiques associées :

Préambule

Les boîtes de dialogue font partie des composants que l’on retrouve régulièrement sur un site web. Malheureusement il n’existe pas de composant natif HTML permettant de créer ce type d’objet. Elles doivent donc être créées de toute pièce lors du développement, phase qui introduit souvent des problèmes d’accessibilité si on n’y prête pas attention.

Les boîtes de dialogue présentent souvent de cruelles difficultés, jusqu’à une complète inaccessibilité pour les utilisateurs qui naviguent à l’aide du clavier (touche Tab), ou celles qui naviguent à l’aide d’un lecteur d’écran (personnes malvoyantes ou non-voyantes). Cet exemple liste pas à pas les différentes étapes nécessaires pour créer une boîte de dialogue accessible.

Rappel ergonomie : il faut éviter l’emploi des boîtes de dialogue, fenêtres modales et autres popins, en particulier pour afficher de l’information, car leur utilisabilité et lisibilité sont problématiques pour tous les utilisateurs. Ne réserver leur emploi qu’à une petite quantité d’information avec un appel à action (validation d’une action, confirmation d’effacement de données…).

Si vous ne souhaitez pas créer ce composant de toute pièce, vous pouvez également utiliser le composant « modal » de la librairie Boosted qui présente le double avantage d’être accessible et d’être déjà aux couleurs de la charte Orange.

Exemple final

Voici le rendu final de la boîte de dialogue dont nous allons expliquer la création pas à pas dans cet exemple.

Étape 1 : définition du squelette

Nous créons ici le squelette de notre boîte de dialogue à l’aide d’une balise <div>. Nous lui ajoutons :

  • Un bouton « x » qui permettra de fermer la boîte de dialogue.
  • Un titre.
  • Un message.
  • Deux boutons permettant à l’utilisateur d’interagir.

Avertissement

Je suis une boîte de dialogue modale. Je suis accessible à la souris, au clavier et au lecteur d’écran.


<div class="dialog">
  <div class="dialogContainer">
    <button>X</button>
    <h1>Avertissement</h1>
    <p>Je suis une boîte de dialogue modale. Je suis accessible à la souris, au clavier et au lecteur d’écran.</p>
    <button>J’ai compris</button>
    <button>Annuler</button>
  </div>
</div>

Place aux tests

  • Navigation au clavier : déplacer le focus sur le bouton « Ouvrir l’exemple étape 1 » à l’aide de la touche Tab et valider avec la touche Entrée. La boîte de dialogue s’affiche, par contre on ne sait plus où est le focus. Si on appuie à nouveau sur la touche Tab pour tenter de fermer la boîte de dialogue, on se rend compte que le focus se déplace dans la page en arrière-plan et non dans la boîte de dialogue. Enfin la touche Échap n’a aucun effet.
  • Navigation au lecteur d’écran : se positionner sur le bouton « Ouvrir l’exemple 1 » à l’aide du lecteur d’écran puis valider avec la touche Entrée. La boîte de dialogue s’affiche mais au lecteur d’écran on n’entend rien. C’est comme si le bouton n’avait aucun effet, on peut continuer à naviguer normalement.

Étape 2 : gérer la navigation au clavier

Pour que l’on puisse naviguer correctement à l’aide du clavier :

  • Le focus doit être positionné dans la boîte de dialogue lors de son ouverture et repositionné lors de sa fermeture.
  • Le focus ne doit pas pouvoir sortir de la boîte de dialogue lors de la navigation à l’aide de la touche Tab.
  • La touche Échap permet de fermer la boîte de dialogue.

Dans ce nouvel exemple, on mémorise l’emplacement du focus à l’ouverture :


  // Récupère l'élément boîte de dialogue
  const dialog =  document.getElementById('step2-dialog');

  // Sauvegarde le focus pour le repositionner à la fermeture
  dialog.setAttribute('data-focusBack', 'step2-openButton');

  // Place le focus sur l’élément par défaut
  dialog.querySelector('.defaultFocus').focus();

On repositionne le focus lors de la fermeture :


  // Repositionne le focus à la fermeture de la dialogue
  var focusBackElement = dialog.getAttribute('data-focusBack');
  document.getElementById(focusBackElement).focus();

On empêche le focus de sortir de la boîte de dialogue.

Pour cela nous nous appuyons sur la méthode querySelectorAll. Elle nous permettra de récupérer facilement les éléments interactifs de la boîte de dialogue, pour ensuite identifier le premier élément et le dernier élément de la boîte.

Enfin à l'aide du gestionnaire d'évènement onKeyDown, nous pourrons intercepter le focus lorsqu'il arrivera sur le premier ou le dernier item, pour le repositionner en début ou en fin de modale, selon notre besoin.

La solution détaillée est présentée dans cet article : How to trap focus inside modal to make it ADA compliant


  // Récupère le premier élément interactif
  const dialogStart = finalDialog.querySelectorAll('button')[0];
  
  // Récupère le dernier élément interactif
  const dialogFocusableContent = dialog.querySelectorAll('button');
  const dialogStop = dialogFocusableContent[finalFocusableContent.length - 1];

  // Ecoute le parcours clavier, et repositionne le focus si nécessaire en fonction de l'élément actif
  dialog.onkeydown = function(e){
	
	if (e.key === 'Tab' || e.keyCode === 9) {
		
		if ( e.shiftKey ) {
		  if (document.activeElement === dialogStart) {
			e.preventDefault();
			dialogStop.focus();
		  }
		} else /* tab */ {
		  if (document.activeElement === dialogStop) {
			e.preventDefault();
			dialogStart.focus();
		  }
		}
	}
  }

Avertissement

Je suis une boîte de dialogue modale. Je suis accessible à la souris, au clavier et au lecteur d’écran.

On écoute également l’événement onKeyDown pour fermer la dialogue lorsque la touche Échap est pressée.

Voici le code HTML à jour de la boîte de dialogue :


<div id="step2-dialog" class="dialog">
  <div class="dialogContainer">
    <button>X</button>
    <h1>Avertissement</h1>
    <p>Je suis une boîte de dialogue modale. Je suis accessible à la souris, au clavier et au lecteur d’écran.</p>
    <button class="defaultFocus">J’ai compris</button>
    <button>Annuler</button>
  </div>
</div>

Place aux tests

  • Navigation au clavier : il n’y a plus de problème, le focus est correctement déplacé à l’ouverture et à la fermeture ; de plus le focus ne peut plus sortir de la boîte de dialogue lors de la navigation à l’aide de la touche Tab. Enfin la touche Échap permet la fermeture de la boîte de dialogue.
  • Navigation au lecteur d’écran : à l’ouverture de la boîte de dialogue on entend « J’ai compris bouton ». Le fait de déplacer le focus sur le bouton par défaut permet de faire vocaliser automatiquement celui-ci par le lecteur d’écran. Par contre l’utilisateur n’a pas connaissance ni du titre, ni du message affiché dans la boîte de dialogue.

Étape 3 : gérer la navigation au lecteur d’écran

Pour que l’on puisse naviguer correctement à l’aide d’un lecteur d’écran :

  • Celui-ci doit indiquer à l’utilisateur qu’une boîte de dialogue vient de s’ouvrir (ajout d’un rôle ARIA dialog ).
  • Le titre de la boîte de dialogue ainsi que le message affiché doivent être vocalisés à l’utilisateur (ajout des attributs aria-labelledby et aria-describedby).
  • Le bouton par défaut doit également être vocalisé, ceci a déjà été réalisé à l’étape précédente (le fait de déplacer le focus sur le bouton à l’ouverture de la boîte permet de faire vocaliser celui-ci par le lecteur d’écran).

L’attribut aria-labelledby permet d’indiquer l’id de l’élément contenant le titre de la boîte de dialogue. Celui-ci sera vocalisé automatiquement par le lecteur d’écran. L’attribut aria-describedby permet d’indiquer l’id de l’élément contenant la description. Cette description sera vocalisée automatiquement après le titre de la boîte de dialogue. Enfin l’attribut aria-label sur le bouton de fermeture « X » permet de spécifier un libellé à vocaliser (sinon par défaut celui-ci sera vocalisé : « x »).


<div id="final-dialog" role="dialog" aria-labelledby="final-dialogTitle" aria-describedby="final-description">
  <div class="dialogContainer">
    <button aria-label="fermer" title="Fermer">X</button>
    <h1 id="final-dialogTitle">Avertissement</h1>
    <p id="final-description">Je suis une boîte de dialogue modale. Je suis accessible à la souris, au clavier et au lecteur d’écran.</p>
    <button class="defaultFocus">J’ai compris</button>
    <button>Annuler</button>
  </div>
</div>

Place aux tests

  • Navigation au lecteur d’écran : le lecteur d’écran indique correctement qu’une boîte de dialogue vient de s’ouvrir. Il annonce le titre ainsi que la description. Enfin il indique que le focus est positionné sur le bouton « J’ai compris ».

Note sur la navigation à l’aide du lecteur d’écran

Dans une page web, la navigation au lecteur d’écran s’effectue principalement à l’aide des touches Flèche haut et Flèche bas. Ce mode de navigation est appelé « mode document ». On peut également utiliser la touche Tab mais dans ce cas seuls les éléments focusables seront vocalisés.

Dans les applications natives, la navigation s’effectue principalement à l’aide de la touche Tab. Ce mode de navigation est appelé « mode application ». L’utilisation du rôle ARIA dialog, passe le lecteur d’écran dans le « mode application », c’est à dire que la navigation avec les flèches n’est plus disponible lorsqu’une boîte de dialogue est affichée. Dans ce cas il faut utiliser la touche Tab pour naviguer entre les différents boutons de la dialogue.

À noter qu’il est également possible de forcer l’utilisation du mode « application » ou « document » à l’aide de l’attribut role. Cependant les utilisateurs n’étant pas habitués à ces changements de modes de navigation il est pour le moment déconseillé de les utiliser, sauf cas exceptionnels :


<div role="application">…</div>
<div role="document">…</div>