Sidebar
The Dashboard's primary navigation. Icon-collapsed by default with hover-to-expand, collapsible section labels, and per-item pinning. Sits fixed on the left of every Dashboard screen.
Live preview
<Sidebar defaultSelectedKey="home" />Anatomy
The sidebar collapses to a 48px icon rail; hovering expands it to a 256px overlay with a soft shadow. Clicking the toggle on the right edge pins the expanded state.
- 1Brand mark
Yuno logo. Anchors the top of the rail and stays centered when collapsed.
- 2Section label
Uppercase 10px muted caption (see exception note below). Clicking it collapses the section's items.
- 3Nav item
Icon (Phosphor light) + label. Active state uses sidebar-accent surface + primary text.
- 4Sub-item
Nested list under an item with children. Indented with a border-l divider.
- 5Pin button
Hover-revealed pushpin. Pinned items surface into a PINNED section at the top.
- 6Toggle
Circular button on the right edge. Toggles between icon-only and fully expanded.
Information architecture
The sidebar is organized in four semantic zones. Each zone groups items by how the merchant uses them, not by product taxonomy.
Daily operational surfaces the merchant lands on first. No group header — just the four essentials, always visible.
Foundational configuration that must be in place before a single transaction flows. Collapsed by default so the daily-use rail stays uncluttered.
Optional product surfaces that extend what the merchant can do. Collapsed by default.
Developer and audit tooling, visited less often. Introduced by a divider, no label.
IA rules
- Order by frequency of use first (Home before Insights before Operations), then setup before consumption (Connections before Routing before Checkout).
- Max one level of children — no grandchildren. Parents with children are pure expanders; leaf items are the destinations.
- Only leaf items can be pinned. Home, Insights, Reconciliation, and API Reference are non-pinnable — they already sit at surface.
- Section labels are uppercase 10px only for grouping surfaces (CORE SETUP, SOLUTIONS, PINNED). Sentence case everywhere else.
Entry animation
On mount, every element in the sidebar cascades in top-down. The effect is intentional — it draws the eye to Home first and reveals the setup + solutions groups last, hinting at the hierarchy without a heavy header treatment.
| Effect | slide-in-from-left + fade-in (composed) |
|---|---|
| Distance | translateX(-14px) → 0 |
| Curve (slide) | cubic-bezier(0.22, 1, 0.36, 1) |
| Duration (slide) | 500 ms |
| Curve (fade) | ease-out |
| Duration (fade) | 450 ms |
| Stagger — nav items | delay = 120 + index × 30 (ms). Index counts every slot in reading order: separators, section labels, and nav items — the cascade reads as one continuous top-down beat. |
| Stagger — sub-items | delay = childIndex × 30 (ms), reset per parent when it opens. |
| Reduced motion | Honored globally via prefers-reduced-motion in globals.css — animations become instant. |
Tokens involved
- --animate-fade-in — fade-in 0.45s ease-out both
- --animate-slide-in-left — slide-in-from-left 0.5s cubic-bezier(0.22, 1, 0.36, 1) both
Behavior notes
When to use
- As the shell of any Dashboard-adjacent prototype.
- When you need to prototype navigation between multiple product areas (Payments, Rules, Reports…).
- When the flow involves nested destinations (e.g. Operations → Payments, Transactions).
When not to use
- For marketing landing pages — use a top nav.
- For focused single-flow prototypes (wizards, standalone modals) — hide it or use a bare shell.
- For mobile-first prototypes — this sidebar is desktop-only.
Usage
- Wrap it in a fixed container (fixed inset-y-0 left-0 z-30) at the layout level.
- Reserve a 256px content column when expanded, or a 48px one when collapsed.
- Let the user pin frequent destinations — don't hide the pushpin.
- Keep parent items as pure expanders — never route them to a page.
- Don't override Phosphor weights — icons are light (default), fill (active) only.
- Don't add a third state to the toggle — it's binary (collapsed / expanded).
- Don't nest more than one level of children — no grandchildren.
- Don't hardcode section labels in UPPERCASE letterSpacing on top of the built-in styles.
Composition
Sidebar is self-sized (flex flex-col h-full). Position it from the parent layout — usually as a fixed sibling of your page content.
import { Sidebar } from "@/components/organisms/sidebar";
export default function DashboardLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<div className="min-h-screen">
<div className="fixed inset-y-0 left-0 z-30">
<Sidebar />
</div>
<main className="pl-64">{children}</main>
</div>
);
}Common configurations
Three canonical ways the Sidebar shows up in Yuno prototypes, plus a partner/white-label variant. Copy the closest one and adjust.
The everyday Dashboard shell: 256px column visible, Home selected. Use for any prototype where nav labels should be legible at all times.
<Sidebar defaultSelectedKey="home" />48px icon-only rail with hover-to-preview. Use when the flow needs maximum canvas width (dense tables, editor screens).
<Sidebar
defaultExpanded={false}
defaultSelectedKey="ops-payments"
/>Hook onSelect into router.push. Only leaf items fire, so parent expanders never navigate on their own.
<Sidebar
defaultExpanded={false}
defaultSelectedKey="ops-payments"
onSelect={(key, label) => router.push(`/${key}`)}
/>Swap logoSrc when the Sidebar sits inside a partner or white-label prototype. Everything else stays the same.
<Sidebar
logoSrc="/my-brand.svg"
defaultSelectedKey="home"
onSelect={(key) => router.push(`/${key}`)}
/>Import
Copy this import line at the top of the file where you compose the Sidebar. No provider is required at the app root: it is self-contained.
import { Sidebar } from "@/components/organisms/sidebar";Props
The Sidebar owns its own IA (sections, items, sub-items, pinning): you configure entry state and wire the select handler. All other layout concerns (fixed positioning, z-index, column reservation) belong on the parent container.
| Prop | Type | Default | Description |
|---|---|---|---|
| defaultExpanded | boolean | true | Uncontrolled initial expanded state. When false, the rail starts collapsed to 48px; hovering still overlays a 256px preview. |
| defaultSelectedKey | string | null | null | Uncontrolled initial active item key. Match a leaf key from the built-in IA (e.g. 'home', 'ops-payments', 'rc-rules'). |
| onSelect | (key: string, label: string) => void | — | Fires when the user activates a leaf item. Wire to your router. Parent items with children never fire this — they toggle open/closed instead. |
| logoSrc | string | "/yuno-logo.svg" | Path to the brand mark shown at the top of the rail. Swap for partner or white-label prototypes. |
| className | string | — | Merged onto the outer <aside> via cn(). Use sparingly — layout (fixed / z-index / column reservation) belongs on the parent, not the sidebar itself. |
Related
Cross-links to atoms and patterns you may reach for next.
- Top barPairs with the Sidebar to form the full Dashboard shell.
- Page headerPage-level title block that sits inside content, below the top bar.
- Account menuLives in the top bar; user identity + profile actions.
- Global chipEnvironment and scope switcher; lives in the top bar.
- TooltipReveals nav item labels when the rail is collapsed.
- IconographyPhosphor icons used in every nav item: light default, fill for active.
- Colorssidebar-accent surface + primary text drive the active-item styling.