DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»

DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’» is a community of 963,274 amazing developers

We're a place where coders share, stay up-to-date and grow their careers.

Create account Log in
Cover image for Building accessible modals
Top
Top

Posted on

Building accessible modals

If you are a frontend developer, I think you know that modal is an ubiquitous UI elements on the web.
Today I will show you how to implement it solving the hard problems.

Image description

Everything works with this when you are using a mouse.

πŸ’‘ But what if we weren't using mouse?
What if we were navigating with the keyboard, so we can start here if I press Tab, it moves to the next element, I press Enter, it opens the modal.

And here we run into our first problem.
Because if you notice, my focus is still on this button, I am still kind of floating around in the background here.

Image description

A related issue is I should have my focus be locked to this modal.
So if I am at the final interactive element and I press Tab, it should cycle focus back to the escape button.
So I should move through these in a big loop, a never ending loop

Image description

Third, I ought to be able to close the modal by pressing Esc.
And this is just something that you expect just generally, even if you are a mouse user, but it is especially important for keyboard users.

Image description

Next, the fourth problem is when I close the modal, where is my focus?
I don't even know I think it gets lost.

Image description

It should be restored to the element that was focused before the modal was open, usually the modal trigger.
So if I click this button, it focuses the button.
And then when I close the modal, when I close the modal, focus should be restored to this element.

Image description

Fifth is that we need to annotate our markup so that people
know that this is a modal, for this, I have deactivated all this background content.

πŸ’‘ Problems:
In this modal component, how is it supposed to know where to restore focus, once you close the modal, we would need to have that in global state where we are constantly keeping track of what's focused.
And then knowing how to restore when we close the modal, it's non trivial stuff, it's really really hard.

Thankfully, we don't have to solve those problems ourselves, because there are tools that exist that do it.

And the one that we are going to use today, it is called a
Reach UI.

So in this, I am going to use Reach UI, and import DialogOverlay and DialogContent.

import {
  DialogOverlay,
  DialogContent
} from "@reach/dialog";
Enter fullscreen mode Exit fullscreen mode

Next, we have to actually tell the Reach component whether or not it's open.
So before we were just bailing early, so that if this modal component was called without isOpen being truthy, it was just bailing.
Instead, we are going to pass that straight to the Overlay.
So I am going to say isOpen={isOpen}.
And we are also going to give Reach UI the ability to close the modal with an onDismiss callback, have it be equal to handleDismiss.

function Modal({
  title,
  isOpen,
  handleDismiss,
  children
}) {
  return (
    <Overlay
      isOpen={isOpen}
      onDismiss={handleDismiss}
    >
      <Content aria-label={title}>
        <Header>
          <Title>{title}</Title>
          <CloseButton onClick={handleDismiss}>
            <X />
            <VisuallyHidden>
              Dismiss modal
            </VisuallyHidden>
          </CloseButton>
        </Header>
        <ChildWrapper>{children}</ChildWrapper>
      </Content>
    </Overlay>
  );
}
Enter fullscreen mode Exit fullscreen mode

If I try to do this with the keyboard, I press Enter, it opens.
And notice where my focus moves, it moves to the escape button
As I press Tab, I move through the interactive elements in the modal, but it cycles through them, it makes sure that focus can't drop to these background elements.

And I feel pretty good using Reach UI for those really difficult mechanics and managing the CSS myself.

Top comments (0)

In defense of the modern web

I expect I'll annoy everyone with this post: the anti-JavaScript crusaders, justly aghast at how much of the stuff we slather onto modern websites; the people arguing the web is a broken platform for interactive applications anyway and we should start over;

React users; the old guard with their artisanal JS and hand authored HTML; and Tom MacWright, someone I've admired from afar since I first became aware of his work on Mapbox many years ago. But I guess that's the price of having opinions.