fix(dashboard): data tables paint an opaque surface so the grid doesn't bleed through (#4233)

Integrated into release/v3.8.29
This commit is contained in:
Diego Rodrigues de Sa e Souza
2026-06-19 00:01:17 -03:00
committed by GitHub
parent 48773face6
commit 291103513e
13 changed files with 104 additions and 15 deletions

View File

@@ -1,6 +1,6 @@
# OmniRoute — Design System & Visual Identity
> **Status:** standardization plan. **Phases 13 are implemented in this PR** (grid, primitives, status-color centralization, mono token, and the DataTable token migration). The DataTable migration is **faithful** — dark stays byte-identical (the new `--table-*` dark values equal the old hardcoded rgba); light is fixed (it was buggy always-dark via dead `var()` fallbacks). **⚠️ Wants a visual pass before merge** (light-theme tables + the secondary-text shift `#888`→`--color-text-muted`). **Phase 4 is now largely done too** (C6 focus-ring → accent, C7 Checkbox/Textarea primitives, C9 `cn()`→tailwind-merge); only the selective C8 hex-sweep remains. Note several remaining "hardcoded" hex are _intentional_ (always-dark console terminal, ReactFlow SVG strokes) and must NOT be swept. **Phase 5 (D4 + D8): the grid now reaches every standalone screen** (login/auth/error/legal/status/onboarding — their opaque `bg-bg` full-screen wrappers were hiding it) **and the dashboard content shell is fluid up to 4K** (`max-w-7xl` → `max-w-[3840px]`) so it follows the viewport on large monitors instead of centering with wide side gutters.
> **Status:** standardization plan. **Phases 13 are implemented in this PR** (grid, primitives, status-color centralization, mono token, and the DataTable token migration). The DataTable migration is **faithful** — dark stays byte-identical (the new `--table-*` dark values equal the old hardcoded rgba); light is fixed (it was buggy always-dark via dead `var()` fallbacks). **⚠️ Wants a visual pass before merge** (light-theme tables + the secondary-text shift `#888`→`--color-text-muted`). **Phase 4 is now largely done too** (C6 focus-ring → accent, C7 Checkbox/Textarea primitives, C9 `cn()`→tailwind-merge); only the selective C8 hex-sweep remains. Note several remaining "hardcoded" hex are _intentional_ (always-dark console terminal, ReactFlow SVG strokes) and must NOT be swept. **Phase 5 (D4 + D8): the grid now reaches every standalone screen** (login/auth/error/legal/status/onboarding — their opaque `bg-bg` full-screen wrappers were hiding it) **and the dashboard content shell is fluid up to 4K** (`max-w-7xl` → `max-w-[3840px]`) so it follows the viewport on large monitors instead of centering with wide side gutters. **Phase 6 (D9): data tables are now opaque surfaces** so the grid no longer bleeds through their rows — card-less tables paint `bg-surface`, and the two log tables' semi-transparent `bg-black/5` tint (which tailwind-merge let win over the Card's `bg-surface`) is removed. The grid size itself is already correct (32px, identical to the site); a "bigger" grid on a running instance is a stale build, not code.
> **Date:** 2026-06-16 · **Scope:** unify the OmniRoute dashboard (`src/`) with the marketing site (`_mono_repo/omnirouteSite/`) into **one visual identity** — same graph-paper grid background, same color tokens, standardized components.
---
@@ -192,6 +192,7 @@ Each phase: `npm run lint` + `npm run typecheck:core` + a visual pass.
- **D6 — Radius 14/9 product-wide:** Rec: yes (Phase 2).
- **D7 — Phase 1 ships first:** Chosen.
- **D8 — Layout width (Phase 5):** the dashboard content shell was capped at `max-w-7xl` (1280px), centering with wide empty side gutters on large monitors. ✅ **DONE** — raised to a fluid `max-w-[3840px]` (true 4K): content now follows the viewport up to ~4K and only centers beyond it (`DashboardLayout.tsx`). Deliberately-narrow pages stay narrow by design (`ProviderOnboardingWizard` max-w-5xl, `Rtk`/`CavemanContextPageClient` max-w-6xl).
- **D9 — Opaque data tables (Phase 6):** with the dashboard content area now transparent (so the grid wallpaper shows through, Phase 5), data tables whose container was _not_ an opaque surface let the grid bleed through their transparent even-rows / low-alpha zebra. ✅ **DONE** — every card-less table now paints `bg-surface` (or, for the `<DataTable>` primitive, `background: var(--color-surface)` on its scroll container). Fixed: `DataTable` (primitive), `ProxyLogger`/`RequestLoggerV2` (their `<Card>` `bg-black/5 dark:bg-black/20` tint was winning over the Card's `bg-surface` via tailwind-merge → ~95% transparent), `BatchListTab`/`FilesListTab`/`CacheEntriesTab`/`ReasoningCacheTab`/`cache page`/`FreePoolTab`/`ModelMappingTable`/`HeaderTable`, plus the two CSS-grid "tables" in the cache views (`bg-surface/35` → `bg-surface`). Tables already inside a `<Card>`/Modal were verified opaque and deliberately left untouched (bg-surface there is a redundant no-op). The grid itself needed **no change** — dashboard `body::before` is byte-identical to the site (`--grid-size: 32px`); any "bigger grid" seen on a running instance is a stale pre-`#4143` build, not code. Guarded by `tests/unit/design-grid-background.test.ts` (Phase 6 block).
---

View File

@@ -402,7 +402,7 @@ export default function BatchListTab({
</div>
{/* Table — 9 columns: Status | ID | Endpoint | Model | Progress | Cost | Created | Expires | Actions */}
<div className="overflow-x-auto overflow-y-hidden rounded-xl border border-[var(--color-border)]">
<div className="overflow-x-auto overflow-y-hidden rounded-xl border border-[var(--color-border)] bg-[var(--color-surface)]">
<table className="w-full text-sm" role="table" aria-label={t("batchListBatchesTable")}>
<thead>
<tr className="bg-[var(--color-bg-alt)] border-b border-[var(--color-border)]">

View File

@@ -162,7 +162,7 @@ export default function FilesListTab({
</div>
{/* Table */}
<div className="overflow-x-auto overflow-y-hidden rounded-xl border border-[var(--color-border)]">
<div className="overflow-x-auto overflow-y-hidden rounded-xl border border-[var(--color-border)] bg-[var(--color-surface)]">
<table className="w-full text-sm" role="table" aria-label={t("batchFilesListFilesTable")}>
<thead>
<tr className="bg-[var(--color-bg-alt)] border-b border-[var(--color-border)]">

View File

@@ -111,7 +111,7 @@ export default function CacheEntriesTab() {
<div className="text-sm text-text-muted text-center py-8">{t("noEntries")}</div>
) : (
<>
<div className="overflow-x-auto">
<div className="overflow-x-auto bg-surface">
<table className="w-full text-sm">
<thead>
<tr className="text-left text-xs text-text-muted border-b border-border/30">

View File

@@ -277,7 +277,7 @@ export default function ReasoningCacheTab() {
{providerEntries.length > 0 && (
<div className="rounded-2xl border border-border/30 bg-surface/20 p-5">
<h3 className="text-sm font-medium text-text-main">{t("reasoningByProvider")}</h3>
<div className="mt-3 overflow-x-auto rounded-2xl border border-border/20 bg-surface/35">
<div className="mt-3 overflow-x-auto rounded-2xl border border-border/20 bg-surface">
<table className="w-full text-sm">
<thead>
<tr className="border-b border-border/20 text-left text-[11px] uppercase tracking-[0.12em] text-text-muted">
@@ -330,7 +330,7 @@ export default function ReasoningCacheTab() {
{modelEntries.length > 0 && (
<div className="rounded-2xl border border-border/30 bg-surface/20 p-5">
<h3 className="text-sm font-medium text-text-main">{t("reasoningByModel")}</h3>
<div className="mt-3 overflow-x-auto rounded-2xl border border-border/20 bg-surface/35">
<div className="mt-3 overflow-x-auto rounded-2xl border border-border/20 bg-surface">
<table className="w-full text-sm">
<thead>
<tr className="border-b border-border/20 text-left text-[11px] uppercase tracking-[0.12em] text-text-muted">
@@ -376,7 +376,7 @@ export default function ReasoningCacheTab() {
{t("reasoningNoData")}
</div>
) : (
<div className="overflow-hidden rounded-2xl border border-border/20 bg-surface/35">
<div className="overflow-hidden rounded-2xl border border-border/20 bg-surface">
<div className="grid grid-cols-[minmax(120px,1fr)_100px_minmax(100px,1fr)_80px_80px_60px] gap-3 border-b border-border/20 px-4 py-3 text-[11px] font-medium uppercase tracking-[0.12em] text-text-muted">
<span>{t("reasoningToolCallId")}</span>
<span>{t("tableProvider")}</span>

View File

@@ -293,7 +293,7 @@ function PromptTrendPanel({
/>
</div>
<div className="mt-5 overflow-hidden rounded-2xl border border-border/25 bg-surface/35">
<div className="mt-5 overflow-hidden rounded-2xl border border-border/25 bg-surface">
<div className="grid grid-cols-[84px_minmax(0,1fr)_92px_120px] gap-3 border-b border-border/20 px-4 py-3 text-[11px] font-medium uppercase tracking-[0.12em] text-text-muted">
<span>{hourLabel}</span>
<span>{activityLabel}</span>
@@ -570,7 +570,7 @@ export default function CachePage() {
</div>
{providerEntries.length > 0 ? (
<div className="mt-3 overflow-x-auto rounded-2xl border border-border/20 bg-surface/35">
<div className="mt-3 overflow-x-auto rounded-2xl border border-border/20 bg-surface">
<table className="w-full text-sm">
<thead>
<tr className="border-b border-border/20 text-left text-[11px] uppercase tracking-[0.12em] text-text-muted">

View File

@@ -216,7 +216,7 @@ export default function FreePoolTab() {
</div>
)}
<div className="overflow-x-auto rounded border border-border">
<div className="overflow-x-auto rounded border border-border bg-surface">
<table className="w-full text-sm">
<thead className="bg-surface-alt text-text-muted text-xs">
<tr>

View File

@@ -48,7 +48,7 @@ export function ModelMappingTable({ agentId, mappings, onSave }: ModelMappingTab
return (
<div className="flex flex-col gap-3">
<div className="rounded-lg border border-border/40 overflow-hidden">
<div className="rounded-lg border border-border/40 overflow-hidden bg-surface">
<table className="w-full text-sm">
<thead>
<tr className="border-b border-border/40 bg-surface/60">

View File

@@ -22,7 +22,7 @@ export function HeaderTable({ headers }: HeaderTableProps) {
{masked ? "Show" : "Hide"}
</button>
</div>
<table className="w-full text-xs font-mono border-collapse">
<table className="w-full text-xs font-mono border-collapse bg-surface">
<thead>
<tr className="border-b border-border">
<th className="text-left px-2 py-1 text-text-muted font-medium">Name</th>

View File

@@ -100,7 +100,16 @@ export default function DataTable({
}
return (
<div style={{ overflow: "auto", maxHeight, borderRadius: "8px" }}>
<div
style={{
overflow: "auto",
maxHeight,
borderRadius: "8px",
// Opaque surface so the body grid wallpaper never bleeds through the
// transparent even-rows / low-alpha zebra when the table renders card-less.
background: "var(--color-surface)",
}}
>
<table
style={{
width: "100%",

View File

@@ -390,7 +390,7 @@ export default function ProxyLogger() {
</div>
{/* Table */}
<Card className="overflow-hidden bg-black/5 dark:bg-black/20">
<Card className="overflow-hidden bg-surface">
<div className="p-0 overflow-x-auto max-h-[calc(100vh-320px)] overflow-y-auto">
{loading && logs.length === 0 ? (
<div className="p-8 text-center text-text-muted">{t("loadingProxyLogs")}</div>

View File

@@ -947,7 +947,7 @@ const RequestLoggerV2 = forwardRef<RequestLoggerV2Handle, { initialSelectedId?:
{/* Table */}
<Card
padding="none"
className="min-h-[460px] resize-y overflow-auto bg-black/5 dark:bg-black/20"
className="min-h-[460px] resize-y overflow-auto bg-surface"
>
<div
ref={scrollContainerRef}

View File

@@ -222,3 +222,82 @@ test("DashboardLayout content shell is fluid up to ~4K before centering", () =>
);
assert.ok(!dashboardLayout.includes("max-w-7xl"), "the old 1280px max-w-7xl cap is gone");
});
// ── Phase 6: data tables are opaque content surfaces so the grid never bleeds through ──
//
// The dashboard content area is intentionally transparent (the body::before grid shows
// through as a wallpaper). A data table whose nearest ancestor is NOT an opaque surface
// would let the grid bleed through its transparent even-rows / low-alpha zebra rows.
// Cards already carry bg-surface; these guards cover the shared table primitives and the
// tables that render *without* a Card. Tables verified to live inside a <Card>/Modal are
// intentionally left untouched (bg-surface there would be a redundant no-op).
test("DataTable primitive paints its own opaque surface", () => {
const dt = read("../../src/shared/components/DataTable.tsx");
assert.ok(
dt.includes("var(--color-surface)"),
"DataTable scroll container is opaque (its even rows are transparent by design)"
);
});
test("log table cards are opaque (no semi-transparent bg-black tint)", () => {
// bg-black/5|20 on a <Card> wins over the Card's own bg-surface via tailwind-merge,
// turning the big log tables ~95% transparent — the grid bled straight through them.
for (const p of [
"../../src/shared/components/ProxyLogger.tsx",
"../../src/shared/components/RequestLoggerV2.tsx",
]) {
const src = read(p);
assert.ok(
!src.includes("bg-black/5") && !src.includes("bg-black/20"),
`${p} must not tint the table Card with bg-black/5|20 (it drops the Card's opaque surface)`
);
assert.ok(src.includes("bg-surface"), `${p} table card uses the opaque bg-surface`);
}
});
test("card-less data tables wrap their table in an opaque surface", () => {
const expect = [
[
"../../src/app/(dashboard)/dashboard/batch/BatchListTab.tsx",
"rounded-xl border border-[var(--color-border)] bg-[var(--color-surface)]",
],
[
"../../src/app/(dashboard)/dashboard/batch/FilesListTab.tsx",
"rounded-xl border border-[var(--color-border)] bg-[var(--color-surface)]",
],
[
"../../src/app/(dashboard)/dashboard/cache/components/CacheEntriesTab.tsx",
"overflow-x-auto bg-surface",
],
[
"../../src/app/(dashboard)/dashboard/settings/components/proxy/FreePoolTab.tsx",
"rounded border border-border bg-surface",
],
[
"../../src/app/(dashboard)/dashboard/tools/agent-bridge/components/ModelMappingTable.tsx",
"overflow-hidden bg-surface",
],
[
"../../src/app/(dashboard)/dashboard/tools/traffic-inspector/components/shared/HeaderTable.tsx",
"bg-surface",
],
];
for (const [p, needle] of expect) {
const src = read(p);
assert.ok(src.includes(needle), `${p} must include "${needle}" so the table is opaque over the grid`);
}
});
test("semi-transparent cache table boxes are now opaque", () => {
for (const p of [
"../../src/app/(dashboard)/dashboard/cache/components/ReasoningCacheTab.tsx",
"../../src/app/(dashboard)/dashboard/cache/page.tsx",
]) {
const src = read(p);
assert.ok(
!src.includes("bg-surface/35"),
`${p} table box no longer uses the ~35%-opaque bg-surface/35 (the grid bled through it)`
);
}
});