Skip to content

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.

neo-corporate/layout/sidebar-dual
Open ↗

Source

"use client";

import { useState, type ReactNode } from "react";
import { BarChart3, CreditCard, FileText, Settings, Users, type LucideIcon } from "lucide-react";
import { cn } from "@/lib/utils";

export interface SidebarDualSection {
  id: string;
  label: string;
  icon: LucideIcon;
  links: { label: string; href: string }[];
}

export interface SidebarDualProps {
  sections?: SidebarDualSection[];
  defaultSection?: string;
  activeHref?: string;
  children?: ReactNode;
  className?: string;
}

const DEFAULT_SECTIONS: SidebarDualSection[] = [
  {
    id: "ar",
    label: "Receivables",
    icon: FileText,
    links: [
      { label: "Invoices", href: "#invoices" },
      { label: "Credit notes", href: "#credits" },
      { label: "Reminders", href: "#reminders" },
      { label: "Customer portal", href: "#portal" },
    ],
  },
  {
    id: "ap",
    label: "Payables",
    icon: CreditCard,
    links: [
      { label: "Bills", href: "#bills" },
      { label: "Approvals", href: "#approvals" },
      { label: "Payment runs", href: "#runs" },
    ],
  },
  {
    id: "crm",
    label: "Contacts",
    icon: Users,
    links: [
      { label: "Customers", href: "#customers" },
      { label: "Vendors", href: "#vendors" },
    ],
  },
  {
    id: "rep",
    label: "Reports",
    icon: BarChart3,
    links: [
      { label: "Aging", href: "#aging" },
      { label: "Cash flow", href: "#cashflow" },
      { label: "P&L", href: "#pnl" },
    ],
  },
];

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

/** 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. */
export function SidebarDual({ sections = DEFAULT_SECTIONS, defaultSection = "ar", activeHref = "#invoices", children, className }: SidebarDualProps) {
  const [section, setSection] = useState(defaultSection);
  const current = sections.find((s) => s.id === section) ?? sections[0];
  return (
    <div className={cn("flex min-h-dvh bg-da-surface-2 text-da-fg", className)}>
      <nav
        aria-label="Sections"
        className="sticky top-0 flex h-dvh w-16 shrink-0 flex-col items-center gap-2 border-r border-da-border bg-da-surface py-3 text-da-surface-fg"
      >
        <LedgerlyMark className="mb-3 size-8" />
        {sections.map((s) => (
          <button
            key={s.id}
            type="button"
            title={s.label}
            aria-label={s.label}
            aria-pressed={s.id === current?.id}
            onClick={() => setSection(s.id)}
            className={cn(
              "da-focus da-transition grid size-10 place-items-center rounded-da-md",
              s.id === current?.id ? "bg-da-primary/10 text-da-primary" : "text-da-muted-fg hover:bg-da-surface-2 hover:text-da-fg",
            )}
          >
            <s.icon aria-hidden className="size-5" />
          </button>
        ))}
        <a
          href="#settings"
          title="Settings"
          aria-label="Settings"
          className="da-focus mt-auto grid size-10 place-items-center rounded-da-md text-da-muted-fg hover:bg-da-surface-2 hover:text-da-fg"
        >
          <Settings aria-hidden className="size-5" />
        </a>
      </nav>
      {current && (
        <nav aria-label={current.label} className="sticky top-0 hidden h-dvh w-52 shrink-0 border-r border-da-border bg-da-surface-2 p-3 md:block">
          <p className="px-2.5 py-2 font-da-display text-sm font-bold">{current.label}</p>
          <ul key={current.id} className="mt-1 space-y-0.5 transition-opacity duration-(--da-duration) starting:opacity-0">
            {current.links.map((l) => (
              <li key={l.href}>
                <a
                  href={l.href}
                  aria-current={l.href === activeHref ? "page" : undefined}
                  className={cn(
                    "da-focus flex h-8 items-center rounded-da-md px-2.5 text-sm",
                    l.href === activeHref ? "bg-da-surface font-semibold text-da-fg shadow-da-sm" : "text-da-muted-fg hover:text-da-fg",
                  )}
                >
                  {l.label}
                </a>
              </li>
            ))}
          </ul>
        </nav>
      )}
      <main className="min-w-0 flex-1">{children}</main>
    </div>
  );
}

export default SidebarDual;

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

Props

PropTypeDefaultDescription
sectionsSidebarDualSection[]—Sections.
defaultSectionstring—Default Section.
activeHrefstring—Active Href.
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