Dialogs 1/2

Associated themes :
  • Component

Introduction

Dialogs are part of the core UI components on a website. Unfortunately, there is no native HTML component to create this kind of object. These dialogs must be created from scratch during development. It often adds accessibility issues if we do not pay attention.

Difficulties associated with dialogs are most of the time impossible to overcome, specifically for those navigating with the keyboard (Tab), or using a screen reader (visually impaired or blind). This example will show you step-by-step how to create an accessible dialog.

Usability reminder: avoid using dialogs, windows and other modal popups, especially for displaying information, their usability and readability are issues for all users. Only use them with few information and particularly to confirm actions (validating an action, confirming erasing data…).

If you do not want to create the component from scratch, you can also use the “modal” component of the Boosted library which has the double advantage of being accessible and being already branded with the Orange colours.

Final example

Here is the final rendering of the dialog, we will show you how to create it step-by-step.

Step 1: defining the layout

Here we create the design of our dialog with a <div> tag. We add:

  • A “x” button that will close the dialog.
  • A title.
  • A message.
  • Two action buttons.

Warning

I am a modal dialog. I am accessible to mouse, keyboard and screen reader users.


<div class="dialog">
  <div class="dialogContainer">
    <button>X</button>
    <h1>Warning</h1>
    <p>I am a modal dialog. I am accessible to mouse, keyboard and screen reader users.</p>
    <button>OK</button>
    <button>Cancel</button>
  </div>
</div>

Testing

  • Keyboard navigation: move the focus on the “Open the first step example” button using the Tab key and press the Enter key. The dialog should appear, although the focus is lost. If you press again the Tab key to close the dialog, you should notice that the focus moves behind the dialog and not inside the dialog. Finally, the Escape key has no effect.
  • Screen reader navigation: move to the “Open the first step example” button with the screen reader and press the Enter key. The dialog should appear, but the screen reader does not detect the new dialog. As if the button had no effect, you can continue to browse normally.

Step 2: handling keyboard navigation

To navigate properly with the keyboard:

  • The focus must be put into the dialog when opening and re-set when closing.
  • The focus should not get out of the dialog when browsing using the Tab key.
  • The Escape button closes the dialog.

In this new example, we save the location of the focus at the opening:


 // Get the dialog element
  const dialog =  document.getElementById('step2-dialog');

 // Save the focus position that will be used when closing
 dialog.setAttribute('data-focusBack', 'step2-openButton');

 // Set the focus on the default item
 dialog.querySelector('.defaultFocus').focus();

It resets the focus when closing:


  // Resets the focus when closing the dialog
  var focusBackElement = dialog.getAttribute('data-focusBack');
  document.getElementById(focusBackElement).focus();

We prevent the focus from leaving the dialog box

For this we rely on the querySelectorAll method. It will allow us to easily retrieve the interactive elements of the dialog box, to then identify the first element and the last element of the box.

Finally, using the onKeyDown event handler, we will be able to intercept the focus when it arrives on the first or the last item, to reposition it at the start or at the end of the modal, as needed.

The full solution is presented in this article: How to trap focus inside modal to make it ADA compliant


  // Get the first focusable element
  const dialogStart = finalDialog.querySelectorAll('button')[0];
  
  // Get the last focusable element
  const dialogFocusableContent = dialog.querySelectorAll('button');
  const dialogStop = dialogFocusableContent[finalFocusableContent.length - 1];

  // Listen the focus sequence, and relocate the focus if necessary depending on which element is currently active
  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();
		  }
		}
	}
  }

Warning

I am a modal dialog. I am accessible to mouse, keyboard and screen reader users.

We also listen to the onKeyDown event to close the dialog when the Escape key is pressed.
Here is the updated HTML code for the dialog:

<div id="step2-dialog" class="dialog">
  <div class="dialogContainer">
    <button>X</button>
    <h1>Warning</h1>
    <p>I am a modal dialog. I am accessible to mouse, keyboard and screen reader users.</p>
    <button class="defaultFocus">OK</button>
    <button>Cancel</button>
  </div>
</div>

Testing

  • Keyboard navigation: there is no problem, the focus moves correctly when opening and closing the dialog. In addition, the focus cannot get out of the dialog when browsing with the Tab key. Finally, the Escape key allows closing the dialog.
  • Screen reader navigation: when opening the dialog, we can hear “OK button”. Moving the focus to the default button automatically vocalizes it but it does not read the title nor the message of the dialog.

Step 3: handling screen reader navigation

To navigate properly with a screen reader:

  • You should indicate explicitly to the user that a dialog has been opened (adding an ARIA role dialog).
  • The dialog title and the message must be vocalized to the user (adding aria-labelledby and aria-describedby attributes).
  • The default button should also be vocalized; this has already been done in the previous step (setting the focus automatically on the button when opening the dialog allows to vocalize the button by the screen reader).

The aria-labelledby attribute is used to specify the id of the element containing the title of the dialog. This will be spoken automatically by the screen reader. The aria-describedby attribute is used to specify the id of the element containing the description. This description will automatically be vocalized after the title of the dialog. Finally, the aria-label attribute on the close button (“X”) specifies a label to be vocalized (otherwise “X” will be read).


<div id="final-dialog" role="dialog" aria-labelledby="final-dialogTitle" aria-describedby="final-description">
  <div class="dialogContainer">
    <button aria-label="close" title="Close">X</button>
    <h1 id="final-dialogTitle">Warning</h1>
    <p id="final-description">I am a modal dialog. I am accessible to mouse, keyboard and screen reader users.</p>
    <button class="defaultFocus">OK</button>
    <button>Cancel</button>
  </div>
</div>

Testing

  • Screen reader navigation: the screen reader should indicate that a dialog was opened. He vocalizes the title and the description. Finally, it indicates that the focus is set on the “OK” button.

Note on navigating using a screen reader

In a web page, when navigating with a screen reader the user mainly uses the Up arrow and the Down arrow. This navigation mode is called “document mode”. One can also use the Tab key but in this case only focusable elements will be vocalized.

In native applications, navigation is mainly using the Tab key. This navigation mode is called “application mode.” Using the ARIA dialog role, the screen reader goes into the “application mode”, i.e. the navigation with the mouse is no longer available when a dialog is displayed. In this case you must use the Tab key to navigate between the buttons of the dialog.

Note that it is also possible to force the use of the “application” or “document” mode with the attribute role, however users are not used to these navigation mode changes and it is currently not recommended to do it, except in exceptional cases:


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