36 lines
1.7 KiB
TypeScript
36 lines
1.7 KiB
TypeScript
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");
|
|
});
|
|
});
|