refactor: tighten types and reuse instrument UI
This commit is contained in:
@@ -61,14 +61,13 @@ const ColorsView: FC = () => {
|
||||
))}
|
||||
</SimpleGrid>
|
||||
|
||||
{Object.keys(designTokens.color.palettes).map(palette => (
|
||||
{Object.entries(designTokens.color.palettes).map(([palette, colors]) => (
|
||||
<Stack gap="xs" key={palette}>
|
||||
<Title order={2}>{palette}</Title>
|
||||
|
||||
<SimpleGrid cols={{ base: 2, lg: 10, sm: 5 }}>
|
||||
{
|
||||
// eslint-disable-next-line security/detect-object-injection -- This is a positional index into the local array, not a user-supplied object key.
|
||||
theme.colors[palette].map((value, shade) => (
|
||||
colors.map((value, shade) => (
|
||||
<Paper key={value} p="xs" withBorder>
|
||||
<Box aria-hidden="true" bg={value} h={40} />
|
||||
|
||||
|
||||
@@ -1,14 +1,22 @@
|
||||
import tokens from "@wealthledger/tokens/tokens.json";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { assert, 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,
|
||||
);
|
||||
let result = 0;
|
||||
|
||||
return channels[0] * 0.2126 + channels[1] * 0.7152 + channels[2] * 0.0722;
|
||||
for (const [index, weight] of [0.2126, 0.7152, 0.0722].entries()) {
|
||||
const channel = Number.parseInt(
|
||||
color.slice(1 + index * 2, 3 + index * 2), 16,
|
||||
) / 255;
|
||||
|
||||
const linear = channel <= 0.04045
|
||||
? channel / 12.92
|
||||
: ((channel + 0.055) / 1.055) ** 2.4;
|
||||
|
||||
result += linear * weight;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
describe("semantic text contrast", () => {
|
||||
@@ -24,6 +32,8 @@ describe("semantic text contrast", () => {
|
||||
const foreground
|
||||
= palettes[role.color as keyof typeof palettes][role.shade];
|
||||
|
||||
assert.isDefined(foreground);
|
||||
|
||||
const surfaces = [
|
||||
"#ffffff",
|
||||
palettes.gray[0],
|
||||
@@ -34,25 +44,31 @@ describe("semantic text contrast", () => {
|
||||
...Object.entries(palettes)
|
||||
.filter(([name]) => name !== "dark")
|
||||
.map(
|
||||
([, palette]) =>
|
||||
`#${[1, 3, 5]
|
||||
([, palette]) => {
|
||||
const base = palette[0];
|
||||
assert.isDefined(base);
|
||||
|
||||
return `#${[1, 3, 5]
|
||||
.map(index =>
|
||||
Math.round(
|
||||
Number.parseInt(palette[0].slice(index, index + 2), 16)
|
||||
Number.parseInt(base.slice(index, index + 2), 16)
|
||||
* 0.45
|
||||
+ 255 * 0.55,
|
||||
)
|
||||
.toString(16)
|
||||
.padStart(2, "0"),
|
||||
)
|
||||
.join("")}`,
|
||||
.join("")}`;
|
||||
},
|
||||
),
|
||||
];
|
||||
|
||||
for (const background of surfaces)
|
||||
for (const background of surfaces) {
|
||||
assert.isDefined(background);
|
||||
expect(
|
||||
(luminance(background) + 0.05) / (luminance(foreground) + 0.05),
|
||||
).toBeGreaterThanOrEqual(4.5);
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user