Concepts

Stacking

How multiple modals stack and what "topmost" means globally vs per-group.

Stacking

Multiple modals can be open at once. The package keeps a single global stack and routes Esc, overlay clicks, and focus to the topmost layer — without unmounting underlying modals.

One global stack

Every open modal lives in one ordered list. Opening appends, closing removes. The last entry is the global topmost modal.

[ Modal A: default ]   ← opened first
[ Modal B: default ]   ← opened second (global top)

This list drives all stacking decisions: which modal reacts to Esc, which one owns focus, which one closes when you click outside.

Global topmost vs topmost in group

ConceptDrives
Global topmost — last entry overallEsc key, overlay click — only the global topmost reacts.
Topmost in group — last entry with that groupWhich modal is visually "open" inside a <ModalTarget>.

These differ when groups mix. With a default form open and a confirm dialog on top:

[ Modal A: default ]   ← mounted, data-state="closed"
[ Modal B: confirm ]   ← global top — reacts to Esc

Pressing Esc closes Modal B. The default target ignores the event.

Underlying modals stay mounted

When a second modal opens on top, the underlying one is not unmounted. Form values, scroll position, and local state are preserved. Its <ModalContent> switches to data-state="closed" so your CSS animates or hides it.

Statedata-state
Visible top in its group"open"
Mounted but covered"closed"
Playing exit animation"closed" (until animation ends, then unmounts)

Stack swap sequencing

When the visible top within a group changes, the package waits for the outgoing modal's CSS exit animation before promoting the next one. You never see two modals "open" simultaneously mid-transition.

Group "default"           Group "confirm"
─────────────────         ─────────────────
[ A: open ]               (empty)

openModal(ConfirmDialog)  →  group "confirm"

[ A: closed ]             [ B: open ]
  (still mounted)

close B

[ A: open ]               (empty)
  (promoted back)

Each group sequences its own stack independently.

Same-group stacking

Multiple modals in the same group stack inside one <ModalTarget>. Only one is data-state="open" at a time; closing the top promotes the next one back after its enter animation.

openModal(FormDialog)
// from inside FormDialog:
openModal(ConfirmDialog)

Cross-group stacking

Each group has its own <ModalTarget>, so modals from different groups render in separate DOM subtrees with their own overlays. The global stack still coordinates Esc and overlay-click priority across groups.

A modal opened in a group with no mounted <ModalTarget> has nowhere to render. Mount one target per group you use.
Copyright © 2026