To you, modal windows1 might be a blessing of additional screen real estate, providing a way to deliver contextual information, notifications and other actions relevant to the current screen. On the other hand, modals might feel like a hack that you’ve been forced to commit in order to cram extra content on the screen. These are the extreme ends of the spectrum, and users are caught in the middle. Depending on how a user browses the Internet, modal windows can be downright confusing.
Modals quickly shift visual focus from one part of a website or application to another area of (hopefully related) content. The action is usually not jarring if initiated by the user, but it can be annoying and disorienting if it occurs automatically, as happens with the modal window’s evil cousins, the “nag screen” and the “interstitial.”
However, modals are merely a mild annoyance in the end, right? The user just has to click the “close” button, quickly skim some content or fill out a form to dismiss it.
Well, imagine that you had to navigate the web with a keyboard. Suppose that a modal window appeared on the screen, and you had very little context to know what it is and why it’s obscuring the content you’re trying to browse. Now you’re wondering, “How do I interact with this?” or “How do I get rid of it?” because your keyboard’s focus hasn’t automatically moved to the modal window.
This scenario is more common than it should be. And it’s fairly easy to solve, as long as we make our content accessible to all through sound usability practices.
For an example, I’ve set up a demo of an inaccessible modal window2 that appears on page load and that isn’t entirely semantic. First, interact with it using your mouse to see that it actually works. Then, try interacting with it using only your keyboard.
Better Semantics Lead To Better Usability And Accessibility
Usability and accessibility are lacking in many modal windows. Whether they’re used to provide additional actions or inputs for interaction with the page, to include more information about a particular section of content, or to provide notifications that can be easily dismissed, modals need to be easy for everyone to use.
To achieve this goal, first we must focus on the semantics of the modal’s markup. This might seem like a no-brainer, but the step is not always followed.
Suppose that a popular gaming website has a full-page modal overlay and has implemented a “close” button with the code below:
<div id="modal_close" onClick="modalClose()">
div element has no semantic meaning behind it. Sighted visitors will know that this is a “close” button because it looks like one. It has a hover state, so there is some visual indication that it can be interacted with.
But this element has no inherit semantic meaning to people who use a keyboard or screen reader.
There’s no default way to enable users to tab to a
div without adding a
tabindex attribute to it. However, we would also need to add a
:focus state to visually indicate that it is the active element. That still doesn’t give screen readers enough information for users to discern the element’s meaning. An “X” is the only label here. While we can assume that people who use screen readers would know that the letter “X” means “close,” if it was a multiplication sign (using the HTML entity
×) or a cross mark (
❌), then some screen readers wouldn’t read it at all. We need a better fallback.
We can circumvent all of these issues simply by writing the correct, semantic markup for a button and by adding an ARIA label for screen readers:
<button type="button" class="btn-close" id="modal_close" aria-label="close">
By changing the
div to a button, we’ve significantly improved the semantics of our “close” button. We’ve addressed the common expectation that the button can be tabbed to with a keyboard and appear focused, and we’ve provided context by adding the ARIA label for screen readers.
That’s just one example of how to make the markup of our modals more semantic, but we can do a lot more to create a useful and accessible experience.
Making Modals More Usable And Accessible
Including Focus States
Provide a focus state! This obviously isn’t exclusive to modal windows; many elements lack a proper focus state in some form or another beyond the browser’s basic default one (which may or may not have been cleared by your CSS reset). At the very least, pair the focus state with the hover state you’ve already designed:
However, because focusing and hovering are different types of interaction, giving the focus state its own style makes sense.
box-shadow: 0 0 3px rgba(0,0,0,.75);
Really, any item that can be focused should have a focus state. Keep that in mind if you’re extending the browser’s default dotted outline.
Saving Last Active Element
When a modal window loads, the element that the user last interacted with should be saved. That way, when the modal window closes and the user returns to where they were, the focus on that element will have been maintained. Think of it like a bookmark. Without it, when the user closes the modal, they would be sent back to the beginning of the document, left to find their place. Add the following to your modal’s opening and closing functions to save and reenable the user’s focus.
function modalShow ()
lastFocus = document.activeElement;
function modalClose ()
lastFocus.focus(); // place focus on the saved element
When the modal loads, focus should shift from the last active element either to the modal window itself or to the first interactive element in the modal, such as an input element. This will make the modal more usable because sighted visitors won’t have to reach for their mouse to click on the first element, and keyboard users won’t have to tab through a bunch of DOM elements to get there.
var modal = document.getElementById('your-modal-id-here');
function modalShow ()
Going Full Screen
If your modal takes over the full screen, then obscure the contents of the main document for both sighted users and screen reader users. Without this happening, a keyboard user could easily tab their way outside of the modal without realizing it, which could lead to them interacting with the main document before completing whatever the modal window is asking them to do.
function focusRestrict ( event )
document.addEventListener('focus', function( event )
if ( modalOpen && !modal.contains( event.target ) )
If we also put the modal at the top of the DOM tree, as the first child of
var m = document.getElementById('modal_window'),
p = document.getElementById('page');
// Remember that <div id="page"> surrounds the whole document,
// so aria-hidden="true" can be applied to it when the modal opens.
function swap ()
A second option would be to create a list of all focusable nodes in the modal window and, upon the modal firing, allow for tabbing only through those nodes.
A third option would be to find all focusable nodes outside of the modal and set
tabindex="-1" on them.
The problem with these first and second options is that they render the browser’s chrome inaccessible. If you must take this route, then adding a well-marked “close” button to the modal and supporting the Escape key are critical; without them, you will effectively trap keyboard users on the website.
The third option allows for tabbing within the modal and the browser’s chrome, but it comes with the performance cost of listing all focusable elements on the page and negating their ability to be focused. The cost might not be much on a small page, but on a page with many links and form elements, it can become quite a chore. Not to mention, when the modal closes, you would need to return all elements to their previous state.
Clearly, we have a lot to consider to enable users to effectively tab within a modal.
Finally, modals should be easy to dismiss. Standard
alert() modal dialogs can be closed by hitting the Escape key, so following suit with our modal would be expected — and a convenience. If your modal has multiple focusable elements, allowing the user to just hit Escape is much better than forcing them to tab through content to get to the “close” button.
function modalClose ( e )
if ( !e.keyCode
Moreover, closing a full-screen modal when the overlay is clicked is conventional. The exception is if you don’t want to close the modal until the user has performed an action.
mOverlay.addEventListener('click', function( e )
if (e.target == modal.parentNode)
modalClose( e );
Additional Accessibility Steps
aria-hidden attribute. By toggling the value
false, the element and any of its children will be either hidden or visible to screen readers. However, as with all ARIA attributes, it carries no default style and, thus, will not be hidden from sighted users. To hide it, add the following CSS:
Notice that the selector is pretty specific here. The reason is that we might not want all elements with
aria-hidden="true" to be hidden (as with our earlier example of the “X” for the “close” button).
However, if the modal is more of an error or alert message that requires the user to input something before proceeding, then use
aria-labelledby attribute along with
role="dialog". If your modal window has a heading, you can use the
aria-labelledby attribute to point to it by referencing the heading’s ID. If your modal doesn’t have a heading for some reason, then you can at least use the
aria-label to provide a concise label about the element that screen readers can parse.
What About HTML5’s Dialog Element?
Chrome 37 beta and Firefox Nightly 34.0a1 support the
dialog element, which provides extra semantic and accessibility information for modal windows. Once this native
dialog element is established, we won’t need to apply
role="dialog" to non-dialog elements. For now, even if you’re using a polyfill for the
dialog element, also use
role="dialog" so that screen readers know how to handle the element.
var modal = document.getElementById('myModal'),
openModal = document.getElementById('btnOpen'),
closeModal = document.getElementById('btnClose');
// to show our modal
openModal.addEventListener( 'click', function( e )
// to close our modal
closeModal.addEventListener( 'click', function( e )
show() method launches the dialog, while still allowing users to interact with other elements on the page. The
showModal() method launches the dialog and prevents users from interacting with anything but the modal while it’s open.
dialog element also has the
open property, set to
false, which replaces
aria-hidden. And it has its own
::backdrop pseudo-element, which enables us to style the modal when it is opened with the
There’s more to learn about the
dialog element than what’s mentioned here. It might not be ready for prime time, but once it is, this semantic element will go a long way to helping us develop usable, accessible experiences.
Where To Go From Here?
Whether you use a jQuery plugin or a homegrown solution, step back and evaluate your modal’s overall usability and accessibility. As minor as modals are to the web overall, they are common enough that if we all tried to make them friendlier and more accessible, we’d make the web a better place.
I’ve prepared a demo of a modal window4 that implements all of the accessibility features covered in this article.
(hp, il, al, ml)
- 1 http://www.smashingmagazine.com/2009/05/27/modal-windows-in-modern-web-design/
- 2 http://www.smashingmagazine.com/wp-content/uploads/2014/inaccessible.html
- 3 http://www.w3.org/TR/wai-aria/
- 4 http://www.smashingmagazine.com/wp-content/uploads/2014/accessible.html
The post Making Modal Windows Better For Everyone appeared first on Smashing Magazine.
Read this article:
Making Modal Windows Better For Everyone