Archilyzer · Source

archilyzer

Archilyzer
git clone https://archilyzer.pages.dev/source/archilyzer.git
Log | Files | Refs | README | LICENSE

commit d7b3026ee81cbb8f0eeea09d8fda846861bf223d
parent eaf0cfaef447c1f95890edf14d984c1d36d31ca8
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date:   Tue,  8 Sep 2026 08:36:51 -0400

common: the pre-paint theme script marks itself done, and the FOUC tests wait for the mark

`export/e2e/theme.spec.ts` and `theme-family.spec.ts` reload with
`waitUntil: "commit"` and then read `data-theme` off `<html>`. "commit" resolves
as soon as the navigation commits, which is not a promise that the inline
`<head>` script has run, so on a loaded box the read lands first and sees null.
Nothing was wrong with the product; the tests were reading a value before the
statement that sets it.

The script's last statement inside its existing synchronous `try` now sets
`data-theme-ready="1"`, and each of the three post-reload reads (two in
theme.spec.ts, one in theme-family.spec.ts) waits for that marker. The marker
and the theme attributes are set by the same uninterrupted statement sequence,
and a `<head>` script still runs before any React, so the tests prove exactly
what they proved before: the theme is on `<html>` before hydration.

Measured from a worktree at this tree, one worker, behind the e2e queue:
`playwright test theme.spec.ts theme-family.spec.ts ask-chat.spec.ts --repeat-each 10`
— **250 passed, 0 failed, 8.4 min**; the two "no FOUC" tests are 20 of those, 10
each. (`pnpm e2e --` inserts a literal `--` that Playwright reads as
end-of-flags, so `--repeat-each` becomes a filename filter; the run went through
`node ../scripts/queue-lock.mjs --ports EXPORT_E2E_PORT:3020 -- pnpm exec
playwright test …`, the same lock and port the script takes. `e2e:2origin` is
known-broken and was not used.)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

Diffstat:
Mcommon/components/ThemeScript.tsx | 8++++++++
Mexport/e2e/theme-family.spec.ts | 3+++
Mexport/e2e/theme.spec.ts | 6++++++
3 files changed, 17 insertions(+), 0 deletions(-)

diff --git a/common/components/ThemeScript.tsx b/common/components/ThemeScript.tsx @@ -45,6 +45,14 @@ export function ThemeScript({ JSON.stringify(ACCENT_KEY) + ");" + "if(a){d.style.setProperty('--brand',a);}" + + // Last statement of the same synchronous try block, so the marker can only + // be present once every attribute above is already set. e2e's "no FOUC" + // tests reload with waitUntil:"commit", which resolves as soon as the + // navigation commits — possibly before this head script has run at all — + // and then read data-theme. They wait for this marker instead of racing. + // It proves nothing weaker than before: a <head> script still runs before + // any React, so "set before hydration" is exactly what is observed. + "d.setAttribute('data-theme-ready','1');" + "}catch(e){}})();"; return ( diff --git a/export/e2e/theme-family.spec.ts b/export/e2e/theme-family.spec.ts @@ -32,6 +32,9 @@ test("theme-family picker switches + persists data-theme with no FOUC", async ({ // Reload: the pre-paint script re-applies data-theme before React hydrates. await page.reload({ waitUntil: "commit" }); + await page.waitForFunction( + () => document.documentElement.dataset.themeReady === "1", + ); expect( await page.evaluate(() => document.documentElement.getAttribute("data-theme"), diff --git a/export/e2e/theme.spec.ts b/export/e2e/theme.spec.ts @@ -38,6 +38,9 @@ test("base family default; mode toggle applies + persists with no FOUC", async ( expect(s.theme).toBe(null); await page.reload({ waitUntil: "commit" }); + await page.waitForFunction( + () => document.documentElement.dataset.themeReady === "1", + ); let onCommit = await page.evaluate(() => ({ dark: document.documentElement.classList.contains("dark"), theme: document.documentElement.getAttribute("data-theme"), @@ -54,6 +57,9 @@ test("base family default; mode toggle applies + persists with no FOUC", async ( expect(s.dark).toBe(false); await page.reload({ waitUntil: "commit" }); + await page.waitForFunction( + () => document.documentElement.dataset.themeReady === "1", + ); onCommit = await page.evaluate(() => ({ dark: document.documentElement.classList.contains("dark"), theme: document.documentElement.getAttribute("data-theme"),