Sidebar
Most apps do not need one: the dashboard already gives you a rail, a breadcrumb and a header bar, and a second rail inside the first is a maze. But an app with genuinely separate areas — a library, a queue, its own settings — earns one, and when it does it should behave exactly like the dashboard’s.
Which means: it collapses to nothing, not to icons. A 56px strip of unlabelled glyphs is a quiz, and the merchant has already spent attention getting into your app. Width and padding animate together over 200ms so the content reflows on one curve rather than two. While collapsed, brushing the left edge floats the rail back as a peek, so a quick jump costs nothing.
.folio-sidebarThe content reflows as the rail folds, on the same 200ms curve.
Collapsed, sweep the left edge of a real app and the rail peeks back without pushing the content.
import { SidebarLayout, Sidebar, SidebarContent, SidebarGroup, SidebarItem, useSidebarCollapsed } from '@/components/app-sidebar'
const [collapsed, setCollapsed] = useSidebarCollapsed('myapp_sidebar') // remembered in localStorage
// The toggle goes in the dashboard's bar, where every other page control is.
useTitleBar({
crumbs: [{ id: 'home', label: 'My app' }],
secondaryActions: [{ label: collapsed ? 'Show menu' : 'Hide menu', onAction: () => setCollapsed(!collapsed) }],
})
<SidebarLayout>
<Sidebar collapsed={collapsed}>
<SidebarGroup title="Library">
<SidebarItem icon={<BoxSolid />} active={tab === 'products'} onClick={() => setTab('products')}>Products</SidebarItem>
<SidebarItem icon={<InboxSolid />} onClick={() => setTab('deliveries')}>Deliveries</SidebarItem>
</SidebarGroup>
<SidebarGroup title="Settings" collapsible defaultOpen={false}>
<SidebarItem onClick={…}>Webhooks</SidebarItem>
</SidebarGroup>
</Sidebar>
<SidebarContent>{/* the page */}</SidebarContent>
</SidebarLayout>Rules
- Collapse to zero width, never to an icon strip. Width and padding transition together, 200ms, ease-out.
- The active item is
aria-current="page", not a class. Screen readers and the styling then agree by construction. - The collapse toggle belongs in the header bar via
titleBar, not floating over your content. - Persist the collapsed state per install — sandbox and live are separate installs and keep separate storage.
- Two or three groups. If you need a fourth, you are building a second dashboard inside the dashboard.