SidebarLayoutcomponent
SidebarLayout({ sidebar, children, right = false }: SidebarLayoutProps): ReactElement| Param | Type | |
|---|---|---|
sidebar | SidebarLayoutProps | The side-column content, rendered inside a <nav>. required |
.sidebar | ReactNode | Content rendered in the fixed-width side column. required |
.right | boolean | Render the sidebar on the right rather than the left. |
| Return | |
|---|---|
ReactElement | The sidebar layout element. |
Layout with a fixed-width side column (typically navigation) next to a scrollable main content column.
- The sidebar is rendered as
<nav>— it almost always contains the page's primary navigation. - On narrow viewports the sidebar becomes an off-canvas drawer toggled by a single menu button that switches between a burger and a close icon.
- While the drawer is open an overlay dims the rest of the page; clicking the overlay closes the drawer.
- Inside a
<Navigation>the drawer closes itself whenever the route changes (e.g. tapping a sidebar link). - The scrollable content column is kept alive across navigation via
<RouteCache>, so returning to a recently-visited page restores its scroll position and state; the sidebar stays mounted throughout. - Use the
--sidebar-layout-width,--sidebar-layout-bg,--sidebar-layout-border, and--sidebar-layout-color-bordercustom properties to override defaults.
A full-viewport layout with a fixed-width side column next to a scrollable main content column. The sidebar renders as a <nav> landmark — it almost always holds the primary navigation. On narrow viewports it collapses to an off-canvas drawer toggled by a single burger/close button.
Things to know:
- Pass
rightto place the sidebar on the right rather than the left. - The sidebar renders as
<nav>, so it is a navigation landmark without extra markup — drop a<Menu>inside it. - While the drawer is open an overlay dims the page; clicking the overlay closes it.
- Inside a
<Navigation>context the drawer closes itself whenever the route changes (e.g. tapping a sidebar link). - The layout owns scroll, padding, and safe-area insets so individual pages don't have to.
Usage
import { SidebarLayout, Menu, MenuItem, Router } from "shelving/ui";
function AppShell() {
const nav = (
<Menu>
<MenuItem href="/dashboard">Dashboard</MenuItem>
<MenuItem href="/users">Users</MenuItem>
<MenuItem href="/settings">Settings</MenuItem>
</Menu>
);
return (
<SidebarLayout sidebar={nav}>
<Router routes={ROUTES}/>
</SidebarLayout>
);
}Layouts compose naturally as <Router> route values — wrap a group of routes in a shared layout, then route further inside it.
Keyboard-aware safe area
useSafeKeyboardArea() (exported alongside the layouts) tracks the dynamic viewport and writes a --layout-inset-bottom custom property reflecting the space hidden behind the on-screen keyboard. This is an iOS Safari workaround until interactive-widget viewport support lands.
import { useSafeKeyboardArea } from "shelving/ui";
useEffect(useSafeKeyboardArea, []);Styling
| Variable | Styles | Default |
|---|---|---|
--sidebar-layout-width | Width of the side column (and drawer) | 17.5rem |
--sidebar-layout-background | Page background while the layout is mounted | var(--tint-100) |
--sidebar-layout-sidebar-background | Sidebar column fill | var(--tint-90) |
--sidebar-layout-content-background | Main content column fill | var(--tint-100) |
--sidebar-layout-border | Divider between sidebar and content | var(--stroke-normal) solid var(--tint-80) |
The sidebar and content columns own their own scroll behaviour directly (this layout no longer composes a shared .layout class). useSafeKeyboardArea() still writes --layout-inset-bottom for layouts that pad to the safe area.
Global tokens it reads — the tint ladder --tint-80 / --tint-90 / --tint-100, plus --space-normal, --stroke-normal, --duration-normal, and --color-shadow.
Examples
<SidebarLayout sidebar={<Menu />}><Page /></SidebarLayout>