--- name: frontend-design description: Overmind Console design system — semantic tokens, shared primitives, geometry and icons, the border-contrast floor, the duplicated table implementations, and the verification scripts. Use when writing or changing frontend UI, styles, tables, or component markup. --- # Frontend design conventions The Console is "a quiet workshop for agent improvement" — warm minimalism, copper/gold brand, NeueBit (pixel, display + labels) and PP Neue Montreal (body), small radii, no shadows. Keep it; don't drift toward default Tailwind. The token spec is the root `DESIGN.md` — extend its values, don't restructure it. Tokens are declared in `frontend/src/styles.css` under `@theme inline`. ## Color — semantic tokens only - **Never** raw Tailwind palette utilities (`bg-green-500`, `text-blue-600`, `border-amber-400`) or hex in class strings (`bg-[#1c1917]`). - **Status** → the four semantic tokens as a tint triplet. Tokens flip per theme, so **no `dark:` variants**: `border-success/40 bg-success/10 text-success`. Meanings: `success` = passed/healthy/connected/completed; `warning` = review/degraded/stale; `info` = running/pending/queued/training; `destructive` = failed/error/cancelled. Prefer the components — `` or `` (`lib/job-status.tsx`, `lib/colors.ts`). - **Categorical** — a fixed set of entity types, not status → `text-cat-1 … text-cat-6` / `bg-cat-1/10`. Assign a stable slot per type. - **Login / onboarding**, the always-dark cinematic surfaces → the named `--auth-*` tokens (`bg-auth-panel`, `border-auth-border`, `text-auth-text-dim`, `text-auth-accent`, …), never inline hex. - Neutrals → `bg-background/card/muted`, `text-foreground/muted-foreground`, `border-border`. Titles use `text-foreground`, never `text-black dark:text-white`. ## Border contrast — hard constraint `--border` at full strength is only ~1.3:1 against a card; the visibility floor in `scripts/check-contrast.mjs` is 1.2. The sanctioned ramp: - outline = bare `border-border` - divider = `border-border/70` - faint = `border-border/60` Anything below `/60` is invisible. Never lower a border opacity without running `bun run check:contrast -- --all` and reading the border rows. ## Shared primitives — use them, don't hand-roll - A page title is ``. Never a raw `

`. - A page scaffold is ``. - Loading is `` inline (buttons) or `` for a region; `Skeleton` when the layout is known on first paint. Never a raw `animate-spin` or a "Loading…" text block. - An empty state is ``. - A titled card is ``; plain `` otherwise. Don't hand-roll `rounded-lg border` surfaces. - A destructive confirm is ``. Never `window.confirm`. Dialog Cancel is always `variant="secondary"` (what `AlertDialogCancel` renders). - Feedback: `sonner` toast = transient async result; `Alert` = persistent inline notice; `DismissibleAlert` = in-dialog mutation error. - Model choices use `ModelOptionLabel`: provider logo, model name, then separate metadata. Use it in both dropdown rows and selected values, including judge, benchmark, comparison and filter selectors. Keep fit/cost/size annotations and selection rules; model IDs remain the stored values. `ModelProviderChip` is for read-only badges, not dropdown rows. ## Geometry, type, icons - **No box-shadows** — every `--shadow-*` is `none`, so `shadow-*` utilities are dead no-ops. Depth = surface layering + 1px borders. - Radii: `rounded-sm` for interactive primitives, `rounded-md` for containers. `rounded-lg/xl/2xl/3xl` are off-system. - Headings use `font-display` (NeueBit, unweighted). `font-mono` is the loaded system stack — Fira Code is not shipped, don't reference it. - Icons: **always the central registry** — `import { Icon } from "@/components/ui/icons"` → ``. The glyphs are pixelarticons paths vendored into `ui/icons/glyphs.ts` (24×24 grid, 2px inset, `currentColor`); never import that module or `lucide-react` in app code; both fail `check:design`. One concept = one glyph (`Icon.delete` = trash, `Icon.close`/`Icon.failed` = the "X"). Missing a glyph? Add `name: glyph("")` to `ui/icons/index.ts`, the only file that may read the table, then `bun run icons:vendor` with `PIXELARTICONS_LICENSE_KEY` set (or `--source ` of an unlocked package) — typecheck fails until the table is regenerated. Removing one is the reverse. There is no icon package to install. Sizes: `size-4` in buttons, `size-[17px]` in nav, `size-3` for dense chips. ## Two duplicated table implementations A table fix applied to one codepath silently misses the other: 1. `src/components/ui/data-table.tsx` — the shared TanStack `` (Traces, Sessions, Runs, Datasets, Experiments, Billing) 1. `src/components/datasets/notebook/rows-grid.tsx` — the paged rows grid every dataset table (source, cell output, product) renders through Sort headers are in `components/ui/sortable-header.tsx` for hand-built tables and inline inside `data-table.tsx` for the shared one — check both before declaring a header fix done. Eval results align model variants by the generated client's `rowIndex`, including zero. Trace-filter samples use `sourceTraceId`; samples with neither keep their own identity. Use `components/evaluations/sample-index.ts` for table and detail lookups so unrelated outputs never collapse or stand in for another model's missing response. The eval datapoint popup selects one model at a time and shows its input beside its generated output, grading reference, and evaluator reasoning. `SampleInput` consumes the generated `EvalSampleIO` contract: captured initial runner requests include system prompts and tools; historical dataset inputs explicitly say the exact request was not captured. Never label a combined trajectory as input or an extracted statistical prediction as the model's full output. Trajectory model-call cards use `ModelProviderChip` as their title and a three-line system-prompt preview that opens in a dialog. Mode-specific model/prompt context requires an exact step-anchor match; unbound or shared calls show only context common to their candidates. Never borrow a conflicting worker prompt. Excerpts stay labelled as excerpts; missing capture has no expand control. ## App shell The left sidebar logic in `src/routes/_auth.tsx` and `src/components/ui/sidebar.tsx` is owned separately — pages adapt to the native in-flow sidebar. Don't add routes to focus-mode patterns or modify shell logic without explicit approval. ## Tooling - Package manager is **Bun** (`bun add`, `bunx shadcn@latest add …`) — never pnpm/npm/yarn. New shadcn components get adapted to the token system after generation. - **Biome must not format assets.** `.svg` is excluded in `biome.json` — keep it excluded and never run `biome … --write` over `**/*.svg` (2.5.x sorts attributes and corrupted a logo's XML prolog). - Verify every UI change, in both light and dark: ```bash cd frontend bun run typecheck && bun run lint bun run check:all # check:design + check:contrast + check:controls ```