* Restyle widget cards in solid gray on one shared palette Cards, number boxes and off|on switches take their colors, font and radii from js/shared/theme.mjs. Controls are solid gray with no outlines on a near-black card, grays are neutral, faint text holds 4.5:1, and number boxes in a card are as tall as the row. A test keeps colour literals out of the three modules and checks text contrast. * Lift the card toward the node colour and brighten the off mark * Restyle the picture bar, its stepper and switch on the shared palette * Darken the card a step * Picture frame back to the lighter well; changelog covers the picture bar
44 lines
1.8 KiB
JavaScript
44 lines
1.8 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import { readFileSync } from "node:fs";
|
|
import test from "node:test";
|
|
|
|
import { THEME } from "../js/shared/theme.mjs";
|
|
|
|
// The card, the number box, the switch and the picture bar take every colour
|
|
// from theme.mjs, so the palette is changed in one place.
|
|
const ON_THEME = [
|
|
"js/shared/widget_card.mjs", "js/shared/scrub_input.mjs", "js/shared/slide_switch.mjs",
|
|
"js/input_preview/index.js", "js/shared/panel_controls.mjs", "js/shared/picture_stepper.mjs",
|
|
"js/shared/step_preview.mjs",
|
|
];
|
|
|
|
test("the card, number box, switch and picture bar carry no colour of their own", () => {
|
|
for (const file of ON_THEME) {
|
|
const source = readFileSync(new URL(`../${file}`, import.meta.url), "utf8");
|
|
const hex = source.match(/#[0-9a-fA-F]{3,8}\b/g) ?? [];
|
|
assert.deepEqual(hex, [], `${file} has colour literals; add them to js/shared/theme.mjs`);
|
|
}
|
|
});
|
|
|
|
const luminance = (hex) => {
|
|
const channels = [1, 3, 5].map((i) => parseInt(hex.slice(i, i + 2), 16) / 255)
|
|
.map((c) => (c <= 0.03928 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4));
|
|
return 0.2126 * channels[0] + 0.7152 * channels[1] + 0.0722 * channels[2];
|
|
};
|
|
const contrast = (a, b) => {
|
|
const [hi, lo] = [luminance(a), luminance(b)].sort((x, y) => y - x);
|
|
return (hi + 0.05) / (lo + 0.05);
|
|
};
|
|
|
|
test("text a person reads holds 4.5:1 on the card and on a control", () => {
|
|
const pairs = [
|
|
["ink", "fill"], ["inkSoft", "card"], ["label", "card"], ["label", "fill"],
|
|
["faint", "card"], ["faint", "fill"], ["onBrand", "brand"], ["ink", "switchOff"], ["warnInk", "warnSoft"],
|
|
["dangerInk", "dangerSoft"], ["faint", "well"], ["inkSoft", "fill"],
|
|
];
|
|
for (const [text, ground] of pairs) {
|
|
const ratio = contrast(THEME[text], THEME[ground]);
|
|
assert.ok(ratio >= 4.5, `${text} on ${ground} is ${ratio.toFixed(2)}:1`);
|
|
}
|
|
});
|