Modal Dialogs

A guide to making accessible modal dialogs.

Summary

The modal dialog pattern presents content that requires a user’s full and immediate attention. The modal characteristic means the user is temporarily blocked from reading or interacting with the underlying webpage until the dialog is closed. The dialog content is presented as an overlay in front of the underlying webpage.

The use cases for modal dialogs include displaying a time-sensitive message for the user to acknowledge, collecting user input required to use the webpage, or confirming a critical action before proceeding.

Caution

Because this pattern intentionally interrupts the user’s workflow, it should be used sparingly, particularly if the modal dialog is opened automatically, for example, on page load. Deploying an inaccessible modal dialog can effectively exclude users from a website entirely. Consider alternatives such as an in-page presentation or a link to a separate, dedicated page.

Semantic dialog roles

WAI-ARIA provides two possible semantic roles that can be used with modal dialogs based on the urgency of information they are meant to communicate:

In practice, to the user, there is little to distinguish thse two roles, but you may choose to style them differently. Some assistive technologies may describe them differently, informing the user which role was used to define the dialog.

Setting aria-modal="true" on a dialog allows assistive technology to inform the user of its modal nature, and screen readers may restrict the user from browsing1 outside of the dialog content.

Implementation options for dialogs

There are currently two options available for implementing modal dialogs:

It’s possible to build nearly identical user experiences using either technique. Deciding which approach to take will depend on several considerations and how those factors might impact or exclude your particular users. We document both methods here and leave it to you to determine which will work better for your users.

Consider browser compatibility

Using a generic HTML element with role="dialog" is long established and can be made to work with older browsers, at least back to Microsoft Internet Explorer 9.

Older browsers do not support using the new dialog element.2 Importantly, no Internet Explorer (IE) version supports it. You can, however, find polyfills for the dialog element designed to support recent versions of Internet Explorer.

Older browsers are an accessibility concern because surveys show that about 3% of people using screen reader software also use Internet Explorer; probably because older versions of the popular JAWS screen reader once recommended Internet Explorer 11. Poorer, older, less technically-savvy users are more likely to use older computers and software.

A custom-built modal dialog implementation

When planning to build an accessible modal dialog from a generic HTML element, we give ourselves the following requirements:

HTML and semantics

Our example implementation uses a generic div HTML element as its basis. Adding the role="dialog" HTML attribute gives it the semantic role of a dialog. The aria-modal="true" attribute tells assistive technology we will present this element modally.

We use the hidden HTML attribute to hide the dialog from users, including assistive technology users, before it is opened.

We will use an h3 element, with an ID of myModal-label-1 as our dialog’s label. To tell assistive technology about this label, we add the aria-labelledby="myModal-label-1" attribute to the element with the role dialog.

Notice that we use a second inner div to hold the dialog’s content, this one with role="document". This is done partly for practical reasons: we need the outer element to look and function as the dialog backdrop, while the inner, content-containing div element is styled to look like a card centered on the screen. The document role is not strictly necessary, but, unless your dialog contains only a form, it is helpful to tell assistive technology to treat the dialog content as it would a document.

<div id="myModal-1" hidden role="dialog" aria-labelledby="myModal-label-1" aria-modal="true">
  <div role="document" class="dialog-content centered-middle">
    
    <h3 id="myModal-label-1" tabindex="0">
      Registration Has Ended
    </h3>
    <p>
      You may continue viewing the registration website but can no longer register.
    </p>
    
    <!-- a button to close the modal dialog -->
    <button type="button" class="button-close" aria-label="Close" onclick="ModalDialog('myModal-1').close()">
      <span aria-hidden="true">×</span>
    </button>
    
  </div>
</div>

The tabindex="0" attribute on the h3 element makes it the first focusable element in the dialog content. Our JavaScript implementation will automatically move keyboard focus to the first focusable element, meaning that for screen readers, it will move to the dialog’s heading when it opens. (You should configure another focusable element in your content if it is more appropriate for the user experience.)

We’ve also added a close button; every modal needs to have an accessible way of being closed. Because we are building this implementation from scratch, it is up to us what JavaScript API we implement to close our dialog. In our example, the API for opening the modal dialog is ModalDialog("myModal-1").open(), and the API for closing it is ModalDialog("myModal-1").close().

Presentation style

A modal dialog has two presentation elements that must be styled: the backdrop and the content.

The modal dialog backdrop

Visual affordances should be carefully considered when implementing a modal interaction. For example, people using screen magnification software or settings may only see a portion of the entire webpage at any time. If the part of the page they can see suddenly becomes non-interactive when a modal dialog opens elsewhere on the page, and there is no accompanying visual indication, this will be very confusing, at least.

Modal dialogs are opened with a full-window “backdrop” behind them. This backdrop should be semi-transparent to visually indicate that while the underlying webpage is still there, it is temporarily blocked from any user interaction.

The modal dialog is presented as a card-like content area within a semi-transparent backdrop layer, sitting above and covering the rest of the underlying webpage.

In our example implementation, the outermost HTML element, which has the aria-modal="true" attribute, acts as the backdrop. We use CSS to position this above and covering the underlying webpage. The backdrop is styled to have a semi-transparent black background-color, allowing the underlying webpage to remain partially visible. We use the position: fixed; declaration to keep the backdrop from moving when the user scrolls.

[aria-modal="true"] {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: 1000;
  background-color: rgba(0, 0, 0, 0.8);
}

Notice we set the z-index to 1000. This is an arbitrary value; it can be any value sufficient to place it atop all other visible layers on the webpage.

The modal dialog content

For our example implementation we want the dialog content to appear as a card centered in the middle of the browser window.

You must consider what will happen on narrow displays when the dialog content may wrap and not fit within its containing card. We use the max-height: 100% CSS declaration to prevent the dialog content from being clipped by the top and bottom boundaries of the web browser window. The overflow: auto declaration will allow the user to scroll the content (either horizontally or vertically) within the dialog in cases if it doesn’t fit within the available space. This is especially important to accommodate users who may set their default font size to be “large” or “very large” in their OS or web browser settings.

.centered-middle {
  position: fixed;
  top: 50%;
  bottom: auto;
  left: 50%;
  transform: translate(-50%, -50%);
}

.dialog-content {
  background-color: #ffffff;
  padding: 0.5rem 1.6rem 2rem 1.6rem;
  max-height: 100%;
  width: 80%;
  max-width: 32rem;
  overflow: auto;
  border: 1px solid currentColor;
}

The rest of the webpage

Another consideration is the underlying webpage while the dialog is open. Even though we’ve blocked any user interaction with the underlying webpage, it is still possible to scroll its content while the dialog is open. This looks odd and can be confusing if the user unintentionally scrolls the webpage content and then finds they are looking at a part of the page they don’t recognize when the dialog eventually closes.

We can prevent the underlying webpage from scrolling by using JavaScript to add a data-hasmodal attribute to the body HTML element whenever a modal dialog opens, then remove that attribute when it closes. This attribute matches a CSS rule that uses overflow: clip, which prevents scrolling.

[data-hasmodal] {
  overflow: clip;
}

Note that the inert HTML attribute can effectively be used to prevent user interaction with the underlying webpage, but because its browser support is limited to modern browsers, and the use of a custom modal dialog approach implies you are interested in supporting older browsers, we’ve chosen to not use it in this example implementation.

Scripted behavior

There are two types of features that we need to implement with JavaScript: opening and closing the modal dialog and controlling keyboard focus.

Opening and closing

The opening and closing task is relatively straightforward. Assuming the HTML element for the dialog was given a hidden HTML attribute, it will already be hidden to start. Our JavaScript must remove that attribute to “open” the modal dialog and then replace the hidden HTML attribute to “close” it later.

It’s common practice to configure modal dialogs so the user can dismiss them by clicking on the backdrop or pressing the escape key. We handle both of these behaviors in our JavaScript. Still, we also recognize limited cases where you want a modal dialog to avoid being so easily dismissed. For example, if you require the user to give some legal consent before proceeding, you might want to enforce a more explicit “I do” or “I don’t” response before continuing. We’ve added support for a custom data-undismissable attribute for such cases. This attribute will exclude those dialogs from being dismissed using the escape key or by clicking the backdrop element. When using this attribute, it is up to you to make other means of closing the modal dialog available to the user.

Controlling keyboard focus

We must implement the modal behavior for our dialog using JavaScript. To limit the movement of keyboard focus, we add an invisible focusable element at the top and bottom of the modal dialog content, creating a “focus fence.” These fence elements are configured with JavaScript to bounce focus back from the bottom to the top and vice-versa whenever they receive focus. The effect is that when focus moves past the end or start of the dialog content, it is moved to wrap around to the opposite side: preventing keyboard focus from leaving the open dialog.

We also use JavaScript to remember which element, if any, was active when a modal dialog opens. We use this information to restore focus back to that element when the modal dialog closes. At the same time, we add a data-hasmodal attribute to the body HTML element of the webpage, which allows us to use a CSS style to turn off scrolling while the modal dialog is open.

To simplify managing keyboard focus, we’ve written a small utility library to manage focus movement.

Notice: if the modal dialog contains an iframe HTML element displaying content from another website, web browser security policies may not let you use JavaScript to move keyboard focus into any part of the iframe content. In such cases, you should add a focusable element to the modal dialog content before the iframe to act as a landing point for keyboard users.

Multiple modals?

Modals are, by definition, singular and exclusive interaction patterns. If you need the user to be presented with more than one modal, rather than layering them together all at once, we prefer to take a chained approach, where each modal is opened individually but in series: only showing the next modal once the previous has closed. We’ve added a feature to our implementation that allows a callback function to be registered and run whenever a modal dialog closes.

So, for example, to open myModal-1 and then have myModal-2 open when myModal-1 closes, you could use this API:

ModalDialog("myModal-1").open(function onClose() {
  ModalDialog("myModal-2").open();
});

This chain could be extended of course, but for the user’s sake, we hope you consider how annoying that could be!

If you find that you want to nest modal dialogs, that is, to have a modal dialog open from within the content of another modal dialog, we suggest that you are beyond the scope of a modal dialog use case. This complexity level indicates that the content in your first modal dialog probably warrants its own dedicated page.

Demo: custom modal dialogs

This demonstration uses the implementation details described above.

Clicking the “Open Both Custom Modal Dialogs” button demonstrates how to chain two modal dialogs together. It will open modal dialog 1 and modal dialog 2 once modal dialog 1 closes.

See the CSS and JavaScript used in the above demo.


Working with the native dialog HTML element

Recent browsers support an alternative approach to creating modal dialogs, using a native dialog HTML element. This comes with built-in features, meaning you won’t have to author and manage those yourself.

Native dialog and keyboard focus

Managing keyboard focus is handled for you by the native dialog element.

Native dialog backdrop

The native dialog element provides its backdrop as a pseudo-element automatically sized to fully cover the web browser window. It can be styled using the CSS ::backdrop pseudo-element selector.

dialog::backdrop {
  background-color: rgba(0, 0, 0, 0.8);
}

Closing a native dialog

Native dialogs are implemented to close in any of the following ways:

<dialog id="dialog1" class="dialog-content" role="alert">
  <h3 tabindex=0>Alert: Logged Out</h3>
  <p>You have successfully logged out. You may safely close the browser now.</p>
  <button type="button" class="button-close" onclick="window.dialog1.close();" aria-label="close">
    <span aria-hidden="true">×</span>
  </button>
</dialog>
<dialog id="dialog2" class="dialog-content" data-undismissable>
  <h3 tabindex=0>
    Are You Sure? 
  </h3>
  <p>
    Before continuing, please confirm that you understand that this is a big deal. 
  </p>
  <form
    method="dialog"
    onsubmit="if (!document.getElementById('check-agree').checked) { return false; } else { console.log('submitted'); }">
    <p>
      <label> 
        <input
          id="check-agree"
          name="check-agree"
          type="checkbox"
          autocomplete="off"
          onchange="document.getElementById('button-continue').disabled = !this.checked">
        Yes, I know what I'm doing.
      </label> 
    </p>
    <p>
      <button id="button-continue" type="submit" disabled>
        Do It
      </button> 
    </p>
  </form>
</dialog>

Native dialog demo

This demo shows how to create a user experience similar to the ones we built using a custom modal dialog approach.

Alert: Logged Out

You have successfully logged out. You may safely close the browser now.

Are You Sure?

Before continuing, please confirm that you understand that this is a big deal.

See the CSS and JavaScript used in the above demo.

Checkpoints for modal dialogs

Regardless of the implementation, all modal dialogs must meet the following criteria.

Notes and references

  1. Assistive technology, like screen readers, operate in different modes. When the user presses keys to operate form elements or navigate from one interactive element on a webpage to the next, screen readers pass those keypresses through to the web browser. For example, pressing the tab key in this mode will move the keyboard focus to the next interactive element. Another mode, called browse mode or reading mode, allows the assistive technology user to advance a virtual reading cursor over each element on a webpage, not just the interactive ones. The assistive technology then will try to describe each element using its label or read out any text or alternative text it finds. back to reference 1

  2. The Can I Use website reports that the dialog element is only available to about 94% of global web browser users. back to reference 2