Skip to content

feat(dashboard): unified visual identity — grid, primitives, tables, form controls (design phases 1-4) - #4122

Merged
diegosouzapw merged 6 commits into
release/v3.8.29from
feat/visual-identity-grid
Jun 18, 2026
Merged

diegosouzapw merged 6 commits into
release/v3.8.29from
feat/visual-identity-grid

Conversation

@diegosouzapw

@diegosouzapw diegosouzapw commented Jun 17, 2026 •

Copy link
Copy Markdown
Collaborator

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.

The colors were already shared — the site's tokens.css mirrors src/app/globals.css (verified token-by-token). No palette change; this finishes the identity.

Phase 1 — Graph-paper grid wallpaper

Grid tokens + a single fixed body::before behind every screen (z-index:-1, contrast unchanged); drop the opaque bg-bg wrapper 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) + new accent variant; Card/Modal → rounded-card, Card border → border-border; Input/Select → rounded-control.

Phase 3 — Status colors + theme-aware tables

statusColors.ts (STATUS_HEX single source; edgeStyles + TokenHealthBadge repointed). --font-mono token. 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's className correctly replaces a primitive's conflicting class. New token-driven Checkbox + Textarea primitives (barrel-exported; additive).

⚠️ Reviewer — please do a visual pass

  • Grid in dark + light on a busy screen (providers/logs).
  • Light-theme tables (the one behavioral change — they were buggy-dark) + the table secondary-text shift #888→--color-text-muted.
  • Button gradient, rounder cards/inputs.
  • 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)

  • Do NOT blind-sweep remaining hex: ConsoleLogViewer is 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

  • guard test → 12/12 pass · typecheck:core clean · eslint clean · pre-commit hooks (docs-sync, any-budget, tracked-artifacts) clean
  • npm run build → exit 0; compiled CSS confirms rounded-card/rounded-control/grad-brand/accent-color:var(--color-accent)/grid-line.

🤖 Generated with Claude Code

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +1 to +69
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"
);
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

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"');
});

@github-actions

Copy link
Copy Markdown
Contributor

CI Coverage Report

  • Coverage job: skipped
  • PR test policy: success

Coverage artifact was not available for this run.

@diegosouzapw
diegosouzapw changed the base branch from main to release/v3.8.29 June 18, 2026 00:08
@diegosouzapw
diegosouzapw force-pushed the feat/visual-identity-grid branch from 4da1440 to 55bbfb4 Compare June 18, 2026 00:23
@diegosouzapw diegosouzapw changed the title feat(dashboard): unified visual identity — grid wallpaper + tokens (design phase 1) feat(dashboard): unified visual identity — grid + primitives (design phases 1-2) Jun 18, 2026
@diegosouzapw
diegosouzapw changed the base branch from release/v3.8.29 to main June 18, 2026 00:24
…(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.
@diegosouzapw
diegosouzapw force-pushed the feat/visual-identity-grid branch from 55bbfb4 to 49e1c42 Compare June 18, 2026 00:38
@diegosouzapw
diegosouzapw changed the base branch from main to release/v3.8.29 June 18, 2026 00:38
…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.
@diegosouzapw diegosouzapw changed the title feat(dashboard): unified visual identity — grid + primitives (design phases 1-2) feat(dashboard): unified visual identity — grid + primitives + status colors (design phases 1-3) Jun 18, 2026
…(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.
@diegosouzapw diegosouzapw changed the title feat(dashboard): unified visual identity — grid + primitives + status colors (design phases 1-3) feat(dashboard): unified visual identity — grid, primitives, tables, form controls (design phases 1-4) Jun 18, 2026
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.
@diegosouzapw
diegosouzapw merged commit 541ff3b into release/v3.8.29 Jun 18, 2026
4 checks passed
tkgo11 pushed a commit to tkgo11/OmniRoute that referenced this pull request Sep 23, 2026
…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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant