Modal
A modal is an overlay dialog that focuses the user’s attention on a specific task or content.
<div class="tng-modal"> <div class="tng-modal-panel"> <button class="tng-button is-tertiary is-ghost"> <span>Close</span> <i class="tng-icon icon-close" aria-hidden="true"></i> </button> <div class="tng-modal-content"> <div class="tng-slot is-primary"></div> </div> </div></div>Fullscreen
Section titled “Fullscreen”You can use the positioning utilities to create a fullscreen modal.
<div class="tng-modal | p-absolute at-maximum"> <div class="tng-modal-panel"> <button class="tng-button is-tertiary is-ghost"> <span>Close</span> <i class="tng-icon icon-close" aria-hidden="true"></i> </button> <div class="tng-modal-content"> <div class="tng-slot is-primary"></div> </div> </div></div>With Scroll
Section titled “With Scroll”When a modal contains potentially more content than fits in the viewport, you can use the overflow utility to make the content area scrollable.
<div class="tng-modal" style="max-block-size: 300px"> <div class="tng-modal-panel"> <button class="tng-button is-tertiary is-ghost"> Close <i class="tng-icon icon-close" aria-hidden="true"></i> </button> <div class="tng-overflow-scroll is-block"> <div class="tng-modal-content"> <p class="tng-text-body">…</p> <p class="tng-text-body">…</p> </div> </div> </div></div>With Image
Section titled “With Image”<div class="tng-modal"> <div class="tng-frame"> <div class="tng-slot"></div> </div> <div class="tng-modal-panel"> <button class="tng-button is-tertiary is-ghost"> Close <i class="tng-icon icon-close" aria-hidden="true"></i> </button> <div class="tng-modal-content"> <div class="tng-slot is-primary"></div> </div> </div></div>Recipes
Section titled “Recipes”Modal Dialog
Section titled “Modal Dialog”Accessibility
Section titled “Accessibility”The <dialog> element
Section titled “The <dialog> element”Always render a modal on a <dialog> element and open it with showModal(). This gives you built-in browser behaviour that is difficult to replicate manually:
- Promotes the dialog to the top layer — no
z-indexconflicts. - Renders a
::backdroppseudo-element automatically (.tng-modalon a<dialog>applies thetng-backdropbackground). - Traps focus inside the dialog while it is open.
- Returns focus to the previously focused element on close.
- Closes on Escape by default.
Roles & attributes
Section titled “Roles & attributes”| Attribute | Purpose |
|---|---|
aria-labelledby | Point to the modal’s visible title so assistive technologies announce it. |
aria-describedby | Optionally point to a description paragraph for additional context. |
aria-controls | Set on the trigger button, referencing the dialog’s id. |
aria-modal="true" | Implied automatically when using showModal() — do not set it yourself. |
Inert background
Section titled “Inert background”When a modal is open the rest of the page must be unreachable. showModal() handles this for the accessibility tree, but pointer events on the underlying page can still leak through. Set document.body.inert = true while the dialog is open and reset it on close, as shown in the recipe above.
Keyboard interaction
Section titled “Keyboard interaction”| Key | Action |
|---|---|
| Tab / Shift+Tab | Cycles focus through focusable elements inside the dialog (focus trap is built in with showModal()). |
| Escape | Closes the dialog (built-in). Listen for the close event to run cleanup. |
Closing with a form
Section titled “Closing with a form”Wrap the dialog content in a <form method="dialog">. Any <button> inside the form that is not type="button" will close the dialog and its value will be available via dialog.returnValue. This keeps close / confirm actions accessible without custom JavaScript.