Files
frontend/src/shared/theme/contrast.test.ts
T
ksv741 f6db28defa
Frontend security / security (push) Successful in 10s
Frontend verify / verify (push) Successful in 4m57s
Conventional commits / commits (pull_request) Successful in 28s
Frontend security / security (pull_request) Successful in 10s
Frontend verify / verify (pull_request) Successful in 5m10s
fix: resolve frontend audit findings
Repair investment recovery, draft preservation, date validation,
currency presentation, accessibility, and route error recovery.
Correct Docker and dependency checks and consume explicit income roles.
2026-09-27 11:38:20 +03:00

51 lines
1.5 KiB
TypeScript

import tokens from "@wealthledger/tokens/tokens.json";
import { describe, expect, it } from "vitest";
function luminance(color: string) {
const channels = [1, 3, 5]
.map((index) => Number.parseInt(color.slice(index, index + 2), 16) / 255)
.map((value) =>
value <= 0.04045 ? value / 12.92 : ((value + 0.055) / 1.055) ** 2.4,
);
return channels[0] * 0.2126 + channels[1] * 0.7152 + channels[2] * 0.0722;
}
describe("semantic text contrast", () => {
it.each(["positive", "negative", "muted"] as const)(
"keeps %s readable on canvas, white, subtle and tinted surfaces",
(tone) => {
const role = tokens.color.roles[tone];
const palettes = tokens.color.palettes;
const foreground =
palettes[role.color as keyof typeof palettes][role.shade];
const surfaces = [
"#ffffff",
palettes.gray[0],
palettes.gray[2],
...Object.entries(palettes)
.filter(([name]) => name !== "dark")
.map(([, palette]) => palette[0]),
...Object.entries(palettes)
.filter(([name]) => name !== "dark")
.map(
([, palette]) =>
`#${[1, 3, 5]
.map((index) =>
Math.round(
Number.parseInt(palette[0].slice(index, index + 2), 16) *
0.45 +
255 * 0.55,
)
.toString(16)
.padStart(2, "0"),
)
.join("")}`,
),
];
for (const background of surfaces)
expect(
(luminance(background) + 0.05) / (luminance(foreground) + 0.05),
).toBeGreaterThanOrEqual(4.5);
},
);
});