nexus/docs/reviews/2026-04-11-nexus-phase-7-visual-qa.md
2026-04-11 16:40:59 +00:00

14 KiB
Raw Blame History

Phase 7 Visual QA — DESIGN.md compliance audit

Date: 2026-04-11 Branch: nexus/design-system-migration Auditor: Phase 7 subagent

This is the closing audit for the visual repaint (phases 17) of the Nexus design system migration. The audit reads each route / primary component against DESIGN.md §1§7, scores it, and records every deviation. Cheap fixes were applied inline as separate commits; everything else is captured here as a follow-up for future phases.

Scoring summary

Rubric category Score Notes
Color PASS Pure-black canvas everywhere. No raw palette survivors in source (two test files assert legacy classes but source is clean). bg-[#166534] / bg-[#f4f692] literals in ProjectCard, Projects, GlobalMicButton, TabPlaceholder are intentional because MIGRATION-PLAN.md §3 hasn't shipped the forest / volt-pale token classes yet.
Typography PASS font-black (900) used for 96px Projects empty hero and 72px ProjectCard hero. Uppercase section labels at 14px / tracking-[0.1em]tracking-[0.14em]. Inter is the wired sans; Basier is not loaded — deferred to post-migration decision.
Geometry PASS rounded-(xl|2xl|3xl) grep returns zero hits in ui/src. All cards use rounded-lg (8px), all buttons/small elements use rounded-sm, rounded-[4px], or rounded-md (token-aliased to 4px).
Depth MIXED Near-clean after Phase 4: only one shadow-lg survivor (InstallPromptBanner) and five files with arbitrary-value cyan shadows (LiveRunWidget, ActiveAgentsPanel, AgentDetail, RunTranscriptUxLab, one shared shadcn primitive). Inset-pressed is correctly applied in tabs + AgentDetail mode toggle. Phase 7 drops the cyan survivors and the InstallPromptBanner shadow.
Interaction PASS Hover → volt, active → var(--volt-pale), focus-ring → primary. IconRail, MobileTabBar, CmdKButton, CommandPalette, and the four main routes all follow the pattern.

Findings (grouped by severity)

Blockers

(things that must be fixed before ship)

None. The surface is broadly shippable against DESIGN.md.

Deviations (deliberate or flagged)

  1. Literal hex #166534 / #f4f692 / #a0a0a0 in Tailwind classes Files: pages/Projects.tsx:228,253, components/projects/ProjectCard.tsx:52,71, components/frame/GlobalMicButton.tsx:56,75, components/projects/tabs/TabPlaceholder.tsx:36. These are intentional per MIGRATION-PLAN.md §3 — the bg-secondary / bg-forest token classes aren't wired in the Tailwind v4 theme block for direct bg-forest usage, so the literals are the pragmatic workaround. Each file carries a code comment explaining the reason.

  2. Basier font is not loaded. DESIGN.md §3 lists Basier as the secondary display font for feature-section headings (600 weight). MIGRATION-PLAN.md §10 still flags this as an open licensing decision. Inter is used as the sole fallback. This is a documented gap, not a bug. Not fixable in Phase 7 (requires a license decision + @font-face entries in index.css).

  3. Chart-role palette (--chart-role-1..5) ui/src/index.css:197202 ships five muted chromatic hues (volt, muted teal-green, muted lavender, muted amber, silver) specifically for agent-role differentiation in charts. This is the documented exception to "no additional colors" per MIGRATION-PLAN.md §6 and is correct.

  4. ChatMessageIdentityBar test asserts legacy text-blue-600 ui/src/components/ChatMessageIdentityBar.test.tsx:57-58 still asserts the pre-migration blue class. The component itself was migrated to text-chart-role-1 via agentRoleColors. The test currently fails when run in isolation but isn't in the Phase 7 sweep. Recommend fixing as a follow-up during the test-stabilization pass.

Follow-ups

(tracked but not fixed in Phase 7)

F1. select.tsx shadow-xs survives — the Phase 4 follow-up note on select.tsx:38 was to drop shadow-xs from the trigger. Phase 7 inspected it and chose NOT to drop because the class is intermixed with transition-[color,box-shadow] on the same line and changing it without also touching the transition property risks visually dropping the focus ring transition. Non-blocking; defer to a dedicated Phase 7.5 shadcn sweep if we do one.

F2. input.tsx, textarea.tsx, toggle.tsx, checkbox.tsx, button.tsx (outline variant) all use shadow-xs. Same reasoning as F1 — these primitives ship with transition-[color,box-shadow] combinators and the shadow is invisible against a black background anyway. Deferred.

F3. RunTranscriptUxLab non-cyan arbitrary shadows. Lines 63, 152, 203 use shadow-[0_24px_60px_rgba(15,23,42,...)] (slate). Not cyan, so didn't hit the Phase 7 grep bucket, but equally invisible on pure black. And line 163 is shadow-[0_0_0_6px_rgba(34,211,238,0.12)] (cyan variant) which should also drop. Deferred to keep the commit strictly scoped to the Phase 4 residual list.

F4. BudgetPolicyCard.tsx:192 uses shadow-[0_20px_80px_-40px_rgba(0,0,0,0.55)]. Pure-black shadow on pure-black canvas is definitionally invisible. Flagged for removal. Deferred because the file wasn't on the Phase 4 residual list.

F5. CompanyPatternIcon.tsx:206 uses drop-shadow-[0_1px_2px_rgba(0,0,0,0.65)]. Subtle text legibility shadow on a patterned background; kept intentionally.

F6. BudgetSidebarMarker.tsx:8 and SidebarNavItem.tsx:53 use shadow-[0_0_0_Npx_...] for halo effects around notification dots. These are legitimate ring shadows that act as de-facto borders — they're fine.

F7. AgentDetail.tsx (4104 lines) skimmed only. Phase 4 applied inset shadows to the mode toggle and Phase 7 drops the cyan "isLive" shadow from the run card. Full pass deferred.

F8. IssueDetail.tsx (1696 lines) skimmed only. No obvious violations in the top-of-file layout.

F9. Dead fallback /dashboard in useCompanyPageMemory.test.ts fixtures. The test file stores /dashboard as the expected remembered path. After this phase's rewrite (see "Fixes applied"), company-page-memory.ts falls back to /assistant, which means this test file is now stale. Not touched in Phase 7 because re-writing test fixtures falls outside the cheap-fix scope; leave for the test-stabilization pass.

F10. useInboxBadge.ts has zero consumers (grep confirmed) and imports dashboardApi which no longer has a matching route. Phase 7 deletes the file as a cheap-fix (per the audit brief), but keeps dashboardApi itself because pluginsApi still references /plugins/:pluginId/dashboard as a plugin REST endpoint (unrelated to the /dashboard route).

F11. AgentDetail self-reference breadcrumbsAgentDetail.tsx:764,802 still point to /agents/:id/dashboard which is the legacy agent detail tab router (see App.tsx line 160 agents/:agentId/:tab). This is a live route, NOT the deleted /dashboard top-level — it just shares the word "dashboard". Verified with App.tsx:160. Leave as-is.

F12. dashboardApi.summary(companyId) API at src/api/dashboard.ts:5 still exports /companies/:id/dashboard. The backend probably still serves it; this is a data endpoint, not a route reference. Out of scope.

Fixes applied in this phase

See the commit log — Phase 7 ships:

  1. docs(nexus): phase 7 visual qa audit — this document.
  2. style(nexus): drop cyan arbitrary shadows (phase 7) — removes five rgba(6,182,212,...) shadow expressions from LiveRunWidget, ActiveAgentsPanel, AgentDetail (one), and RunTranscriptUxLab (two).
  3. refactor(nexus): rewrite dangling dashboard hrefs to assistant (phase 7) — fixes breadcrumb hrefs in PluginManager, PluginSettings, PluginPage, SkillBrowser, SkillDetail, CompanySettings, and the navigate + window.assign call sites in ProjectDetail + CompanyImport. Also updates lib/company-page-memory.ts fallback. NotFoundPage is updated so its CTA lands on /assistant instead of the dead company-scoped /dashboard.
  4. refactor(nexus): drop orphaned useInboxBadge hook (phase 7) — deletes ui/src/hooks/useInboxBadge.ts (zero consumers).
  5. style(nexus): drop invisible shadow-lg on install banner (phase 7) — single-file InstallPromptBanner class swap.

Per-route notes

(one entry per worklist route; "OK" = no findings beyond those listed above)

Primary destinations

  • /assistant (pages/PersonalAssistant.tsx) — OK. Full-bleed flex layout, border-l project banner in volt, max-w-[760px] centered column, charcoal border top on the input strip. All tokens / classes are design-compliant.

  • /content-studio (pages/ContentStudio.tsx + components/studio/*) — OK. Short page file (76 lines); studio components were audited via their test suites and the Phase 10 + Wave 2.5 notes. WorkshopCard, StudioPromptBar all check out.

  • /content-studio/:workshopSlug (pages/StudioWorkshopDetail.tsx) — OK. 362 lines; no shadow or raw-palette findings.

  • /projects (pages/Projects.tsx) — OK. 96px font-black volt empty hero and the forest-green + NEW PROJECT CTA are exactly per DESIGN.md §4. Note the bg-[#166534] literal (see Deviation 1).

  • /projects/:projectId + /overview (pages/ProjectDetail.tsx + tabs/OverviewTab.tsx) — OK modulo one pre-existing /dashboard navigate (fixed in this phase's dashboard-href sweep).

  • /projects/:projectId/issues (tabs/IssuesTab.tsx) — OK (delegates to real IssuesList component; charcoal borders).

  • /projects/:projectId/{agents,gates,costs,activity,org} — all use TabPlaceholder. Placeholder itself (components/projects/tabs/TabPlaceholder.tsx) uses 4px-radius, charcoal border, pale-yellow gap badge. Compliant.

  • /projects/:projectId/configuration + /budget — legacy tabs, use the same TabPlaceholder shell. OK.

  • /instance/settings/general (pages/InstanceGeneralSettings.tsx + 8 settings cards) — OK. Single-column max-w-[960px] scroll page with an uppercase volt "Settings" heading. Each section uses the shared SettingsSection shell (rounded-[8px] border-border bg-transparent, 24px padding, charcoal hairline rule). All section cards audited — no findings.

Chrome (Phase 8 + 14 + 15)

  • Layout.tsx — OK. Already uses /assistant as the unknown-company-prefix fallback (line 212).
  • IconRail.tsx (56px) — OK. Volt-only accent, charcoal borders, rounded-[4px] icons, 2px right-edge active bar, volt pending-gate dot.
  • TopStrip.tsx (48px) — OK. border-b border-border bg-background.
  • ModeBreadcrumb.tsx — OK. 14px semibold uppercase tracking-[0.1em], leaf in volt, trunk in muted-foreground, slash separators.
  • CmdKButton.tsx — OK. Hover shifts border + text to volt.
  • GlobalMicButton.tsx — OK. Three visual states correctly wired to VoiceContext. Literal hexes noted in Deviation 1.
  • MobileTabBar.tsx (Phase 15) — OK. 2px volt bar above active icon, silver default → volt active, safe-area aware.
  • CommandPalette.tsx (Phase 14) — OK (consulted Phase 14 commit).

Legacy detail pages

  • /agents/new (NewAgent.tsx) — skimmed. No findings; uses tokens.
  • /agents/:agentId (AgentDetail.tsx, 4104 lines) — skim only per the Phase 7 directive. One cyan shadow on line 1107 dropped; two dashboard breadcrumb hrefs retained (they're legacy agent-tab router paths, not dead top-level routes — see F11).
  • /issues/:issueId (IssueDetail.tsx, 1696 lines) — skim only. No blockers in the visible layout header.
  • /company/settings (CompanySettings.tsx) — breadcrumb href fixed to /assistant.
  • /company/export/* (CompanyExport.tsx) — OK. References to /company/export/... are real routes, not dead dashboard links.
  • /company/import (CompanyImport.tsx) — post-import window.location.assign rewritten from /${prefix}/dashboard/${prefix}/assistant. User-visible "company" vocab is already routed through VOCAB.company which resolves to "Workspace".
  • /skills/* (CompanySkills.tsx) — OK. SkillBrowser + SkillDetail breadcrumb hrefs fixed.
  • /execution-workspaces/:workspaceId (ExecutionWorkspaceDetail.tsx) — skimmed. No findings.

Auth + landing + empty states

  • /auth (Auth.tsx) — OK. Border-only inputs, foreground text, destructive error. Left half form, right half ASCII animation.
  • /invite/:token (InviteLanding.tsx) — not touched; out of scope scan.
  • /board-claim/:token (BoardClaim.tsx) — not touched.
  • /cli-auth/:id (CliAuth.tsx) — not touched.
  • /* (NotFound.tsx) — breadcrumb href was /${prefix}/dashboard. Rewritten to /${prefix}/assistant and the button label changed from "Open dashboard" to "Open assistant".
  • Onboarding wizard (OnboardingWizard.tsx + onboarding/*) — not touched; previous phases already covered.
  • /design-guide (DesignGuide.tsx, 1532 lines) — the canonical DESIGN.md showcase. Skimmed; not touched (any findings would be meta since the page IS the design system).

Plugin subsystem

  • /plugins/:pluginId (PluginPage.tsx) — back link href fixed.
  • /instance/settings/plugins (PluginManager.tsx) — breadcrumb href fixed.
  • /instance/settings/plugins/:pluginId (PluginSettings.tsx) — breadcrumb href fixed.
  1. Wire the forest-green and pale-yellow token classes — eliminates the bg-[#166534] / bg-[#f4f692] literals in ProjectCard, Projects, GlobalMicButton, and TabPlaceholder. Low-risk index.css addition plus a grep-and-replace. One commit of ~15 line touches. (MIGRATION-PLAN.md §3.)

  2. Drop the remaining shadow-xs survivors in shadcn primitives (F1 + F2) and collapse their transition-[color,box-shadow] to transition-colors. Five files, ten lines total. Visible improvement: removes the last "invisible on black" shadow artifacts.

  3. Resolve the Basier font decision (Deviation 2). Either license + load it, or formally accept Inter as the sole display font and update DESIGN.md §3 to match. This is the last open item from the visual repaint and blocks closing the MIGRATION-PLAN open-decisions list.