import { readFileSync } from "node:fs"; import path from "node:path"; import { describe, expect, it } from "vitest"; import source from "../src/lib/tokens.json"; function luminance(hex: string) { const channels = hex.match(/[a-f\d]{2}/gi)!.map((value) => parseInt(value, 16) / 255); const linear = channels.map((value) => value <= 0.04045 ? value / 12.92 : ((value + 0.055) / 1.055) ** 2.4); return 0.2126 * linear[0] + 0.7152 * linear[1] + 0.0722 * linear[2]; } function contrast(foreground: string, background: string) { const values = [luminance(foreground), luminance(background)].sort((a, b) => b - a); return (values[0] + 0.05) / (values[1] + 0.05); } describe("brand tokens", () => { it.each(["dark", "light"] as const)("%s theme meets normal-text contrast targets", (mode) => { const colors = source.color[mode]; expect(contrast(colors.text, colors.bg)).toBeGreaterThanOrEqual(4.5); expect(contrast(colors.muted, colors.bg)).toBeGreaterThanOrEqual(4.5); expect(contrast(colors.brandForeground, colors.brand)).toBeGreaterThanOrEqual(4.5); expect(contrast(colors.successForeground, colors.success)).toBeGreaterThanOrEqual(4.5); expect(contrast(colors.warningForeground, colors.warning)).toBeGreaterThanOrEqual(4.5); expect(contrast(colors.dangerForeground, colors.danger)).toBeGreaterThanOrEqual(4.5); expect(contrast(colors.infoForeground, colors.info)).toBeGreaterThanOrEqual(4.5); }); it("keeps generated CSS synchronized with the canonical source", () => { const generated = readFileSync(path.resolve("src/styles/theme.generated.css"), "utf8"); expect(generated).toContain("--brand-foreground:"); expect(generated).toContain(":root, .dark"); expect(generated).toContain(".light"); }); });