The screenshots said seven dark screens differed and could not say why. Comparing COMPUTED theme variables between the old build and the new one, in the same colour mode, answered it in a single run: 47 of 53 matched exactly, and the six that did not were the `--duck-*` variables — EMPTY in the new build. The utilities were remapped to `--brand-primary`, `--ink` and friends, which is correct. What that missed is code reading the VARIABLE rather than using the utility. `prose.css` does exactly that eleven times — chat markdown links, blockquotes, inline code, checkboxes — and `landing/styles.css` does it six more. All silently resolved to nothing. Fixed by keeping the legacy names as aliases in every theme, pointing at the new source of truth rather than duplicating values. That also covers any plugin or inline style doing the same thing, which I have not enumerated. They retire when the utility rename lands. Every variable now matches. ── What the screenshots are actually worth here ── Less than I assumed. The wallpaper rotates per page load, so a control capture of the same build against itself had 4 of 16 screens differ — and a different 4 each time. Between 5 and 6 come out byte-identical to the baseline on any run; the rest is noise I mistook for signal for two rounds, including one where I removed a `color-scheme` line to test a theory it turned out not to explain. The variable diff is the instrument that settles it, and it is cheap. Added as `scripts/theme-vardiff.ts`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
112 lines
3.3 KiB
TypeScript
112 lines
3.3 KiB
TypeScript
/**
|
|
* Compare the computed theme variables of two running builds.
|
|
*
|
|
* Screenshots say THAT something changed; this says WHICH VARIABLE changed, which is the question worth
|
|
* asking after an extraction. Seven dark screens differed between the old stylesheet and the new one, and
|
|
* staring at two near-identical dark screenshots is not a way to find a hue that moved by 2%.
|
|
*
|
|
* bun scripts/theme-vardiff.ts <token> [oldUrl] [newUrl]
|
|
*/
|
|
import { chromium } from 'playwright';
|
|
|
|
const token = process.argv[2];
|
|
if (!token) {
|
|
console.log(' usage: bun scripts/theme-vardiff.ts <jwt> [oldUrl] [newUrl]');
|
|
process.exit(1);
|
|
}
|
|
const oldUrl = process.argv[3] ?? 'http://127.0.0.1:9000';
|
|
const newUrl = process.argv[4] ?? 'http://127.0.0.1:9010';
|
|
|
|
const VARS = [
|
|
'--background',
|
|
'--foreground',
|
|
'--card',
|
|
'--card-foreground',
|
|
'--popover',
|
|
'--popover-foreground',
|
|
'--primary',
|
|
'--primary-foreground',
|
|
'--secondary',
|
|
'--secondary-foreground',
|
|
'--muted',
|
|
'--muted-foreground',
|
|
'--accent',
|
|
'--accent-foreground',
|
|
'--destructive',
|
|
'--destructive-foreground',
|
|
'--success',
|
|
'--success-foreground',
|
|
'--warning',
|
|
'--warning-foreground',
|
|
'--info',
|
|
'--info-foreground',
|
|
'--border',
|
|
'--input',
|
|
'--ring',
|
|
'--brand',
|
|
'--brand-foreground',
|
|
'--brand-muted',
|
|
'--radius',
|
|
'--cta',
|
|
'--cta-foreground',
|
|
'--ctahover',
|
|
'--naturegreen',
|
|
'--naturegreen-foreground',
|
|
'--duck-teal',
|
|
'--duck-dark',
|
|
'--duck-yellow',
|
|
'--duck-forest',
|
|
'--duck-beige',
|
|
'--duck-orange',
|
|
'--glass-bg',
|
|
'--glass-border',
|
|
'--dock-bg',
|
|
'--dock-border',
|
|
'--dock-tooltip-bg',
|
|
'--card-bg',
|
|
'--card-grid-color',
|
|
'--pixel-grid-color',
|
|
'--chart-primary',
|
|
'--chart-secondary',
|
|
'--status-success',
|
|
'--status-warning',
|
|
'--status-in-progress',
|
|
];
|
|
|
|
const browser = await chromium.launch();
|
|
|
|
async function read(url: string, extra: string): Promise<Record<string, string>> {
|
|
const context = await browser.newContext({ viewport: { width: 1000, height: 700 } });
|
|
await context.addInitScript(`localStorage.setItem('BEARER_TOKEN', ${JSON.stringify(token)}); ${extra}`);
|
|
const page = await context.newPage();
|
|
// `commit` rather than `load`: this app holds websockets open, so waiting for a quiet network can hang.
|
|
await page.goto(`${url}/jobs`, { waitUntil: 'commit', timeout: 20_000 });
|
|
await page.waitForTimeout(2500);
|
|
const values = await page.evaluate((vars: string[]) => {
|
|
const cs = getComputedStyle(document.documentElement);
|
|
const out: Record<string, string> = {};
|
|
for (const v of vars) out[v] = cs.getPropertyValue(v).trim();
|
|
return out;
|
|
}, VARS);
|
|
await context.close();
|
|
return values;
|
|
}
|
|
|
|
// The OLD build spoke in colour modes; the NEW one in themes. Same appearance, different vocabulary — which
|
|
// is the entire refactor, and why the two sides are set up differently here.
|
|
const before = await read(oldUrl, `localStorage.setItem('officer-color-mode','dark');`);
|
|
const after = await read(newUrl, `localStorage.setItem('officer-theme','duck-dark');`);
|
|
await browser.close();
|
|
|
|
let differing = 0;
|
|
for (const key of VARS) {
|
|
const a = before[key] ?? '';
|
|
const b = after[key] ?? '';
|
|
if (a !== b) {
|
|
console.log(` DIFF ${key.padEnd(24)} old=${JSON.stringify(a)} new=${JSON.stringify(b)}`);
|
|
differing += 1;
|
|
}
|
|
}
|
|
console.log(differing === 0 ? ' every variable matches' : ` ${differing} of ${VARS.length} differ`);
|
|
process.exit(0);
|