SidebarLayoutcomponent

SidebarLayout({ sidebar, children, right = false }: SidebarLayoutProps): ReactElement
ParamType
propsSidebarLayoutProps
Props for <SidebarLayout> — the sidebar column content, main children, and a right placement flag. required
    .sidebarReactNode
Content rendered in the fixed-width side column. required
    .rightboolean
Render the sidebar on the right rather than the left.
Return
ReactElement

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.

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 right to 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.
  • Under prefers-reduced-motion: reduce the drawer snaps open/closed instead of sliding off-canvas; the overlay keeps its dimming fade, which is opacity-only and therefore reduced-motion-safe.
  • 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

tsx
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.

Styling

VariableStylesDefault
--sidebar-layout-widthWidth of the side column (and drawer)17.5rem
--sidebar-layout-backgroundPage background while the layout is mounted (also fills the content column)var(--tint-100) (white)
--sidebar-layout-colorText colour for the layout (set on body, inherited by the content column)var(--tint-00) (black)
--sidebar-layout-sidebar-backgroundSidebar column fillvar(--tint-90) (one shade darker than the page)
--sidebar-layout-sidebar-colorSidebar column text colourvar(--tint-00) (black)
--sidebar-layout-borderDivider between sidebar and contentvar(--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).

Global tokens it reads--tint-00 / --tint-80 / --tint-90 / --tint-100, plus --space-normal, --stroke-normal, --duration-normal, and --color-shadow.