Modals interrupt the user's flow to request focused attention, which makes accessibility especially important. This lesson builds a complete Tailwind modal, covering the overlay, positioning, and keyboard behavior.
What Makes Up a Tailwind Modal?
A modal combines several concepts from earlier lessons: fixed inset-0 for a full-screen overlay, flex items-center justify-center to center the dialog, a high z-* value to layer above everything else, and a semi-transparent backdrop using a color opacity modifier.
Beyond styling, a fully accessible modal also needs to trap keyboard focus inside itself while open, and close on both an explicit close button and the Escape key.
Checking e.target === e.currentTarget ensures clicks inside the dialog box itself don't accidentally close the modal.
Trapping Focus Inside the Modal
While a modal is open, Tab and Shift+Tab should cycle only through focusable elements inside the dialog, never back to the underlying page, and focus should move to the modal automatically when it opens, then return to the triggering element when it closes.
Move focus to the modal's first focusable element (or the dialog itself) when it opens.
Trap Tab/Shift+Tab navigation within the modal's focusable elements while it's open.
Return focus to the element that triggered the modal once it closes.
Consider using a well-tested library (like Headless UI's Dialog) instead of hand-rolling focus trapping.
Accessible Modal Requirements
Modals are one of the trickiest components to make fully accessible by hand. At minimum, they need proper ARIA roles, focus management, and full keyboard operability.
Use role="dialog", aria-modal="true", and aria-labelledby pointing to the dialog's heading.
Trap focus inside the modal while it's open and restore it afterward.
Always support closing via Escape, not just a visible close button.
Common Mistakes
Building a modal with no focus trapping, letting keyboard users tab into the hidden background page.
Forgetting to lock body scroll while the modal is open, causing a confusing double-scroll experience.
Not closing the modal on Escape or backdrop click, only on an explicit close button.
Using a low z-index that lets other fixed or sticky elements render above the modal.
Hand-rolling complex modal accessibility from scratch instead of using a proven library like Headless UI when the project already uses a JS framework.
Key Takeaways
A modal combines a fixed full-screen overlay, a centered dialog box, and a high z-index layer.
Modals must support closing via Escape and backdrop click, not just an explicit close button.
Focus should move into the modal on open and trap Tab navigation until it closes.
role="dialog", aria-modal="true", and aria-labelledby are the minimum required ARIA attributes.
Consider a tested accessibility library instead of hand-rolling focus trapping for production applications.
Pro Tip
If your project already uses React, Vue, or a similar framework, use a library like Headless UI or Radix for the modal's behavior and accessibility, then style it entirely with your own Tailwind classes, you get correct focus trapping without maintaining it yourself.
You now understand how to build an accessible modal dialog. Next, learn how to build a dropdown menu with Tailwind CSS.