fix(theme): dark theme + landing toggle now actually applies
build-and-publish / test (push) Has been cancelled
build-and-publish / image (push) Has been cancelled

Two bugs:
1. [data-theme="dark"] block was defined BEFORE :root in source CSS,
   so :root cascade overrode it. Moved to AFTER :root and bumped
   specificity to html[data-theme="dark"] to beat plain :root.
2. Theme toggle button only existed in the topbar, which is hidden
   on landing/login/sso-callback scenes. Added a ThemeToggle to
   the Landing header so users can flip theme from the home page.

Confidence: high
Scope-risk: narrow
This commit is contained in:
2026-06-14 11:52:02 +04:00
parent 6f8832d222
commit f1324b7539
7 changed files with 159 additions and 14 deletions
+23
View File
@@ -0,0 +1,23 @@
import { chromium } from "playwright";
const b = await chromium.launch({
headless: true,
args: ["--host-resolver-rules=MAP canvas.flow-master.ai 65.21.71.186", "--ignore-certificate-errors"],
});
const ctx = await b.newContext({ viewport: { width: 1440, height: 900 } });
const p = await ctx.newPage();
await p.goto("https://canvas.flow-master.ai/", { waitUntil: "networkidle" });
await p.waitForTimeout(1500);
await p.locator("input[type='email']").fill("dev@flow-master.ai");
const devBtn = p.locator("button", { hasText: /developer|dev-login/i });
if (await devBtn.count() > 0) await devBtn.click();
await p.waitForTimeout(3000);
console.log("=== ALL .link-btn buttons ===");
const buttons = await p.$$eval(".link-btn", (els) => els.map((e) => ({
class: e.className,
text: (e.innerText || "").slice(0, 50),
aria: e.getAttribute("aria-label"),
title: e.getAttribute("title"),
})));
buttons.forEach(b => console.log(JSON.stringify(b)));
await b.close();
+29
View File
@@ -0,0 +1,29 @@
import { chromium } from "playwright";
const b = await chromium.launch({
headless: true,
args: ["--host-resolver-rules=MAP canvas.flow-master.ai 65.21.71.186", "--ignore-certificate-errors"],
});
const ctx = await b.newContext({ viewport: { width: 1440, height: 900 } });
const p = await ctx.newPage();
await p.goto("https://canvas.flow-master.ai/", { waitUntil: "networkidle" });
await p.waitForTimeout(2000);
// Sign in first
await p.locator("input[type='email']").fill("dev@flow-master.ai");
const devBtn = p.locator("button", { hasText: /developer|dev-login/i });
if (await devBtn.count() > 0) await devBtn.click();
await p.waitForTimeout(2500);
console.log("after login theme:", await p.evaluate(() => document.documentElement.dataset.theme));
console.log("after login body bg:", await p.evaluate(() => getComputedStyle(document.body).backgroundColor));
const toggle = p.locator(".theme-toggle, .link-btn.theme-toggle, button[aria-label*='theme']").first();
const count = await toggle.count();
console.log("toggle count:", count);
if (count > 0) {
await toggle.click();
await p.waitForTimeout(800);
console.log("after click theme:", await p.evaluate(() => document.documentElement.dataset.theme));
console.log("after click body bg:", await p.evaluate(() => getComputedStyle(document.body).backgroundColor));
}
await b.close();
+28
View File
@@ -0,0 +1,28 @@
import { chromium } from "playwright";
const b = await chromium.launch({
headless: true,
args: ["--host-resolver-rules=MAP canvas.flow-master.ai 65.21.71.186", "--ignore-certificate-errors"],
});
const ctx = await b.newContext({ viewport: { width: 1440, height: 900 } });
const p = await ctx.newPage();
await p.goto("https://canvas.flow-master.ai/", { waitUntil: "networkidle" });
await p.waitForTimeout(1500);
// Get the computed --bp-paper at root
console.log("html dataset.theme:", await p.evaluate(() => document.documentElement.dataset.theme));
console.log("--bp-paper (root):", await p.evaluate(() => getComputedStyle(document.documentElement).getPropertyValue("--bp-paper")));
console.log("body background:", await p.evaluate(() => getComputedStyle(document.body).backgroundColor));
// Force toggle data-theme via JS
await p.evaluate(() => document.documentElement.dataset.theme = "dark");
await p.waitForTimeout(200);
console.log("after force dark:");
console.log(" --bp-paper:", await p.evaluate(() => getComputedStyle(document.documentElement).getPropertyValue("--bp-paper")));
console.log(" body bg:", await p.evaluate(() => getComputedStyle(document.body).backgroundColor));
await p.evaluate(() => document.documentElement.dataset.theme = "light");
await p.waitForTimeout(200);
console.log("after force light:");
console.log(" --bp-paper:", await p.evaluate(() => getComputedStyle(document.documentElement).getPropertyValue("--bp-paper")));
console.log(" body bg:", await p.evaluate(() => getComputedStyle(document.body).backgroundColor));
await b.close();
+28
View File
@@ -0,0 +1,28 @@
import { chromium } from "playwright";
const b = await chromium.launch({
headless: true,
args: ["--host-resolver-rules=MAP canvas.flow-master.ai 65.21.71.186", "--ignore-certificate-errors"],
});
const ctx = await b.newContext({ viewport: { width: 1440, height: 900 } });
const p = await ctx.newPage();
await p.goto("https://canvas.flow-master.ai/", { waitUntil: "domcontentloaded" });
await p.waitForLoadState("networkidle");
await p.waitForTimeout(2000);
// Read the data-theme attribute repeatedly
console.log("docElement.dataset.theme:", await p.evaluate(() => document.documentElement.dataset.theme));
console.log("docElement attributes:", await p.evaluate(() => document.documentElement.getAttributeNames().join(",")));
console.log("html outerHTML head:", await p.evaluate(() => document.documentElement.outerHTML.slice(0, 200)));
console.log("computed --bp-paper:", await p.evaluate(() => getComputedStyle(document.documentElement).getPropertyValue("--bp-paper")));
// Try setting dark via JS and reading
await p.evaluate(() => {
document.documentElement.setAttribute("data-theme", "dark");
});
await p.waitForTimeout(500);
console.log("\nafter setAttribute('data-theme','dark'):");
console.log("dataset.theme:", await p.evaluate(() => document.documentElement.dataset.theme));
console.log("--bp-paper:", await p.evaluate(() => getComputedStyle(document.documentElement).getPropertyValue("--bp-paper")));
console.log("body bg:", await p.evaluate(() => getComputedStyle(document.body).backgroundColor));
await b.close();
+17
View File
@@ -0,0 +1,17 @@
import { chromium } from "playwright";
const b = await chromium.launch({
headless: true,
args: ["--host-resolver-rules=MAP canvas.flow-master.ai 65.21.71.186", "--ignore-certificate-errors"],
});
const ctx = await b.newContext({ viewport: { width: 1440, height: 900 } });
const p = await ctx.newPage();
await p.goto("https://canvas.flow-master.ai/", { waitUntil: "networkidle" });
await p.waitForTimeout(1500);
await p.locator("input[type='email']").fill("dev@flow-master.ai");
const devBtn = p.locator("button", { hasText: /developer|dev-login/i });
if (await devBtn.count() > 0) await devBtn.click();
await p.waitForTimeout(3000);
console.log("topbar exists:", await p.locator(".topbar").count());
console.log("topbar HTML:");
console.log(await p.evaluate(() => document.querySelector(".topbar")?.outerHTML?.slice(0, 2000)));
await b.close();