Files
md-files/.agents/skills/use-topbar/IMPLEMENTATION.md
T
2026-05-31 20:25:41 +00:00

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:

  1. Ensure components.json has the @aura registry. If absent or missing the entry, add:

    {
      "registries": {
        "@aura": "https://cognitedata.github.io/aura/r/{name}.json"
      }
    }
    

    If components.json does not exist at all, run pnpm dlx shadcn@latest init first, then add the entry.

  2. Detect the package manager:

    • pnpm-lock.yaml → pnpm
    • yarn.lock → yarn
    • package-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 a ThemeProvider in src/
  • 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