Navigation
Die Drei-Zonen-Shell (patterns/app-shell.md): Sidebar als Identitäts-Anker links, Topbar als Arbeitszeile, Footer unten. Du siehst diese Page bereits in der echten App-Shell — alles hier ist live.
App-Shell: Drei Zonen
Für Sidebar-Apps (≥ 4 Hauptbereiche) läuft die Sidebar über die volle Viewport-Höhe und trägt die App-Identität; die Topbar ist eine schmale Arbeitszeile in der Content-Spalte. Zwei Grundregeln: keine Doppel-Identität — Logo und Avatar existieren genau einmal in der Shell — und gleich hohe Bänder, damit oben und unten je eine durchgehende Linie quer über die App läuft.
<div className="flex min-h-screen bg-background">
<Sidebar /> {/* full-height Identitäts-Anker */}
<div className="flex min-h-screen min-w-0 flex-1 flex-col">
<Topbar /> {/* Arbeitszeile, h-12 ─┐ */}
<main className="min-w-0 flex-1">{children}</main>
<Footer /> {/* md:h-14 ─┘ */}
</div>
</div>
// Die Bänder sind in beiden Spalten gleich hoch:
// oben h-12 Identitäts-Header · Arbeitszeile
// unten h-14 Konto-Zeile · Footer
// → zwei durchgehende Linien quer über die AppSidebar: drei Etagen
① Identitäts-Header mit Original-ZVV-Logo (Pflicht) und App-Name — exakt h-12, damit seine Unterkante mit der Arbeitszeile eine durchgehende Linie bildet. Darunter Mandanten-Chip (ohne Rolle) und ⌘K-Suche, ② scrollende Gruppen-Nav mit Uppercase-Labels, ③ Konto-Zeile aus User-Karte und Notification-Bell (Vercel-Muster) — einzeilig auf h-14, der Höhe des Footers. Links im Viewport live zu sehen; Rail-Breite und Gruppen-Zustand sind Cookie-persistent (kein Hydration-Flash).
{/* ① Header: h-12 — gleiche Höhe wie die Arbeitszeile,
damit beide Unterkanten EINE durchgehende Linie bilden */}
<div className="flex h-12 shrink-0 items-center border-b px-4">
<Link href="/">
<Image src="/zvv-logo.svg" alt="ZVV" />
<span>App-Name</span>
</Link>
</div>
{/* ①b unter der Linie: Mandanten-Chip + Suche */}
<div className="shrink-0 space-y-1.5 px-2 pt-2">
<div className="truncate px-1 text-[11px]">
<span className="font-mono font-semibold">VBZ</span>
<span className="text-muted-foreground"> · Verkehrsbetriebe Zürich</span>
</div>
<CommandTrigger />
</div>Topbar: die Arbeitszeile
Kompakt (h-12), sticky, Backdrop-Blur, drei Zonen im Grid: links Einklappen-Toggle und Breadcrumb (der Weg zur Seite), in der Mitte der Seitentitel, rechts Platz für eine seitenbezogene Primary-Action plus Theme-Toggle. Keine Identität — Logo, Avatar, Suche und Bell wohnen in der Sidebar. Der Toggle sitzt hier statt im Sidebar-Fuss: so liegt er in beiden Rail-Zuständen an derselben Stelle, und der Fuss bleibt einzeilig.
- Position:
sticky top-0 z-30(unter dem Env-Ribbon) - Höhe:
h-12— kompakt, damit Vertical für den Content bleibt - Background:
bg-card/80 backdrop-blur dark:bg-muted/30— gleiche Fläche wie Sidebar + Footer (Chrome-Rahmen) - Mobile: Sidebar ist ausgeblendet — die Topbar zeigt stattdessen Logo + App-Name
<header className="sticky top-0 z-30 grid h-12 grid-cols-[1fr_auto_1fr] items-center border-b bg-card/80 px-4 backdrop-blur dark:bg-muted/30">
<div className="flex min-w-0 items-center gap-2">
<SidebarToggle /> {/* kanonische Trigger-Position */}
<Breadcrumbs omitCurrent /> {/* Weg zur Seite: ⌂ › Bereich */}
</div>
<PageTitle /> {/* zentriert */}
<div className="flex items-center justify-end gap-2">
<PageAction /> {/* Primary-Action, optional */}
{collapsed && <NotificationBell />}
<ThemeToggle />
</div>
</header>Topbar-only: die Topbar als Brand-Träger
Die zweite Ausprägung (docs/03 §2.1), für Apps ohne Sidebar — Statuspage, Single-Section- Tools, Marketing-Showcases. Ohne Sidebar gibt es keinen anderen Ort für Identität und Person, also trägt die Topbar beides selbst: Logo und App-Name links, h-16 statt h-12. Ab vier Hauptbereichen ist stattdessen die Drei-Zonen-Shell richtig.
- Logo:
43 × 20ausLOGO_CHROME— dasselbe Mass wie im Sidebar-Kopf. Die Identität schrumpft nicht, wenn sie die Zone wechselt. - App-Name: Text ohne Zweitfarbe, erbt
text-foreground. Kein «ZVV» im Namen — das Logo daneben nennt den Absender bereits. - Untergrenze: 20 px Logo + Schutzraum = 30 px. Unter
h-10trägt ein Band die Identität nicht mehr.
{/* h-16 statt h-12: ohne Sidebar trägt die Topbar
Identität UND Person selbst (docs/03 §2.1) */}
<header className="sticky top-0 z-40 flex h-16 items-center gap-3 border-b bg-card/80 px-4 backdrop-blur-xl sm:px-6 dark:bg-muted/30">
<Link href="/" className="flex shrink-0 items-center gap-2">
<Image src="/zvv-logo.svg" width={LOGO_CHROME.breite} height={LOGO_CHROME.hoehe} />
{/* App-Name als Text, ohne Zweitfarbe — er erbt text-foreground */}
<span className="text-sm font-semibold tracking-tight">{appName}</span>
</Link>
<Breadcrumbs /> {/* hidden auf Mobile */}
<div className="flex-1" /> {/* Spacer */}
<PrimaryNav items={nav} /> {/* max 4–6 Pills */}
<span className="h-6 w-px bg-border" />
<UserSwitcher variant="topbar" />
<ThemeToggle />
</header>Breadcrumbs
Pfad-basiert, automatisch generiert. Erstes Element ist das Home-Icon (der App-Name steht schon im Sidebar-Header). Gezeigt wird der Weg zur Seite — die Seite selbst steht als Titel in der Mitte, nicht doppelt. Weil das Showcase überwiegend einstufig ist, tritt die Nav-Gruppe als Eltern-Ebene ein; ohne sie bliebe hier nur das Home-Icon stehen. Navigiere durch die Sektionen, um es live zu sehen.
/tokens → Breadcrumb ⌂ › Foundation · Titel Color-Tokens/forms/simple → Breadcrumb ⌂ › Daten › Formulare · Titel EinfachKonto-Zeile / UserSwitcher
Die Konto-Zeile dockt unten in der Sidebar an: User-Karte (Avatar + Name + Rolle, Dropdown öffnet nach oben) plus Notification-Bell daneben. Im Atlas-Showcase ist die Karte ein Mock-Persona-Switcher (Live-Demo für RBAC) — in Atlas-Apps ein User-Menu mit Konto + Logout. Klick unten links auf die Karte; wechsle die Persona, um den Mandanten-Chip oben umspringen zu sehen.
Theme-Toggle
4-State Cycle: System → Light → Dark → Auto. Auto folgt einem festen Clock-Fenster 08:00–18:00 Zürich-Zeit (lib/zurich-time.ts, DST-korrekt via Intl). Klick rechts oben (Icon ändert sich pro State).
Footer
Konsistent über alle Atlas-Apps. Links: Copyright. Mitte: Atlas-Spec + Repo + Changelog. Rechts: Version + Commit + Stack-Hinweis.
{/* md:h-14 = Höhe der Konto-Zeile → durchgehende Linie unten */}
<footer className="border-t bg-card md:h-14 dark:bg-muted/30">
<div className="flex h-full max-w-7xl gap-3 px-4 py-6 text-[11px] text-muted-foreground md:py-0">
<span>© {year} ZVV · App-Name</span>
<Link href={ATLAS_DOCS}>Spec</Link>
<Link href={REPO}>Repo</Link>
<span className="font-mono">v{version} · {commit}</span>
</div>
</footer>