Files
hanzo.ai/e2e/gates/scheme.spec.ts
zeekay b54fe45c68 Follow the device's light or dark scheme, and draw /zen's generations as rules
A reader who had never chosen a theme got dark whatever the device said: the
head's boot script was seeded with `dark`. The install's default is now
`system` (lib/appearance.ts), painted before the first frame, and the client
applies it once mounted, which is what installs the listener that follows the
device when it changes.

/zen's generations, the timeline marks and the Train/Attest/Evaluate/Release
underlines set `borderStyle` with one side's width, so the other three sides
drew at the browser's medium width: every generation was a 3px box with its
text against the edge. Each now sets `borderWidth={0}` before its side.

Gate e2e/gates/scheme.spec.ts: a first visit under a light and a dark device
paints that scheme and follows a change, and /zen's generations read
1px 0 0 0. The look gate still passes.

Claude-Session: https://claude.ai/code/session_01SCPMGSQsLNF2YtHFL4bwyp
2026-10-08 11:52:12 -07:00

53 lines
2.2 KiB
TypeScript

import { test, expect } from '@playwright/test'
import { serveExport } from './export'
/**
* A READER WHO HAS NEVER CHOSEN A THEME GETS THEIR DEVICE'S.
*
* The boot script paints `light`/`t_light` or `dark`/`t_dark` before the first
* frame from the stored choice, else from `prefers-color-scheme` (the install's
* default is `system`, app/layout.tsx). Read off the class gui's colours hang
* on, on a fresh profile, for both schemes, and again after the device changes
* its mind: `system` keeps following it.
*
* And /zen's generations are divided by a rule above each, not boxed: a side
* given a width beside an explicit `borderStyle` draws the other three sides at
* the browser's medium width, which is what boxed them.
*/
test.describe('scheme', () => {
let site: Awaited<ReturnType<typeof serveExport>>
test.beforeAll(async () => {
site = await serveExport()
})
test.afterAll(async () => {
await site.close()
})
for (const scheme of ['light', 'dark'] as const) {
const other = scheme === 'light' ? 'dark' : 'light'
test(`a first visit under a ${scheme} device paints ${scheme}, and follows it`, async ({ page }) => {
await page.emulateMedia({ colorScheme: scheme })
for (const path of ['/', '/zen']) {
await page.goto(`${site.url}${path}`)
await expect.poll(() => page.evaluate(() => document.documentElement.className)).toContain(`t_${scheme}`)
expect(await page.evaluate(() => getComputedStyle(document.documentElement).colorScheme)).toBe(scheme)
}
await page.emulateMedia({ colorScheme: other })
await expect.poll(() => page.evaluate(() => document.documentElement.className)).toContain(`t_${other}`)
})
}
test('/zen divides its generations with a rule, not a box', async ({ page }) => {
await page.goto(`${site.url}/zen`)
const sides = await page.locator('[data-gen]').evaluateAll((els) =>
els.map((el) => {
const c = getComputedStyle(el)
return [c.borderTopWidth, c.borderRightWidth, c.borderBottomWidth, c.borderLeftWidth].join(' ')
}),
)
expect(sides.length).toBeGreaterThan(5)
for (const s of sides) expect(s).toBe('1px 0px 0px 0px')
})
})