diff --git a/e2e/theme-flash.spec.ts b/e2e/theme-flash.spec.ts new file mode 100644 index 000000000..cbd3ee120 --- /dev/null +++ b/e2e/theme-flash.spec.ts @@ -0,0 +1,30 @@ +import { test, expect } from "./fixtures"; + +// issue #1497:暗色系统下页面初次加载时,JS 设置 arco-theme 前 var(--color-bg-2) 无定义,出现白屏闪烁。 +// 通过 CDP 禁用脚本执行来固定"首帧"(React 挂载前)状态,验证模板内联 CSS 的暗色兜底背景。 +test.describe("暗色模式首屏闪烁 (issue #1497)", () => { + // options.html / popup.html / install.html 分别对应 options、popup、template 三个 HTML 模板 + for (const pageName of ["options.html", "popup.html", "install.html"]) { + test(`系统暗色模式下 ${pageName} 首帧应为暗色背景`, async ({ context, extensionId }) => { + const page = await context.newPage(); + await page.emulateMedia({ colorScheme: "dark" }); + const cdp = await context.newCDPSession(page); + await cdp.send("Emulation.setScriptExecutionDisabled", { value: true }); + await page.goto(`chrome-extension://${extensionId}/src/${pageName}`); + const bg = await page.evaluate(() => getComputedStyle(document.body).backgroundColor); + expect(bg).toBe("rgb(35, 35, 36)"); + await page.close(); + }); + } + + test("系统亮色模式下 options.html 首帧不应为暗色背景", async ({ context, extensionId }) => { + const page = await context.newPage(); + await page.emulateMedia({ colorScheme: "light" }); + const cdp = await context.newCDPSession(page); + await cdp.send("Emulation.setScriptExecutionDisabled", { value: true }); + await page.goto(`chrome-extension://${extensionId}/src/options.html`); + const bg = await page.evaluate(() => getComputedStyle(document.body).backgroundColor); + expect(bg).not.toBe("rgb(35, 35, 36)"); + await page.close(); + }); +}); diff --git a/src/pages/options.html b/src/pages/options.html index 3e3c3840a..b5cdca2f5 100644 --- a/src/pages/options.html +++ b/src/pages/options.html @@ -14,6 +14,12 @@ background-color: var(--color-bg-2); color: var(--color-text-1); } + /* 暗色系统下,JS 设置 arco-theme 前的兜底背景,避免白屏闪烁 (issue #1497) */ + @media (prefers-color-scheme: dark) { + body:not([arco-theme]) { + background-color: #232324; + } + } diff --git a/src/pages/popup.html b/src/pages/popup.html index dfccc4a64..96a5c5c08 100644 --- a/src/pages/popup.html +++ b/src/pages/popup.html @@ -17,6 +17,12 @@ min-height: 150px; max-height: 500px; } + /* 暗色系统下,JS 设置 arco-theme 前的兜底背景,避免白屏闪烁 (issue #1497) */ + @media (prefers-color-scheme: dark) { + body:not([arco-theme]) { + background-color: #232324; + } + } diff --git a/src/pages/store/AppContext.test.tsx b/src/pages/store/AppContext.test.tsx new file mode 100644 index 000000000..80e4d74bd --- /dev/null +++ b/src/pages/store/AppContext.test.tsx @@ -0,0 +1,37 @@ +import { describe, it, expect, beforeAll, afterEach } from "vitest"; +import { render, setupGlobalMocks } from "@Tests/test-utils"; + +// AppProvider 挂载时根据 localStorage.lightMode 初始化主题。 +// body 上的 arco-theme 属性必须始终被显式设置(light 也不例外), +// 模板内联 CSS 依赖 body:not([arco-theme]) 识别"主题未初始化"状态来做暗色兜底(issue #1497)。 +describe("AppContext 颜色主题初始化", () => { + beforeAll(() => { + setupGlobalMocks(); + }); + + afterEach(() => { + localStorage.removeItem("lightMode"); + document.body.removeAttribute("arco-theme"); + document.documentElement.classList.remove("dark"); + }); + + it("dark 模式下应在 body 上设置 arco-theme=dark", () => { + localStorage.lightMode = "dark"; + render(
); + expect(document.body.getAttribute("arco-theme")).toBe("dark"); + expect(document.documentElement.classList.contains("dark")).toBe(true); + }); + + it("light 模式下应在 body 上显式设置 arco-theme=light,以区分主题未初始化状态", () => { + localStorage.lightMode = "light"; + render(
); + expect(document.body.getAttribute("arco-theme")).toBe("light"); + expect(document.documentElement.classList.contains("dark")).toBe(false); + }); + + it("auto 模式且系统为亮色时也应显式标记 arco-theme=light", () => { + localStorage.lightMode = "auto"; + render(
); + expect(document.body.getAttribute("arco-theme")).toBe("light"); + }); +}); diff --git a/src/pages/store/AppContext.tsx b/src/pages/store/AppContext.tsx index 7c325df81..cb0ceb03f 100644 --- a/src/pages/store/AppContext.tsx +++ b/src/pages/store/AppContext.tsx @@ -54,7 +54,9 @@ const setAppColorTheme = (theme: "light" | "dark" | "auto") => { break; case "light": document.documentElement.classList.remove("dark"); - document.body.removeAttribute("arco-theme"); + // 显式设置 light 而不是移除属性:模板内联 CSS 依赖 body:not([arco-theme]) + // 识别"主题未初始化"状态来做暗色兜底,避免白屏闪烁 (issue #1497) + document.body.setAttribute("arco-theme", "light"); fnPlaceHolder.setEditorTheme?.("vs"); break; } diff --git a/src/pages/template.html b/src/pages/template.html index 89a30c56d..97a3fa7f1 100644 --- a/src/pages/template.html +++ b/src/pages/template.html @@ -16,6 +16,12 @@ background-color: var(--color-bg-2); color: var(--color-text-1); } + /* 暗色系统下,JS 设置 arco-theme 前的兜底背景,避免白屏闪烁 (issue #1497) */ + @media (prefers-color-scheme: dark) { + body:not([arco-theme]) { + background-color: #232324; + } + }