modal-or-page
Where a Flow Lives
Default to a page and make the overlay argue for itself. A page is linkable, back-navigable and scrollable and needs no focus contract; every overlay borrows those properties and pays interest in dismissal rules, focus return, inertness, scroll locking and an invented URL. An overlay earns its place by clearing one of two bars — the context behind it is needed while the flow runs, or the flow is a single decision the user must answer now. This skill decides which container one flow gets and never designs the app's movement graph: tabs, sidebars, breadcrumbs, active state and back behavior are navigation, which names none of these six containers. Easing, duration and the enter/exit shape of each container are motion; this skill states no timing value.
Use the overlay primitive the project already has. Radix, Base UI, Headless UI, Vaul, shadcn/ui and the framework's own <dialog> each already implement the focus trap, inertness, scroll lock, Escape handling and portal ordering described below, and each exposes them differently. Detect and configure it; a hand-rolled position: fixed div with an onClick backdrop reimplements four accessibility contracts badly and silently. If the project has a routed-overlay convention (parallel routes, an intercepting route, a ?panel= param), every new overlay follows it.
Decision: pick the container
Cheapest first — stop at the first row that fits.
| Container | Choose it when | Blocks | Routed |
|---|---|---|---|
| Inline expand / in-place edit | The change is one field or one short list, and the surrounding content is the reference | no | no |
| Popover | A short anchored choice, no internal scroll, nothing lost on dismiss | no | no |
| Modal / dialog | One decision or one step that must be answered before continuing | yes | only if it holds a flow |
| Drawer (side) | A detail opened repeatedly while a list stays visible and operable | no | yes |
| Sheet (bottom) | The narrow-viewport form of a drawer or modal; gestures owns the drag physics |
yes | yes |
| Full page | Multi-step, needs its own scroll or sub-navigation, or must be resumable | — | always |