Sidebar Collapsible
Sidebar that collapses from 240px to a 64px icon rail with a toggle button (aria-expanded); collapsed items keep tooltips (title) and sr-only labels. Always a rail on small screens.
neo-corporate/layout/sidebar-collapsibleSource
"use client";
import { useState, type ReactNode } from "react";
import { ArrowLeftRight, BarChart3, FileText, Home, PanelLeftClose, PanelLeftOpen, Receipt, Settings, Users, Wallet, type LucideIcon } from "lucide-react";
import { cn } from "@/lib/utils";
export interface SidebarCollapsibleProps {
brand?: string;
items?: { label: string; href: string; icon: LucideIcon }[];
activeHref?: string;
defaultCollapsed?: boolean;
onCollapsedChange?: (collapsed: boolean) => void;
children?: ReactNode;
className?: string;
}
const DEFAULT_ITEMS = [
{ label: "Overview", href: "#overview", icon: Home },
{ label: "Invoices", href: "#invoices", icon: FileText },
{ label: "Bills", href: "#bills", icon: Receipt },
{ label: "Transactions", href: "#tx", icon: ArrowLeftRight },
{ label: "Cards", href: "#cards", icon: Wallet },
{ label: "Vendors", href: "#vendors", icon: Users },
{ label: "Reports", href: "#reports", icon: BarChart3 },
];
function LedgerlyMark({ className }: { className?: string }) {
return (
<svg aria-hidden viewBox="0 0 28 28" className={cn("size-7 shrink-0", className)}>
<rect width="28" height="28" rx="7" fill="var(--da-primary)" />
<path d="M8 8v12h12" stroke="var(--da-primary-fg)" strokeWidth="2.6" fill="none" strokeLinecap="round" />
<path d="M13 15h7M13 11h7" stroke="var(--da-primary-fg)" strokeWidth="2.2" strokeLinecap="round" opacity=".7" />
</svg>
);
}
/** Sidebar that collapses from 240px to a 64px icon rail with a toggle button (aria-expanded); collapsed items keep tooltips (title) and sr-only labels. Always a rail on small screens. */
export function SidebarCollapsible({
brand = "Ledgerly",
items = DEFAULT_ITEMS,
activeHref = "#tx",
defaultCollapsed = false,
onCollapsedChange,
children,
className,
}: SidebarCollapsibleProps) {
const [collapsed, setCollapsed] = useState(defaultCollapsed);
const toggle = () => {
setCollapsed(!collapsed);
onCollapsedChange?.(!collapsed);
};
const label = cn("truncate", collapsed ? "sr-only" : "max-md:sr-only");
return (
<div className={cn("flex min-h-dvh bg-da-surface-2 text-da-fg", className)}>
<aside
aria-label="Sidebar"
className={cn(
"sticky top-0 flex h-dvh shrink-0 flex-col border-r border-da-border bg-da-surface text-da-surface-fg transition-[width] duration-(--da-duration-slow) ease-da motion-reduce:transition-none",
collapsed ? "w-16" : "w-16 md:w-60",
)}
>
<div className="flex h-14 items-center gap-2 overflow-hidden border-b border-da-border px-[18px] font-da-display font-extrabold">
<LedgerlyMark />
<span className={label}>{brand}</span>
</div>
<nav aria-label="Main" className="flex-1 overflow-x-hidden overflow-y-auto p-3">
<ul className="space-y-1">
{items.map((it) => {
const on = it.href === activeHref;
return (
<li key={it.href}>
<a
href={it.href}
title={it.label}
aria-current={on ? "page" : undefined}
className={cn(
"da-focus da-transition flex h-10 items-center gap-3 rounded-da-md px-3 text-sm font-medium",
on ? "bg-da-primary text-da-primary-fg" : "text-da-muted-fg hover:bg-da-surface-2 hover:text-da-fg",
)}
>
<it.icon aria-hidden className="size-4 shrink-0" />
<span className={label}>{it.label}</span>
</a>
</li>
);
})}
</ul>
</nav>
<div className="space-y-1 border-t border-da-border p-3">
<a
href="#settings"
title="Settings"
className="da-focus flex h-10 items-center gap-3 rounded-da-md px-3 text-sm font-medium text-da-muted-fg hover:bg-da-surface-2 hover:text-da-fg"
>
<Settings aria-hidden className="size-4 shrink-0" />
<span className={label}>Settings</span>
</a>
<button
type="button"
onClick={toggle}
aria-expanded={!collapsed}
aria-label={collapsed ? "Expand sidebar" : "Collapse sidebar"}
className="da-focus hidden h-10 w-full items-center gap-3 rounded-da-md px-3 text-sm font-medium text-da-muted-fg hover:bg-da-surface-2 hover:text-da-fg md:flex"
>
{collapsed ? <PanelLeftOpen aria-hidden className="size-4 shrink-0" /> : <PanelLeftClose aria-hidden className="size-4 shrink-0" />}
<span aria-hidden className={label}>
Collapse
</span>
</button>
</div>
</aside>
<main className="min-w-0 flex-1">{children}</main>
</div>
);
}
export default SidebarCollapsible;
modules/neo-corporate/layout/sidebar-collapsible/index.tsx
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| brand | string | — | Brand name. |
| items | { label: string; href: string; icon: LucideIcon }[] | — | Content items (see the exported types). |
| activeHref | string | — | Active Href. |
| defaultCollapsed | boolean | — | Default Collapsed. |
| onCollapsedChange | (collapsed: boolean) => void | — | Callback. |
| children | ReactNode | — | Content. |
| className | string | — | Extra classes on the root. |
Other sidebar variants in Neo Corporate
App Sidebar
Classic enterprise app shell: white bordered sidebar with brand, entity switcher, grouped nav (uppercase group labels, count badges, blue active state), settings and user footer; off-canvas drawer on mobile.
Sidebar Dual
Two-level navigation: a narrow icon rail of top-level sections (tooltips, aria-pressed) and a secondary panel listing the selected section’s pages; the panel hides below md.
Sidebar Navy
Dark navy sidebar (fg-colored in light mode, surface in dark mode — dark in both): brand, search field, nav with white-tint active state and badges, and a cash-balance card at the bottom; drawer on mobile.
Sidebar in other art directions
App Sidebar
App shell with a sticky left sidebar: workspace header, grouped nav with yellow active state and counters, usage meter, user footer. Becomes a top bar + slide-over drawer below lg.
Icon Rail
Compact 80px icon rail: ink brand square, square icon links with yellow active state, red counters and ink tooltips on hover/focus, settings pinned at the bottom. Server-safe, CSS-only tooltips.
Sidebar Collapsible
App sidebar that collapses from 256px to a 76px icon column with a stepped width animation; ink active item, labels become sr-only when collapsed, collapse toggle pinned at the bottom.
Sidebar Dual
Two-level navigation: an ink icon rail switches sections, and a paper second panel lists that section's links with counts and a yellow active link. Second panel hides on very small screens.
App Sidebar
Floating frosted sidebar inset from the viewport edges: gradient-orb brand, pill nav items with an ink active state and gradient badges, workspace group, gradient promo card and user chip. Becomes a floating pill top bar + glass drawer below lg.
Sidebar Adaptive
Adaptive navigation: a full glass sidebar with brand, gradient action button, labelled links and a usage meter on desktop; a floating bottom tab bar with a raised gradient “+” on mobile.
Sidebar Channels
Chat-app style double sidebar: a column of gradient workspace squircles plus a glass channel panel with collapsible sections, private/live icons, unread counts and LIVE meeting badges; drawer on mobile.
Sidebar Floating
Floating glass icon rail detached from the viewport edges: gradient logo orb, gradient active item, unread badges, glass tooltips on hover/focus, settings and avatar at the bottom; a floating bottom bar on mobile.
App Sidebar
Linear-style app shell: 240px translucent sidebar with workspace switcher, search, "New" button with shortcut, grouped 32px nav rows with counters, team list with color squares and settings at the bottom. Slide-in drawer below lg.
Sidebar Inset
Inset app shell: the sidebar sits on the tinted page background while content lives in a raised rounded panel. Workspace header, collapsible nested sections with guide line, support/settings and user card; drawer on mobile.
Sidebar Rail
Sidebar that collapses between a 224px labelled list and a 56px icon rail (⌘B or bottom button) with tooltips on hover/focus and corner badges; icon-only on mobile. Controlled or uncontrolled.
Sidebar Tree
Docs / settings sidebar with a filter box and a nested collapsible tree auto-expanded to the active page (highlighted in indigo); stacks above content on mobile.
App Sidebar
Typographic app sidebar: hairline right border, logo + site switcher, mono group labels, plain text links with right-aligned mono counts; the active link is ink with a signal dot; drawer on mobile.
Sidebar Icons
Narrow icon rail (56px) of square hairline cells; the active cell is filled ink; labels appear as mono tooltips to the right on hover/focus; turns into a bottom bar on mobile.
Sidebar Ink
Ink sidebar (stays near-black in dark mode): giant numbered text links “01 Overview”, the active one full white with an arrow, a storage meter as a hairline bar at the bottom; drawer on mobile.
Sidebar Tree
CMS page tree: nested pages drawn with hairline guide lines and +/− toggles (aria-expanded), mono “Pages” header with an add button; active page ink with signal dot; drawer on mobile.
App Sidebar
Calm app shell: cream sidebar with leaf logo, pill nav items (sage pill for the active page), a small “this year” footprint card with a sprout, and settings at the bottom; drawer on mobile.
Sidebar Floating
Floating sidebar: a rounded cream panel inset from the page edges with a soft shadow, italic serif group labels, a sage “New entry” pill and sage-tinted active items; drawer on mobile.
Sidebar Garden
Deep sage sidebar (dark sage tint in dark mode) with cream text, a translucent active pill, count bubbles, rolling hills illustrated along the bottom and the user’s name on the hill; drawer on mobile.
Sidebar Rail
Slim round icon rail: circular icon links on a sand column, the active one a sage circle with a leaf-shaped marker; labels appear as soft tooltips on hover/focus; becomes a bottom tab bar on mobile.
App Sidebar
Friendly app shell: warm sidebar with emoji workspace chip, big periwinkle “New task” pill, search field, nav with peach count bubbles and a colored-dot board list; slide-in drawer on mobile.
Sidebar Boards
Board navigator: starred favorites, then collapsible emoji spaces holding emoji boards with count bubbles; active board in a white pill; dashed “New space” button.
Sidebar Card
Floating white sidebar card with generous radius on a tinted page: two-dot logo, nav with lavender active pill, butter “tip of the day” card and user row; horizontal nav on mobile.
Sidebar Rail
Slim rounded icon rail: round icons whose active item fills with its own pastel color, dark pill tooltips, emoji avatar at the bottom; floating bottom bar on mobile.