feat(dashboard): grid wallpaper on all standalone screens + fluid 4K layout (#4158)

Integrated into release/v3.8.29
This commit is contained in:
Diego Rodrigues de Sa e Souza
2026-06-18 10:07:16 -03:00
committed by GitHub
parent a0e7e92e64
commit e0fe4facdf
13 changed files with 62 additions and 19 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.
> **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.
> **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.
---
@@ -127,7 +127,7 @@ The grid is global by construction (it covers the panel, `auth`/`login`, error p
### 4.6 Login / auth / error pages
These render directly under `<body>` (no panel chrome) and their wrappers are mostly transparent — the global grid appears behind them automatically. Exception: `login/page.tsx:124,139` uses opaque `bg-bg` wrappers; softening them (D4) is a small Phase 2 follow-up.
These render directly under `<body>` (no panel chrome), so the global grid should appear behind them automatically. **Phase 5 — DONE:** the standalone full-screen wrappers were in fact opaque (`min-h-screen … bg-bg`, where `bg-bg` is the same solid fill as `<body>`), which hid the grid on every non-dashboard screen — not just login. All of them are now transparent so the shared wallpaper shows through: `login`, `forgot-password`, `callback`, `maintenance`, `offline`, `status`, `terms`, `privacy`, `onboarding`, and `ErrorPageScaffold` (covers `400`/`401`). This closes **D4** (extended from login-only to every standalone screen). Guarded by `tests/unit/design-grid-background.test.ts`.
### 4.7 Landing page
@@ -187,10 +187,11 @@ Each phase: `npm run lint` + `npm run typecheck:core` + a visual pass.
- **D1 — Button primary:** keep red→red or switch to **red→violet `--grad-brand`**? Rec: **red→violet** (Phase 2).
- **D2 — Grid line color:** **neutral** (site style) — chosen — vs brand-red. Size **32px** (shrunk ~30% from the original 46px on owner feedback — 46px cells read too large on the dashboard layout).
- **D3 — Chrome vibrancy:** sidebar/header **solid** — chosen.
- **D4 — Auth/login grid:** soften `login/page.tsx` wrappers (Phase 2). Rec: yes.
- **D4 — Auth/login grid:** ✅ **DONE (Phase 5)** — opaque `bg-bg` removed from every standalone full-screen wrapper (not just login), so the grid shows on all screens. See §4.6.
- **D5 — Landing page:** leave animated splash as-is. Chosen.
- **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).
---

View File

@@ -224,14 +224,14 @@ export default function OnboardingWizard() {
if (loading) {
return (
<div className="min-h-screen flex items-center justify-center bg-bg">
<div className="min-h-screen flex items-center justify-center">
<div className="animate-pulse text-text-muted">{tc("loading")}</div>
</div>
);
}
return (
<div className="min-h-screen flex items-center justify-center bg-bg p-4">
<div className="min-h-screen flex items-center justify-center p-4">
<div className="w-full max-w-lg">
{/* Progress Indicator */}
<div className="flex items-center justify-center gap-2 mb-8">

View File

@@ -123,7 +123,7 @@ export default function CallbackPage() {
}, []);
return (
<div className="min-h-screen flex items-center justify-center bg-bg">
<div className="min-h-screen flex items-center justify-center">
<div className="text-center p-8 max-w-md">
{status === "processing" && (
<>

View File

@@ -30,7 +30,7 @@ export default function ForgotPasswordPage() {
}, [isElectron]);
return (
<div className="min-h-screen flex items-center justify-center bg-bg p-4">
<div className="min-h-screen flex items-center justify-center p-4">
<div className="w-full max-w-lg">
<div className="text-center mb-8">
<h1 className="text-3xl font-bold text-primary mb-2">{t("resetPassword")}</h1>

View File

@@ -121,7 +121,7 @@ export default function LoginPage() {
if (hasPassword === null || setupComplete === null) {
return (
<div className="min-h-screen flex flex-col items-center justify-center bg-bg p-6">
<div className="min-h-screen flex flex-col items-center justify-center p-6">
{nodeWarningBanner}
<div className="flex flex-col items-center gap-3">
<div className="relative">
@@ -136,7 +136,7 @@ export default function LoginPage() {
if (!hasPassword && !setupComplete) {
return (
<div className="min-h-screen flex flex-col items-center justify-center bg-bg p-6">
<div className="min-h-screen flex flex-col items-center justify-center p-6">
{nodeWarningBanner}
<div
className={`w-full max-w-md transition-all duration-700 ease-out ${mounted ? "opacity-100 translate-y-0" : "opacity-0 translate-y-4"}`}
@@ -174,7 +174,7 @@ export default function LoginPage() {
if (!hasPassword && setupComplete) {
return (
<div className="min-h-screen flex flex-col items-center justify-center bg-bg p-6">
<div className="min-h-screen flex flex-col items-center justify-center p-6">
{nodeWarningBanner}
<div
className={`w-full max-w-md transition-all duration-700 ease-out ${mounted ? "opacity-100 translate-y-0" : "opacity-0 translate-y-4"}`}
@@ -213,11 +213,11 @@ export default function LoginPage() {
}
return (
<div className="min-h-screen flex flex-col bg-bg">
<div className="min-h-screen flex flex-col">
{nodeWarningBanner && (
<div className="flex justify-center pt-6 px-6">{nodeWarningBanner}</div>
)}
<div className="flex-1 flex bg-bg">
<div className="flex-1 flex">
<div className="flex-1 flex items-center justify-center p-6">
<div
className={`w-full max-w-sm transition-all duration-700 ease-out ${mounted ? "opacity-100 translate-y-0" : "opacity-0 translate-y-4"}`}

View File

@@ -2,7 +2,7 @@ import Link from "next/link";
export default function MaintenancePage() {
return (
<main className="min-h-screen bg-bg text-text-main flex items-center justify-center p-6">
<main className="min-h-screen text-text-main flex items-center justify-center p-6">
<section className="w-full max-w-xl rounded-2xl border border-border bg-surface p-8 shadow-soft text-center">
<span className="material-symbols-outlined text-5xl text-primary mb-3" aria-hidden="true">
construction

View File

@@ -20,7 +20,7 @@ export default function OfflinePage() {
);
return (
<main className="min-h-screen bg-bg text-text-main flex items-center justify-center p-6">
<main className="min-h-screen text-text-main flex items-center justify-center p-6">
<section className="w-full max-w-xl rounded-2xl border border-border bg-surface p-8 shadow-soft text-center">
<span className="material-symbols-outlined text-5xl text-primary mb-3" aria-hidden="true">
wifi_off

View File

@@ -15,7 +15,7 @@ export default function PrivacyPage() {
const t = useTranslations("legal");
return (
<main className="min-h-screen bg-bg text-text-main">
<main className="min-h-screen text-text-main">
<div className="max-w-3xl mx-auto px-6 py-16">
<Link
href="/"

View File

@@ -65,7 +65,7 @@ export default function StatusPage() {
}, [health]);
return (
<main className="min-h-screen bg-bg text-text-main p-6 sm:p-10">
<main className="min-h-screen text-text-main p-6 sm:p-10">
<section className="max-w-4xl mx-auto space-y-6">
<header className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3">
<div>

View File

@@ -15,7 +15,7 @@ export default function TermsPage() {
const t = useTranslations("legal");
return (
<main className="min-h-screen bg-bg text-text-main">
<main className="min-h-screen text-text-main">
<div className="max-w-3xl mx-auto px-6 py-16">
<Link
href="/"

View File

@@ -31,7 +31,7 @@ export default function ErrorPageScaffold({
return (
<main
className="min-h-screen bg-bg text-text-main flex items-center justify-center px-6 py-12"
className="min-h-screen text-text-main flex items-center justify-center px-6 py-12"
role="main"
aria-labelledby="error-page-title"
>

View File

@@ -102,7 +102,10 @@ export default function DashboardLayout({ children }) {
/>
{!isE2EMode && <MaintenanceBanner />}
<div className="flex-1 min-h-0 overflow-y-auto overflow-x-hidden custom-scrollbar p-4 sm:p-6 lg:p-10">
<div className="max-w-7xl mx-auto w-full h-full min-h-0 flex flex-col">
{/* Fluid up to a 4K cap (3840px): content follows the viewport on large
monitors and only centers (side gutters) beyond ~4K, instead of the prior
1280px cap that left big empty margins on wide screens. */}
<div className="max-w-[3840px] mx-auto w-full h-full min-h-0 flex flex-col">
<Breadcrumbs />
<div className="flex-1 min-h-0">
{children}

View File

@@ -183,3 +183,42 @@ test("form controls focus on the accent ring, not the red primary", () => {
}
}
});
// ── Phase 5: the grid reaches every standalone screen + the shell is fluid up to 4K ──
test("standalone full-screen pages stay transparent so the grid shows through", () => {
// Same contract as the DashboardLayout shell: a full-viewport wrapper must not paint
// an opaque bg-bg over the body::before grid. These are the auth / error / legal /
// status / onboarding screens that render outside the dashboard layout — the ones the
// first grid phase missed. (?![\w-]) keeps bg-bg-alt / bg-bg-main from matching.
const pages = [
"../../src/app/login/page.tsx",
"../../src/app/forgot-password/page.tsx",
"../../src/app/callback/page.tsx",
"../../src/app/maintenance/page.tsx",
"../../src/app/offline/page.tsx",
"../../src/app/status/page.tsx",
"../../src/app/terms/page.tsx",
"../../src/app/privacy/page.tsx",
"../../src/app/(dashboard)/dashboard/onboarding/page.tsx",
"../../src/shared/components/ErrorPageScaffold.tsx",
];
for (const p of pages) {
const src = read(p);
assert.ok(
!/min-h-screen[^"]*\bbg-bg(?![\w-])/.test(src) &&
!/\bbg-bg(?![\w-])[^"]*min-h-screen/.test(src),
`${p} must not paint bg-bg on a min-h-screen wrapper (it would hide the grid)`
);
}
});
test("DashboardLayout content shell is fluid up to ~4K before centering", () => {
// The inner content wrapper grows with the viewport up to a 4K cap (3840px) instead
// of the old max-w-7xl (1280px) that left wide side gutters on large monitors.
assert.ok(
dashboardLayout.includes("max-w-[3840px] mx-auto"),
"content wrapper caps at 3840px (4K) and centers only beyond that"
);
assert.ok(!dashboardLayout.includes("max-w-7xl"), "the old 1280px max-w-7xl cap is gone");
});