Layout & display
AppShell
The authed application frame: the sidebar, the header bar and the content column. One frame, so a product with several consoles renders one shell rather than one per app.
Why it exists
Three apps hand rolled this frame and no two agreed. The sidebar was 224px in one and 240px in the
other two, opaque in two and translucent in the third, built from SidebarNav in one
and from inline anchors in the others. Two of the three had no header above md:, so
the current page had no name on screen and there was no fixed place for a theme toggle or an
account menu. Every one of those was defensible from inside the app that made it, and together
they read as three products.
Anatomy
Everything is a snippet, so the shell owns the measurements and the app owns the content. It
renders the main landmark and the drawer's dialog role, and nothing else: the
navigation landmark comes from your SidebarNav, and the page's own PageShell owns the one h1. The section name in the header is a span for that reason.
Instances
The page renders here, inside its own PageShell.
The drawer
Below md: the sidebar becomes a drawer behind a hamburger the header grows for it.
There is one aside at every width: rendering a second copy for the drawer puts every nav
link in the page twice, which is what a strict-mode locator trips on and what a screen reader reads
out.
The open drawer is a real dialog. It moves focus in, keeps Tab inside, and hands focus back to the
hamburger when it closes, so aria-modal describes what actually happens rather than
what was claimed. Escape and the backdrop both close it. The dialog role sits on a wrapper and not
on the aside, because a complementary landmark cannot take an interactive role.
The drawer slides in from the edge it is docked on and back out, the right edge in a right-to-left page, and the dimmed page behind it fades in and out, as a Drawer does.
navOpen is bindable so your router can close it after a navigation. Without that, a tapped
link leaves the drawer sitting over the page it just opened.
Where the account goes
In the header, through AccountMenu, never in the sidebar foot. That column is already
the full height of the window, so a menu opened in its last row grows past the bottom edge, and
the entry that gets cut is Sign out every time. The header has room below it by construction.
API
| Prop | Type | Default | Description |
|---|---|---|---|
section | string | no default | The current section, shown in the header. Rendered as a span, not a heading: the page's own PageShell owns the h1. |
navOpen | boolean | false | Bindable. Set it to false after a navigation so the drawer does not stay open. |
collapsible | boolean | false | Adds a header control that puts the sidebar away above md. |
collapsed | boolean | false | Bindable: whether the sidebar is put away above md. Nothing persists it for you. |
rail | boolean | false | Narrows the sidebar to the icon rail at every width above md, not only between md and lg. |
headerHidden | boolean | false | Bindable: puts the header bar away above md, for a page that owns the viewport. Keep a way back on screen. |
sidebarLabel | string | 'Sidebar' | Accessible name for the sidebar region. |
drawerLabel | string | 'Navigation' | Accessible name for the drawer, below md: where the sidebar is a dialog. |
brand | Snippet | no default | The product mark, in the sidebar brand row. |
nav | Snippet | no default | The navigation. A SidebarNav, given class="min-h-0 w-full flex-1 px-inline". |
sidebarFooter | Snippet | no default | A status band at the foot of the sidebar. Not the account: that is header work. |
headerActions | Snippet | no default | The right end of the header. The theme toggle and the account menu live here. |
class | string | no default | Extra classes on the outer frame. |
children required | Snippet | no default | The page, inside main. |
panel | Snippet | no default | A panel on the right. From lg it docks beside the main area and its edge resizes between 320 and 560 pixels. Below lg it opens as a drawer. Put the button that opens it in headerActions. |
panelOpen | boolean | false | Bindable. Whether the panel is open. |
panelLabel | string | no default | The panel's accessible name, as a complementary region and as the drawer's title. |
panelStorageKey | string | no default | The localStorage key the panel's width is kept under. Without one the width lasts the page. |