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.
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.
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
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.
Next, the fourth problem is when I close the modal, where is my focus?
I don't even know I think it gets lost.
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.
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";
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>
);
}
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)