6.1 KiB
Topbar Implementation
Install, theme-hook wiring, and composition for @aura/topbar. Run the interview (INTERVIEW.md) first.
Step 3 — Install
Installation is mandatory. If @aura/topbar cannot be installed, stop and surface the blocker. Do not build a custom component or any workaround.
3a — Check if already installed
Check package.json for @aura/topbar. If present, skip to Step 3d.
3b — Determine install method
@aura/topbar is a shadcn registry component — not on npm. The only valid install path is the shadcn CLI (pnpm dlx shadcn@latest add). Do not use npm install, pnpm add, or yarn add.
Before running the install:
-
Ensure
components.jsonhas the@auraregistry. If absent or missing the entry, add:{ "registries": { "@aura": "https://cognitedata.github.io/aura/r/{name}.json" } }If
components.jsondoes not exist at all, runpnpm dlx shadcn@latest initfirst, then add the entry. -
Detect the package manager:
pnpm-lock.yaml→ pnpmyarn.lock→ yarnpackage-lock.json→ npm
3c — Install
pnpm dlx shadcn@latest add @aura/topbar
If this fails, stop. Tell the user exactly what failed and ask them to resolve the blocker. Do not proceed with a workaround.
3d — Tailwind check
Confirm tailwind.config has darkMode: 'class'. Add it if missing.
Step 4 — Dark mode hook
Always implement theme switching (light / dark). Check for an existing theme system first:
- Search for
useDarkMode,useTheme,useColorScheme, or aThemeProviderinsrc/ - If found, wire into it and skip creating a new hook.
If none exists, create src/hooks/use-theme-mode.ts (or extend your existing hook) so the Topbar menu can set light or dark explicitly:
import { useEffect, useState } from 'react';
export type ThemeMode = 'light' | 'dark';
export function useThemeMode() {
const [mode, setMode] = useState<ThemeMode>(() => {
const stored = localStorage.getItem('theme');
if (stored === 'dark' || stored === 'light') return stored;
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
});
useEffect(() => {
const isDark = mode === 'dark';
document.documentElement.classList.toggle('dark', isDark);
localStorage.setItem('theme', isDark ? 'dark' : 'light');
}, [mode]);
return {
mode,
isDark: mode === 'dark',
setTheme: (next: ThemeMode) => setMode(next),
};
}
Apply the initial class on page load in main.tsx / index.tsx:
const stored = localStorage.getItem('theme');
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
if (stored === 'dark' || (!stored && prefersDark)) {
document.documentElement.classList.add('dark');
}
The Topbar theme trigger should open a Menu whose items call setTheme('light') and setTheme('dark') and show a checkmark on the active row.
Step 5 — Implement the Topbar
Always check Storybook for exact prop names before writing code. The names below are illustrative — verify against the current @aura/topbar package.
import { Topbar } from '@aura/topbar';
import { Breadcrumb, BreadcrumbItem } from '@aura/topbar'; // adjust to actual exports
// App + user Avatar: import from the Aura package / path Storybook documents for Topbar.
import { useThemeMode } from '@/hooks/use-theme-mode';
export function AppShell({ children }: { children: React.ReactNode }) {
const { mode, setTheme } = useThemeMode();
return (
<>
<Topbar
// Left — application mark: Avatar small, fjord (verify Storybook props)
applicationIcon={
<Avatar size="small" colorway="fjord" src={appMarkSrc} alt="" />
}
breadcrumbs={
<Breadcrumb>
<BreadcrumbItem label="Application Name" href="/" />
{/* <BreadcrumbItem label={objectName} ... dropdown ... /> */}
</Breadcrumb>
}
// Inline metadata — optional string immediately after breadcrumb, left-aligned only
// breadcrumbMetadata="Updated 3 hours ago"
// Middle — optional Tabs (routes) OR Segmented control (modes); size small; omit if unused
centerSlot={
null
// Example Tabs: <Tabs size="sm" ... />
// Example Segmented: <SegmentedControl size="sm" ... />
}
// Right strip — fixed order when each is visible: share → notifications → theme → atlas → avatar
// Theme: sun when light, moon when dark; Menu with Light mode / Dark mode + checkmark on active
// Storybook may still call this darkMode or split props differently — map menu choice to setTheme('light'|'dark').
trailingSlot={null}
systemActions={{
share: { visible: true },
notifications: { visible: true },
darkMode: {
visible: true,
mode, // 'light' | 'dark' — illustrative; use whatever resolvedTheme API Aura exposes
onSelectLight: () => setTheme('light'),
onSelectDark: () => setTheme('dark'),
},
atlas: { visible: true },
avatar: { visible: true, src: userPhotoSrc, alt: userName },
}}
/>
<main>{children}</main>
</>
);
}
Layout wrapper: The parent element must allow the Topbar to be full-width and sticky:
<div className="flex min-h-screen flex-col">
<AppShell>
{/* page content — primary actions for the current screen live here */}
</AppShell>
</div>
Additional resources
- Full Topbar architecture rules: RULES.md
- Aura Topbar Storybook: https://cognitedata.github.io/aura/storybook/?path=/docs/primitives-topbar--docs
- Aura Breadcrumb Storybook: https://cognitedata.github.io/aura/storybook/?path=/docs/primitives-breadcrumb--docs
- Aura Button Storybook: https://cognitedata.github.io/aura/storybook/?path=/docs/primitives-button--docs
- Aura colors / dark mode tokens: https://cognitedata.github.io/aura/storybook/?path=/docs/foundations-colors--docs