Skip to content
View as Markdown

Disclosures

Modal

A blocking dialog for must-complete tasks, built on the native <dialog> element and the browser's top layer, which paints above everything else.

Import

TSX
import { Modal } from "@loamui/core";

Usage

Every example has a CSS tab. That’s the real, complete stylesheet for the component: plain, static CSS, with nothing running in the browser.

Basic usage

Compose the dialog from parts. The Popup is a native <dialog> opened with showModal(): top layer, backdrop, focus containment, Escape and focus restore all come from the browser.

Invite a teammate

They'll receive an email invitation to join your workspace.

Alert dialog (confirmation)

alert renders role="alertdialog": the backdrop doesn't light-dismiss (closing on an outside click or Escape) because closedby="closerequest" answers only Escape, and autoFocus belongs on the least-destructive action so it is the default answer. Use for destructive or irreversible confirmations only.

Delete this file?

“report-final-v2.pdf” will be permanently deleted. This cannot be undone.

Width

The panel sizes to its content between a floor and a readable cap: a confirmation shrink-wraps, a form grows. When a design needs an explicit width, set --loam-modal-size where the modal is used.

Signed out

Sign in again to continue.

Release notes

A wide panel for content that needs the room, set with one custom property where the modal is used.

Header with a close button

A header row with an × is a composition pattern, not configuration: compose Modal.Title and Modal.Close however your design needs.

Settings

Manage your workspace settings.

Guidance

When to use it

  • For blocking, must-complete tasks (confirmations of destructive actions, short focused forms) where the user should not interact with the page behind.
  • When losing the in-progress state would be costly, and the dialog protects it.

When not to

  • For supplementary content or quick actions that don't need to block, use Popover.
  • For anything long-form or multi-step, navigate to a page instead and keep the interaction in the page flow.
  • For non-essential announcements, use Alert in the page.

Accessibility

  • Built on the native <dialog> opened with showModal(): the browser provides the top layer, ::backdrop, real focus containment, Escape handling, and restores focus to the trigger on close. None of it re-implemented in JavaScript.
  • Modal.Title and Modal.Description automatically label and describe the dialog via aria-labelledby / aria-describedby.
  • Modal.Trigger carries aria-haspopup="dialog" and aria-expanded, which follows the open state, so a screen reader says what the button opens and whether it is open.
  • Light dismiss (clicking the backdrop) uses the closedby attribute where supported, with a small feature-detected coordinate-check fallback elsewhere: no polyfills, per the browser support policy.
  • Body scroll is locked while open.

How it works

Destructive confirmations use the alert variant

The alert prop on Modal.Popup renders role="alertdialog" and sets closedby="closerequest": the backdrop stops light-dismissing, so a stray click cannot answer a destructive question; only an explicit choice or Escape closes it. Reserve it for decisions with consequences; an ordinary modal should stay casually dismissible.

Focus is the browser's to manage

showModal() moves focus into the dialog, contains it, and returns it to the trigger on close. Add autoFocus only when the dialog's task starts at a specific control, such as a name field in a rename dialog. Anything else fights behaviour screen-reader users rely on.

A modal is one task

If the content scrolls, needs sections, or asks more than one question, it has outgrown the dialog: make it a page. The dialog's value is that everything needed for the decision is visible at once.

Always render a Title

Modal.Title labels the dialog via aria-labelledby; it is what screen readers announce on open. A dialog without one is announced as, at best, "dialog": the user hears that something opened but not what it wants. A Popup with neither a Title nor an aria-label is reported in development.

Parts

Custom properties

PropertySyntaxDefaultDescription
--loam-modal-sizeCSS lengthfit-content, between 24rem and 32remExplicit panel width, replacing the content-sized default and its bounds. The panel never exceeds the viewport either way.