Layout
PageFrame
The page container for map-first apps. A panel, split or full page over a live stage, with a non-modal bottom sheet on phones and a stageInsets store that reports what it covers.
<script>
import { PageFrame, stageInsets } from "@codeatlasdev/ui";
$effect(() => map.fitBounds(bounds, { padding: stageInsets.current }));
</script>
<PageFrame mode="map" side="left" width="md" sheet label="Territory" headerBorder>
{#snippet header()}<h2>Territory</h2>{/snippet}
{#snippet footer()}<Button variant="filled">Save</Button>{/snippet}
<!-- scrolling content -->
</PageFrame>
Use PageFrame when a live stage (a map, a canvas) sits behind the page content. PageShell and AppList own the whole content area; PageFrame lays the page out over the stage and tells the stage what it covers. Place it as the page content of AppShellLayout: it fills the content area and lays its panel out inside it, leaving the rail and panel alone. It does not need the shell: it fills any position: relative parent that has a height, so a one-screen map app puts the stage and the PageFrame in its own <main id="main" tabindex="-1"> (PageFrame never renders <main>). See Map page without a shell.
mode |
Desktop | Below 900px |
|---|---|---|
map |
A floating panel (side, width). The stage around it stays interactive. |
Full screen, or with sheet a non-modal bottom sheet. |
split |
A work area of about 60% (tables, kanban) plus a stage strip. | Full screen; mobileStage shows the stage instead. |
page |
An opaque full page. The stage is hidden (data-ca-stage="hidden" on <html>). |
The same. |
header, toolbar and footer stay fixed; only the body scrolls (overscroll-behavior: contain). The body is keyboard-scrollable while it overflows.
Covered area
The frame publishes the rectangle it covers, in px from each edge of the stage, on stageInsets. It updates on resize, mode, side, width and every sheet height, and resets to zero when the frame unmounts. When two frames are mounted the newest one wins and the previous one takes over when it leaves.
import { stageInsets } from "@codeatlasdev/ui";
stageInsets.current; // { top, right, bottom, left }, reactive
map.fitBounds(bounds, { padding: stageInsets.current });
Without JavaScript, read --ca-stage-inset-top, -right, -bottom and -left on <html>. Never copy the panel widths or the 45% sheet height into the app: they are tokens of the frame.
Bottom sheet
With sheet, mode="map" below 900px is a bottom sheet, not a Sheet: it is not modal (no backdrop, no focus trap, no scroll lock, no inert), so the stage above it still takes taps and pans. Its height snaps to a detent: peek (header only), half (45%, the default) or full. Drag the handle, or focus it and press Arrow Up and Arrow Down (Home and End jump to the ends). bind:detent follows the user.
Map, split and page
Switch the mode and side; the readout shows the pixels the frame reports on stageInsets.
Territory
Covers 0px left, 0px right
Reference #610 · Pick in the gallery
Code
Props
| Prop | Type | Default | Description |
|---|---|---|---|
mode | 'map' | 'split' | 'page' | 'map' | `map`: floating panel. `split`: work area plus stage strip. `page`: opaque full page, stage hidden. |
side | 'left' | 'right' | 'left' | Edge the panel (or the split work area) sits on. |
width | 'sm' | 'md' | 'lg' | 'xl' | 'md' | Panel width on desktop: 352, 416, 544 or 704px. Ignored by `split` and `page`. |
sheet | boolean | false | Below 900px, `mode="map"` is a non-modal bottom sheet instead of a full-screen layer. |
detent | 'peek' | 'half' | 'full' | 'half' | Sheet height step (bindable). Dragging the handle or its arrow keys change it. |
mobileStage | boolean | false | `mode="split"` below 900px: show the stage instead of the work area. |
padded | boolean | true | Pads the body. Turn off for full-bleed tables. |
headerBorder | boolean | false | Separator under the header. |
publishInsets | boolean | true | Publish the covered rectangle on `stageInsets`. |
label | string | 'Panel' | Accessible name of the panel region (localized default). |
header | Snippet | – | Title, description and actions. Fixed. |
toolbar | Snippet | – | Fixed row under the header: search, filters, tabs. |
footer | Snippet | – | Fixed row at the bottom: primary actions. |
children | Snippet | – | Scrolling body. |
class | string | '' | Extra class names. |