DGit UI › Blocks

App header

Dashboard top bar: brand, pill navigation, and a slot for the account menu.

Install

npx shadcn add @dgit/app-header

First time? Add the registry to components.json: see Get started.

app-header.tsx

import type { ReactNode } from "react";
import { cn } from "@/lib/utils";

/**
 * The top bar of a dashboard: brand on the left, pill navigation in the middle (its own row on
 * phones), and whatever goes on the right (usually <AccountMenu />).
 *
 *   <AppHeader brand={<Logo />} nav={links.map(l => <NavLink className={({isActive}) => navPill(isActive)} …/>)}>
 *     <AccountMenu name={me.name} email={me.email} onSignOut={signOut} />
 *   </AppHeader>
 */
export function AppHeader({ brand, nav, children, className }: { brand: ReactNode; nav?: ReactNode; children?: ReactNode; className?: string }) {
  // Three columns from sm up, the outer two equal, so the nav sits in the middle of the page
  // however wide the brand side gets. On phones the nav takes its own row.
  return (
    <header className={cn("grid grid-cols-[1fr_auto] items-center gap-x-6 gap-y-3 px-4 py-[14px] sm:grid-cols-[1fr_auto_1fr] sm:px-6 sm:py-[18px]", className)}>
      <div className="flex min-w-0 items-center justify-self-start">{brand}</div>
      {nav ? (
        <nav className="order-last col-span-2 flex gap-1 overflow-x-auto sm:order-none sm:col-span-1" aria-label="Main">
          {nav}
        </nav>
      ) : (
        <span className="hidden sm:block" />
      )}
      <div className="flex min-w-0 items-center justify-self-end gap-2">{children}</div>
    </header>
  );
}

/** Classes for one navigation pill; the current page is filled. */
export function navPill(active: boolean): string {
  return cn(
    "rounded-full px-3.5 py-1.5 text-sm font-medium whitespace-nowrap no-underline transition-colors",
    active ? "bg-accent text-foreground" : "text-muted-foreground hover:text-foreground",
  );
}

Source on GitHub