Repository navigation
feat(dashboard): unified visual identity — grid, primitives, tables, form controls (design phases 1-4) - #4122
Conversation
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
There was a problem hiding this comment.
Code Review
This pull request implements Phase 1 of the OmniRoute design system standardization, introducing a global, theme-aware graph-paper grid wallpaper and adding shared identity tokens to globals.css. It also updates DashboardLayout to allow the grid to show through and adds static guard tests. Feedback on the tests suggests migrating from the native Node.js test runner to Vitest to align with the repository's standard testing framework, and refactoring the CSS rule selector to avoid matching comments.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| import test from "node:test"; | ||
| import assert from "node:assert/strict"; | ||
| import fs from "node:fs"; | ||
|
|
||
| // Static guards for the shared visual identity (Phase 1: graph-paper grid wallpaper). | ||
| // These lock in the cross-product design contract so an accidental edit can't silently | ||
| // remove the grid or re-introduce the opaque wrapper that hides it. See design.md. | ||
|
|
||
| const globalsCss = fs.readFileSync( | ||
| new URL("../../src/app/globals.css", import.meta.url), | ||
| "utf8" | ||
| ); | ||
| const dashboardLayout = fs.readFileSync( | ||
| new URL("../../src/shared/components/layouts/DashboardLayout.tsx", import.meta.url), | ||
| "utf8" | ||
| ); | ||
|
|
||
| test("globals.css defines the grid wallpaper tokens for both themes", () => { | ||
| // light | ||
| assert.match(globalsCss, /--grid-line:\s*rgba\(0,\s*0,\s*0,\s*0\.045\)/); | ||
| // dark | ||
| assert.match(globalsCss, /--grid-line:\s*rgba\(255,\s*255,\s*255,\s*0\.035\)/); | ||
| // size + alternating-section overlay | ||
| assert.match(globalsCss, /--grid-size:\s*46px/); | ||
| assert.match(globalsCss, /--section-alt:\s*rgba\(0,\s*0,\s*0,\s*0\.022\)/); | ||
| assert.match(globalsCss, /--section-alt:\s*rgba\(255,\s*255,\s*255,\s*0\.018\)/); | ||
| }); | ||
|
|
||
| test("globals.css renders the grid via a body::before fixed layer", () => { | ||
| // The pseudo-element must exist and be the grid renderer. | ||
| const before = globalsCss.slice(globalsCss.indexOf("body::before")); | ||
| assert.ok(before.length > 0, "body::before rule is present"); | ||
| assert.match(before, /position:\s*fixed/); | ||
| assert.match(before, /z-index:\s*-1/); | ||
| assert.match(before, /pointer-events:\s*none/); | ||
| assert.match( | ||
| before, | ||
| /linear-gradient\(to right,\s*var\(--grid-line\) 1px, transparent 1px\)/ | ||
| ); | ||
| assert.match( | ||
| before, | ||
| /linear-gradient\(to bottom,\s*var\(--grid-line\) 1px, transparent 1px\)/ | ||
| ); | ||
| assert.match(before, /background-size:\s*var\(--grid-size\) var\(--grid-size\)/); | ||
| }); | ||
|
|
||
| test("globals.css adds the shared identity tokens", () => { | ||
| assert.match(globalsCss, /--surface-2:\s*#f5f5fa/); // light | ||
| assert.match(globalsCss, /--surface-2:\s*#1c2230/); // dark | ||
| assert.match(globalsCss, /--radius:\s*14px/); | ||
| assert.match( | ||
| globalsCss, | ||
| /--grad-brand:\s*linear-gradient\(135deg,\s*var\(--color-primary\),\s*var\(--color-accent-light\)\)/ | ||
| ); | ||
| // exposed to Tailwind as bg-surface-2 for later phases | ||
| assert.match(globalsCss, /--color-surface-2:\s*var\(--surface-2\)/); | ||
| }); | ||
|
|
||
| test("DashboardLayout wrapper stays transparent so the grid shows through", () => { | ||
| // Regression guard: the outer shell must NOT paint an opaque bg over the body grid. | ||
| assert.ok( | ||
| !dashboardLayout.includes("overflow-hidden bg-bg"), | ||
| "DashboardLayout outer wrapper must not use bg-bg (it would hide the grid wallpaper)" | ||
| ); | ||
| assert.ok( | ||
| dashboardLayout.includes('className="flex h-dvh min-h-0 w-full overflow-hidden"'), | ||
| "DashboardLayout outer wrapper is present and transparent" | ||
| ); | ||
| }); |
There was a problem hiding this comment.
The test file currently uses the native Node.js test runner (node:test and node:assert) instead of Vitest, which is the standard testing framework configured for this repository (as indicated by vitest.config.ts). Using Vitest's native APIs (test, expect) ensures consistency across the test suite, proper integration with Vitest's reporter, watch mode, and coverage tools.
Additionally, globalsCss.indexOf("body::before") matches the comment on line 91 of globals.css rather than the actual CSS rule on line 238. Searching for "body::before {" is much more robust and avoids matching comments.
Here is the refactored test suite using Vitest and the robust selector match.
import { test, expect } from "vitest";
import fs from "node:fs";
// Static guards for the shared visual identity (Phase 1: graph-paper grid wallpaper).
// These lock in the cross-product design contract so an accidental edit can't silently
// remove the grid or re-introduce the opaque wrapper that hides it. See design.md.
const globalsCss = fs.readFileSync(
new URL("../../src/app/globals.css", import.meta.url),
"utf8"
);
const dashboardLayout = fs.readFileSync(
new URL("../../src/shared/components/layouts/DashboardLayout.tsx", import.meta.url),
"utf8"
);
test("globals.css defines the grid wallpaper tokens for both themes", () => {
// light
expect(globalsCss).toMatch(/--grid-line:\s*rgba\(0,\s*0,\s*0,\s*0\.045\)/);
// dark
expect(globalsCss).toMatch(/--grid-line:\s*rgba\(255,\s*255,\s*255,\s*0\.035\)/);
// size + alternating-section overlay
expect(globalsCss).toMatch(/--grid-size:\s*46px/);
expect(globalsCss).toMatch(/--section-alt:\s*rgba\(0,\s*0,\s*0,\s*0\.022\)/);
expect(globalsCss).toMatch(/--section-alt:\s*rgba\(255,\s*255,\s*255,\s*0\.018\)/);
});
test("globals.css renders the grid via a body::before fixed layer", () => {
// The pseudo-element must exist and be the grid renderer.
// Using "body::before {" avoids matching comments containing "body::before".
const ruleIndex = globalsCss.indexOf("body::before {");
expect(ruleIndex).not.toBe(-1);
const before = globalsCss.slice(ruleIndex);
expect(before).toMatch(/position:\s*fixed/);
expect(before).toMatch(/z-index:\s*-1/);
expect(before).toMatch(/pointer-events:\s*none/);
expect(before).toMatch(
/linear-gradient\(to right,\s*var\(--grid-line\) 1px, transparent 1px\)/
);
expect(before).toMatch(
/linear-gradient\(to bottom,\s*var\(--grid-line\) 1px, transparent 1px\)/
);
expect(before).toMatch(/background-size:\s*var\(--grid-size\) var\(--grid-size\)/);
});
test("globals.css adds the shared identity tokens", () => {
expect(globalsCss).toMatch(/--surface-2:\s*#f5f5fa/);
expect(globalsCss).toMatch(/--surface-2:\s*#1c2230/);
expect(globalsCss).toMatch(/--radius:\s*14px/);
expect(globalsCss).toMatch(
/--grad-brand:\s*linear-gradient\(135deg,\s*var\(--color-primary\),\s*var\(--color-accent-light\)\)/
);
// exposed to Tailwind as bg-surface-2 for later phases
expect(globalsCss).toMatch(/--color-surface-2:\s*var\(--surface-2\)/);
});
test("DashboardLayout wrapper stays transparent so the grid shows through", () => {
// Regression guard: the outer shell must NOT paint an opaque bg over the body grid.
expect(dashboardLayout).not.toContain("overflow-hidden bg-bg");
expect(dashboardLayout).toContain('className="flex h-dvh min-h-0 w-full overflow-hidden"');
});
CI Coverage Report
Coverage artifact was not available for this run. |
4da1440 to
55bbfb4
Compare
…(design phase 1) Unify the dashboard with the marketing site into one visual identity. Phase 1 is the low-risk, high-visibility slice: - globals.css: add the graph-paper grid tokens (--grid-line/--grid-size/ --section-alt, per theme) and render a single fixed body::before wallpaper behind every screen (panel, auth, error pages). z-index:-1 paints above body's background but below in-flow content, so text contrast is unchanged. - Add the inert, collision-free identity tokens mirrored from the site: --surface-2 (+ bg-surface-2 via @theme), --grad-brand, --radius. These are definitions only — components are repointed to them in later phases. The small radius and the radius wiring are deferred to Phase 2 because Tailwind v4's reserved --radius-sm backs rounded-sm (used in 12 files). - DashboardLayout: drop the opaque bg-bg on the outer wrapper — the single element that hid the grid in the content area; everything below it is already transparent. - design.md (repo root): full analysis + the 4-phase standardization plan. - Guard test pinning the grid wallpaper + the un-blocked wrapper against regressions. Colors were already shared (the site's tokens mirror globals.css), so there is no palette change. Radius repoint, Button gradient, and table/status-color cleanup follow in Phases 2-4 (one PR each). See design.md.
…ient & border token (design phase 2) Phase 2 of the visual-identity unification — the design-system primitives now match the marketing site, building on Phase 1's tokens: - globals.css: semantic radius utilities via @theme — rounded-card (14px, surfaces) and rounded-control (9px, controls). Custom names, so Tailwind's default rounded-sm/md/lg/xl utilities (used in 400+ files) stay untouched — no blind product-wide radius shift. - Button: primary now paints the brand gradient (--grad-brand, red→violet) instead of the flat red→red; added the missing `accent` variant (indigo); all sizes use rounded-control. - Card / Modal: rounded-card (14px). Card borders moved to the brand `border-border` token (0.08) instead of the under-weight /5. - Input / Select: rounded-control (9px). - Guard test extended; design.md updated. cn()→tailwind-merge (C9) and the ad-hoc rounded-lg sweep are deferred; tables and status-color centralization remain Phase 3. See design.md.
55bbfb4 to
49e1c42
Compare
…n phase 3, safe slice) - src/shared/constants/statusColors.ts: STATUS_HEX as the single source of truth, mirroring globals.css --color-success/--color-warning/--color-error. Repoint flow/edgeStyles.ts and TokenHealthBadge to it — faithful (identical hex, one place instead of copy-pasted literals). - globals.css: add the --font-mono token (parity with the marketing site). - Guard test + design.md updated. The riskier DataTable migration (C3) and hex sweep (C8) are deferred: several hardcoded hex are intentional (always-dark console terminal at ConsoleLogViewer.tsx:240, ReactFlow SVG strokes, table-header fallbacks) and a blind token-sweep would flip them in light theme — those need a visual pass on the running app. See design.md.
…(design phase 3, C3) The shared DataTable hardcoded its colors with inline rgba and two dead var() fallbacks — var(--bg-table-header, rgba(15,15,25,0.95)) and var(--text-secondary, #888) — that never resolved, so the table was effectively always-dark and broken in light theme. Introduce a --table-* token set whose DARK values exactly equal the old hardcoded rgba (header bg, zebra, row hover, cell border) so dark stays byte-identical, and whose light values fix the light theme. Header border → --color-border (which is rgba(255,255,255,0.08) in dark = the exact old value); secondary text → --color-text-muted; the selected-row accent tint is kept (theme-neutral).⚠️ Wants a visual pass before merge — light-theme tables + the #888→muted text shift. logTableStyles.ts + the legacy Ant table rules are left untouched (separate, lower priority). See design.md.
…(design phase 4, safe slice) - cn() now uses twMerge(clsx(...)) (clsx + tailwind-merge added as deps): a caller's className override correctly REPLACES a primitive's conflicting class instead of stacking and relying on CSS source order. Lock synced with --package-lock-only (both packages were already present transitively; node_modules untouched). - New token-driven Checkbox + Textarea primitives, exported from the shared barrel, completing the form-control set. Additive — adoption of the 32 raw checkboxes / 41 raw textareas can follow incrementally. Deferred (need a visual pass / intentional): C6 focus-ring reconcile; C8 hex-sweep is NOT a blind replace — the always-dark console terminal and ReactFlow SVG strokes stay. See design.md.
Both are already declared in package.json and consumed by src/shared/utils/cn.ts (standard shadcn class-merge helper). The check:deps anti-slopsquatting gate requires an explicit allowlist entry per dependency; add them so Fast Quality Gates passes.
…form controls (design phases 1-4) (cheaper-inference#4122) Unified visual identity (design phases 1-4): grid, primitives, tables, form controls. Integrado em release/v3.8.29: clsx + tailwind-merge adicionados à dependency-allowlist (já declarados no package.json, consumidos por src/shared/utils/cn.ts) para o check:deps anti-slopsquatting passar.
What & why
Standardize the OmniRoute dashboard and the marketing site into one visual identity. Full analysis + plan in
design.md(repo root). Base:release/v3.8.29.Phase 1 — Graph-paper grid wallpaper
Grid tokens + a single fixed
body::beforebehind every screen (z-index:-1, contrast unchanged); drop the opaquebg-bgwrapper that hid it; identity tokens--surface-2/--grad-brand/--radius.Phase 2 — Primitives adopt the shared scale
Semantic radius utils
rounded-card(14px) /rounded-control(9px) via@theme(custom names — defaults untouched). Button primary →--grad-brand(red→violet) + newaccentvariant; Card/Modal →rounded-card, Card border →border-border; Input/Select →rounded-control.Phase 3 — Status colors + theme-aware tables
statusColors.ts(STATUS_HEXsingle source; edgeStyles + TokenHealthBadge repointed).--font-monotoken. DataTable → theme-aware via--table-*tokens whose dark values exactly equal the old hardcoded rgba (dark byte-identical) and whose light values fix the previously-buggy always-dark light theme.Phase 4 — cn() merge + form-control primitives
cn()→twMerge(clsx(...))(clsx + tailwind-merge added; lock synced via--package-lock-only, node_modules untouched) so a caller'sclassNamecorrectly replaces a primitive's conflicting class. New token-driven Checkbox + Textarea primitives (barrel-exported; additive).#888→--color-text-muted.cn()is now tailwind-merge (global) — low-risk (last-conflicting-wins is what CSS already did) but worth a glance.Deferred (intentional — see
design.md)ConsoleLogVieweris an always-dark terminal; ReactFlow strokes are SVG. C6 focus-ring reconcile (taste).logTableStyles.ts/legacy Ant rules. Adoption of the 32 raw checkboxes / 41 raw textareas can follow incrementally.Validation
typecheck:coreclean ·eslintclean · pre-commit hooks (docs-sync, any-budget, tracked-artifacts) cleannpm run build→ exit 0; compiled CSS confirmsrounded-card/rounded-control/grad-brand/accent-color:var(--color-accent)/grid-line.🤖 Generated with Claude Code