mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-31 04:12:10 +03:00
feat(dashboard): grid wallpaper on all standalone screens + fluid 4K layout (#4158)
Integrated into release/v3.8.29
This commit is contained in:
committed by
GitHub
parent
a0e7e92e64
commit
e0fe4facdf
@@ -1,6 +1,6 @@
|
||||
# OmniRoute — Design System & Visual Identity
|
||||
|
||||
> **Status:** standardization plan. **Phases 1–3 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 1–3 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).
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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" && (
|
||||
<>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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"}`}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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="/"
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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="/"
|
||||
|
||||
@@ -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"
|
||||
>
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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");
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user