Skip to content
LuoForge/Tungsten
Components/AppShell·Experimental

AppShell

since v12.3.0

AppShell — dashboard frame: persistent sidebar (lg+) or Drawer-collapsed (< lg),

topbar, and main content region. Active-nav styling belongs in the consumer's

sidebar — the active indicator is the documented 2px bg-brand left-edge stripe

(DESIGN.md Brand Voltage Rule: the sidebar's one brand moment), label stays ink.

Install

Add the package and import the component.

pnpm add @hey-mike/tungsten
pnpm add @hey-mike/tungsten
import { AppShell } from '@hey-mike/tungsten';
import { AppShell } from '@hey-mike/tungsten';

Preview

Same fixtures used by the visual-regression suite.

Usage

apps/docs/app/snapshots/app-shell/page.tsx

'use client';

import { AppShell } from '@hey-mike/tungsten';
import { HeroSpecimen } from '../_components/HeroSpecimen';

export default function AppShellSnapshot() {
  return (
    <div
      data-testid="snapshot-root"
      className="h-screen w-[1024px]"
    >
      <AppShell
        sidebar={
          <nav aria-label="Primary navigation" className="flex flex-col gap-1 p-4">
            <a href="/dashboard" className="text-ink-1 bg-surface-hover rounded-md px-3 py-2 text-sm font-medium">
              Dashboard
            </a>
            <a href="/users" className="text-ink-2 hover:bg-surface-hover rounded-md px-3 py-2 text-sm">
              Users
            </a>
            <a href="/settings" className="text-ink-2 hover:bg-surface-hover rounded-md px-3 py-2 text-sm">
              Settings
            </a>
          </nav>
        }
        topbar={
          <div className="text-ink-2 flex items-center gap-2 text-sm">
            <span>Dashboard</span>
          </div>
        }
      >
        <h2 className="text-ink-1 text-lg font-semibold">Overview</h2>
        <p className="text-ink-3 mt-1 text-sm">Welcome to your dashboard.</p>
      </AppShell>

      {/* Gallery thumbnail (`?hero`): the real AppShell is full-screen (1024px) and
          collapses to blank at thumbnail scale, so show a compact static diagram of
          the shell anatomy — sidebar rail + topbar + content. Hidden in the detail
          view; revealed (and the real AppShell hidden) by the hero CSS. */}
      <div data-hero-specimen className="hidden">
        <HeroSpecimen>
          <div className="border-stroke bg-surface flex h-[120px] w-[208px] overflow-hidden rounded-lg border">
            {/* sidebar rail */}
            <div className="bg-surface-2 border-stroke flex w-12 flex-col gap-1.5 border-r p-2">
              <div className="bg-brand-overlay-15 h-2 rounded-sm" />
              <div className="bg-ink-3/25 h-2 rounded-sm" />
              <div className="bg-ink-3/25 h-2 rounded-sm" />
            </div>
            {/* main column */}
            <div className="flex flex-1 flex-col">
              <div className="border-stroke flex h-6 items-center border-b px-2">
                <div className="bg-ink-3/30 h-1.5 w-10 rounded-sm" />
              </div>
              <div className="flex flex-col gap-1.5 p-2.5">
                <div className="bg-ink-3/25 h-2 w-2/3 rounded-sm" />
                <div className="bg-ink-3/15 h-2 w-1/2 rounded-sm" />
                <div className="bg-ink-3/15 h-2 w-3/5 rounded-sm" />
              </div>
            </div>
          </div>
        </HeroSpecimen>
      </div>
    </div>
  );
}
'use client';

import { AppShell } from '@hey-mike/tungsten';
import { HeroSpecimen } from '../_components/HeroSpecimen';

export default function AppShellSnapshot() {
  return (
    <div
      data-testid="snapshot-root"
      className="h-screen w-[1024px]"
    >
      <AppShell
        sidebar={
          <nav aria-label="Primary navigation" className="flex flex-col gap-1 p-4">
            <a href="/dashboard" className="text-ink-1 bg-surface-hover rounded-md px-3 py-2 text-sm font-medium">
              Dashboard
            </a>
            <a href="/users" className="text-ink-2 hover:bg-surface-hover rounded-md px-3 py-2 text-sm">
              Users
            </a>
            <a href="/settings" className="text-ink-2 hover:bg-surface-hover rounded-md px-3 py-2 text-sm">
              Settings
            </a>
          </nav>
        }
        topbar={
          <div className="text-ink-2 flex items-center gap-2 text-sm">
            <span>Dashboard</span>
          </div>
        }
      >
        <h2 className="text-ink-1 text-lg font-semibold">Overview</h2>
        <p className="text-ink-3 mt-1 text-sm">Welcome to your dashboard.</p>
      </AppShell>

      {/* Gallery thumbnail (`?hero`): the real AppShell is full-screen (1024px) and
          collapses to blank at thumbnail scale, so show a compact static diagram of
          the shell anatomy — sidebar rail + topbar + content. Hidden in the detail
          view; revealed (and the real AppShell hidden) by the hero CSS. */}
      <div data-hero-specimen className="hidden">
        <HeroSpecimen>
          <div className="border-stroke bg-surface flex h-[120px] w-[208px] overflow-hidden rounded-lg border">
            {/* sidebar rail */}
            <div className="bg-surface-2 border-stroke flex w-12 flex-col gap-1.5 border-r p-2">
              <div className="bg-brand-overlay-15 h-2 rounded-sm" />
              <div className="bg-ink-3/25 h-2 rounded-sm" />
              <div className="bg-ink-3/25 h-2 rounded-sm" />
            </div>
            {/* main column */}
            <div className="flex flex-1 flex-col">
              <div className="border-stroke flex h-6 items-center border-b px-2">
                <div className="bg-ink-3/30 h-1.5 w-10 rounded-sm" />
              </div>
              <div className="flex flex-col gap-1.5 p-2.5">
                <div className="bg-ink-3/25 h-2 w-2/3 rounded-sm" />
                <div className="bg-ink-3/15 h-2 w-1/2 rounded-sm" />
                <div className="bg-ink-3/15 h-2 w-3/5 rounded-sm" />
              </div>
            </div>
          </div>
        </HeroSpecimen>
      </div>
    </div>
  );
}

Props

Surface specific to <AppShell />.

PropTypeDefaultDescription
sidebar*ReactNodePrimary navigation. Rendered fixed on wide (lg+), inside Drawer on narrow.
topbarReactNodeTop bar content (breadcrumb, search, account).
classNamestring

Source