Accessible Modal Windows

Love them or hate them, modal windows are an established pattern employed by nearly every website today. It is true that they are easier to abuse than the office candy jar. For example, do you want to block a user from finishing a task to show an interstitial ad? Use a modal! Need to make your website a little more app-like just because? Use a modal! Desperate to go viral? Use a… well, you get the point. But for all their misuse, modals do have legitimate utility.

There are a variety of reasons why we would opt to use modal windows, and for the sake of controversy, we can discuss in another post. What they all share in common is that they are excellent for contextualizing a subtask while retaining the user within their current place on the page. When triggered, the modal window appears, usually accompanied by a darker or lighter backdrop beneath it to highlight its content front and center for immediate interaction. But how do we achieve this effect—which is overly visual—in an accessible way?

Modal windows will require some special considerations in order to make their content available to assistive technologies like screen readers or alternative inputs like keyboard navigation. For getting users to interact with modal content, or to simply dismiss it, how will they find it when a modal’s content is most likely in a different part of the DOM, separate from its trigger? How will they close it? And what happens afterwards?

Let’s take a look at various ways to go about addressing these concerns.

ARIA Roles

Using

role="dialog"
informs screen readers that the content within is separate from the rest of the content on the page, in much the way we try to accomplish with a layered presentation. When we open a modal window, its content becomes isolated and we can easily scan it to determine its function. This is visual design honoring intention. A screen reader would have to read the content line by line before any determination about using the modal can be made. Even after that, it could be ambiguous. Luckily, this can be addressed.

In the modal’s root, we can use

aria-labelledby
and
aria-describedby
attributes with values that correspond to an id of elements within it. Think of it as a way to describe an element in speaking terms using a name and a description.

<div
  class="modal"
  role="dialog"
  aria-labelledby="modal-labelledby"
  aria-describedby="modal-selection-describedby"
>
  <h2 id="modal-selection-labelledby">The Great Modal</h2>
  <p id="modal-selection-describedby">This modal will do great things.</p>
</div>

I should also point out that this content doesn’t necessarily have to be part of the visual presentation.

We can further separate modal content from the rest of the page using the aria-hidden attribute on anything that is not applicable to an open window. What content would that be? Perhaps the main navigation or footer or even the main content area. However, be very careful in how aria-hidden is applied because it will impact every child element. In other words, if a modal’s markup is situated in the DOM within a block set to aria-hidden, the modal in turn will be hidden from screen readers.

Providing Focus

One thing to remember is that your modal window’s trigger may not be anywhere near the modal window in the DOM structure. When opening a modal, it’s important that its content is actionable and the user is not left to find it wherever it lives in the markup. With a mouse, this is easy. We just move the cursor over top of the window and start clicking. With keyboard navigation or screen readers, we need to use

:focus
.

When we instantiate a modal window as JavaScript loads successfully, we can assign a tabindex of -1 to each modal window element so that when it is opened, we can programmatically set its

:focus
so the user may interact with that content immediately.

<div
  class="modal"
  role="dialog"
  aria-labelledby="modal-labelledby"
  aria-describedby="modal-selection-describedby"
  tabindex="-1"
></div>

Class Dismissed

Not so fast! That was a joke and I apologize. By dismissing, I’m talking about closing the modal window, which is the best thing to do with them, right? As it often goes, a little redundancy can go a long way. One way to close a window would be through using the ESC key. Backdrops can be created with a button element, which will also close the modal. And finally, a modal window may have a more explicit close button in one of the corners, primarily for visual users.

Modal windows should always be a round trip. After closing a modal, we should politely drop off at the orginial point of departure. The way we do that is by capturing the element that triggered the modal upon opening it, and then returning

:focus
to that element after we’ve dismissed the window. Implementations may vary.

On way of capturing the clicked element in the opening function

this.clickedElement = event.currentTarget;

Then in the close function, we return

:focus
to the original trigger

this.clickedElement.focus();

Wrapping things up

Modals present a unique challenge for accessibility, and design alternatives should be considered if any complexity is introduced. As we can see, building them correctly isn’t simply a matter of opening and closing them. As with the rest of front-end development, there is much more than meets the eye, or meets the mouse. This is our job.