Skip to content

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.

neo-corporate/layout/sidebar-navy
Open ↗

Source

"use client";

import { useEffect, useId, useState, type ReactNode } from "react";
import { ArrowLeftRight, BarChart3, Bell, FileText, Home, Landmark, Menu, Receipt, Search, Wallet, X, type LucideIcon } from "lucide-react";
import { cn } from "@/lib/utils";

export interface SidebarNavyProps {
  brand?: string;
  items?: { label: string; href: string; icon: LucideIcon; badge?: string }[];
  activeHref?: string;
  balance?: { label: string; value: string };
  children?: ReactNode;
  className?: string;
}

const DEFAULT_ITEMS = [
  { label: "Home", href: "#home", icon: Home },
  { label: "Invoices", href: "#invoices", icon: FileText },
  { label: "Bills", href: "#bills", icon: Receipt, badge: "6" },
  { label: "Transactions", href: "#tx", icon: ArrowLeftRight },
  { label: "Accounts", href: "#accounts", icon: Landmark },
  { label: "Cards", href: "#cards", icon: Wallet },
  { label: "Insights", href: "#insights", icon: BarChart3, badge: "New" },
];

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>
  );
}

/** 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. */
export function SidebarNavy({
  brand = "Ledgerly",
  items = DEFAULT_ITEMS,
  activeHref = "#home",
  balance = { label: "Total cash", value: "$2,418,902" },
  children,
  className,
}: SidebarNavyProps) {
  const [open, setOpen] = useState(false);
  const id = useId();
  useEffect(() => {
    if (!open) return;
    const onKey = (e: KeyboardEvent) => e.key === "Escape" && setOpen(false);
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [open]);
  return (
    <div className={cn("flex min-h-dvh bg-da-bg text-da-fg", className)}>
      {open && <div aria-hidden className="fixed inset-0 z-40 bg-da-overlay lg:hidden" onClick={() => setOpen(false)} />}
      <aside
        id={id}
        aria-label="Sidebar"
        className={cn(
          "fixed inset-y-0 left-0 z-50 flex w-64 flex-col bg-(--nav) p-3 text-(--ink) [--ink:var(--da-bg)] [--nav:var(--da-fg)] dark:border-r dark:border-da-border dark:[--ink:var(--da-surface-fg)] dark:[--nav:var(--da-surface)] transition-transform duration-(--da-duration-slow) ease-da lg:sticky lg:top-0 lg:h-dvh lg:translate-x-0 motion-reduce:transition-none",
          open ? "translate-x-0" : "-translate-x-full",
        )}
      >
        <div className="flex h-11 items-center gap-2 px-2 font-da-display font-extrabold">
          <LedgerlyMark />
          {brand}
          <button
            type="button"
            aria-label="Close menu"
            onClick={() => setOpen(false)}
            className="da-focus ml-auto grid size-8 place-items-center rounded-da-md hover:bg-(--ink)/10 lg:hidden"
          >
            <X aria-hidden className="size-4" />
          </button>
        </div>
        <label className="mt-3 flex h-9 items-center gap-2 rounded-da-md bg-(--ink)/10 px-2.5 text-sm focus-within:ring-2 focus-within:ring-(--ink)/40">
          <Search aria-hidden className="size-4 opacity-70" />
          <span className="sr-only">Search</span>
          <input placeholder="Search" className="min-w-0 flex-1 bg-transparent outline-none placeholder:text-current placeholder:opacity-70" />
          <kbd className="font-da-mono text-[11px] opacity-70">⌘K</kbd>
        </label>
        <nav aria-label="Main" className="mt-4 flex-1 overflow-y-auto">
          <ul className="space-y-0.5">
            {items.map((it) => {
              const on = it.href === activeHref;
              return (
                <li key={it.href}>
                  <a
                    href={it.href}
                    aria-current={on ? "page" : undefined}
                    className={cn(
                      "da-focus da-transition flex h-9 items-center gap-2.5 rounded-da-md px-2.5 text-sm font-medium",
                      on ? "bg-(--ink)/15" : "opacity-80 hover:bg-(--ink)/10 hover:opacity-100",
                    )}
                  >
                    <it.icon aria-hidden className="size-4" />
                    <span className="flex-1">{it.label}</span>
                    {it.badge && <span className="rounded-da-sm bg-da-primary px-1.5 text-[11px] font-semibold text-da-primary-fg">{it.badge}</span>}
                  </a>
                </li>
              );
            })}
          </ul>
        </nav>
        <div className="rounded-da-lg bg-(--ink)/10 p-4">
          <p className="text-xs opacity-75">{balance.label}</p>
          <p className="mt-1 font-da-mono text-xl">{balance.value}</p>
          <p className="mt-1 flex items-center gap-1.5 text-xs opacity-75">
            <Bell aria-hidden className="size-3" /> 3 accounts connected
          </p>
        </div>
      </aside>
      <div className="flex min-w-0 flex-1 flex-col">
        <div className="flex h-14 items-center gap-2 border-b border-da-border bg-da-surface px-4 lg:hidden">
          <button
            type="button"
            aria-label="Open menu"
            aria-expanded={open}
            aria-controls={id}
            onClick={() => setOpen(true)}
            className="da-focus grid size-9 place-items-center rounded-da-md hover:bg-da-muted"
          >
            <Menu aria-hidden className="size-5" />
          </button>
          <span className="font-da-display font-extrabold">{brand}</span>
        </div>
        <main className="flex-1">{children}</main>
      </div>
    </div>
  );
}

export default SidebarNavy;

modules/neo-corporate/layout/sidebar-navy/index.tsx

Props

PropTypeDefaultDescription
brandstring—Brand name.
items{ label: string; href: string; icon: LucideIcon; badge?: string }[]—Content items (see the exported types).
activeHrefstring—Active Href.
balance{ label: string; value: string }—Balance.
childrenReactNode—Content.
classNamestring—Extra classes on the root.

Other sidebar variants in Neo Corporate

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.

BrutalistSidebar

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.

BrutalistSidebar

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.

BrutalistSidebar

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.

BrutalistSidebar

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.

GlassSidebar

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.

GlassSidebar

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.

GlassSidebar

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.

GlassSidebar

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.

MinimalSidebar

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.

MinimalSidebar

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.

MinimalSidebar

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.

MinimalSidebar

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.

Mono CleanSidebar

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.

Mono CleanSidebar

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.

Mono CleanSidebar

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.

Mono CleanSidebar

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.

Organic SoftSidebar

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.

Organic SoftSidebar

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.

Organic SoftSidebar

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.

Organic SoftSidebar

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.

Soft FlatSidebar

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.

Soft FlatSidebar

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.

Soft FlatSidebar

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.

Soft FlatSidebar