Release Colour Tools 0.2.0
Verify / verify (push) Canceled after 0s

This commit is contained in:
2026-09-02 07:11:11 +02:00
parent 0c1fc94b58
commit ed030cada6
34 changed files with 1446 additions and 74 deletions
+164 -3
View File
@@ -2,11 +2,15 @@ import { describe, expect, it } from "vitest";
import {
colourHarmony,
buildCssGradient,
deltaE,
exportGradientRecipe,
exportPalette,
formatColour,
normaliseTokenName,
parsePaletteList,
parseDtcgTokens,
paletteContrastMatrix,
shades,
simulateColourVision,
simulateColourVisionSet,
@@ -99,13 +103,170 @@ describe("palette construction and export", () => {
paper: "#f8f5ee",
ink: "#111111",
});
expect(JSON.parse(exportPalette(entries, "tokens")).paper).toEqual({
$type: "color",
$value: "#f8f5ee",
expect(JSON.parse(exportPalette(entries, "tokens"))).toMatchObject({
$schema: "https://www.designtokens.org/schemas/2025.10/format.json",
paper: {
$type: "color",
$value: {
colorSpace: "srgb",
components: expect.any(Array),
hex: "#f8f5ee",
},
},
});
expect(exportPalette(entries, "tailwind")).toMatch(/^export default /);
expect(exportPalette(entries, "csv")).toBe(
"name,value\npaper,#f8f5ee\nink,#111111",
);
});
it("imports DTCG 2025.10 structured colours and local aliases", () => {
const result = parseDtcgTokens(
JSON.stringify({
$schema: "https://www.designtokens.org/schemas/2025.10/format.json",
base: {
$type: "color",
blue: {
$value: {
colorSpace: "display-p3",
components: [0.2, 0.4, 0.8],
alpha: 0.75,
hex: "#3366cc",
},
},
},
semantic: {
primary: { $type: "color", $value: "{base.blue}" },
},
}),
);
expect(result.entries.map((entry) => entry.name)).toEqual([
"base-blue",
"semantic-primary",
]);
expect(result.diagnostics).toEqual([]);
expect(formatColour(result.entries[0]!.colour, "hex8")).toMatch(
/^#[0-9a-f]{8}$/u,
);
});
it("reports missing DTCG types instead of guessing them", () => {
const result = parseDtcgTokens(
'{"untyped":{"$value":{"colorSpace":"srgb","components":[1,0,0]}}}',
);
expect(result.entries).toEqual([]);
expect(result.diagnostics[0]).toMatchObject({ severity: "error" });
});
it("inherits a DTCG alias type and rejects out-of-range components", () => {
const alias = parseDtcgTokens(
JSON.stringify({
base: {
$type: "color",
$value: { colorSpace: "srgb", components: [1, 0, 0] },
},
alias: { $value: "{base}" },
}),
);
expect(alias.entries.map((entry) => entry.name)).toEqual(["base", "alias"]);
expect(alias.diagnostics).toEqual([]);
const invalid = parseDtcgTokens(
JSON.stringify({
bad: {
$type: "color",
$value: { colorSpace: "display-p3", components: [1.2, 0, 0] },
},
}),
);
expect(invalid.entries).toEqual([]);
expect(invalid.diagnostics[0]?.message).toMatch(/from 0 to 1/u);
});
it("resolves DTCG JSON Pointer aliases and property-level references", () => {
const result = parseDtcgTokens(
JSON.stringify({
base: {
$type: "color",
$value: {
colorSpace: "srgb",
components: [0.2, 0.4, 0.8],
hex: "#3366cc",
},
},
alias: { $ref: "#/base/$value" },
mixed: {
$type: "color",
$value: {
colorSpace: "srgb",
components: [
{ $ref: "#/base/$value/components/0" },
{ $ref: "#/base/$value/components/1" },
0.7,
],
},
},
}),
);
expect(result.entries.map((entry) => entry.name)).toEqual([
"base",
"alias",
"mixed",
]);
expect(result.diagnostics).toEqual([]);
});
it("reports cyclic and malformed DTCG property references", () => {
const result = parseDtcgTokens(
JSON.stringify({
cycle: {
$type: "color",
$value: { $ref: "#/cycle/$value" },
},
siblings: {
$type: "color",
$value: { $ref: "#/cycle/$value", extra: true },
},
}),
);
expect(result.entries).toEqual([]);
expect(result.diagnostics.map((item) => item.message).join(" ")).toMatch(
/cycle|sibling/iu,
);
});
});
describe("palette accessibility and gradients", () => {
it("builds a directional contrast matrix with WCAG thresholds", () => {
const matrix = paletteContrastMatrix(["black", "white", "#777"]);
expect(matrix.size).toBe(3);
expect(matrix.cells[0]?.[1]).toMatchObject({
ratio: 21,
aaNormal: true,
aaaNormal: true,
});
expect(matrix.cells[2]?.[1]?.aaNormal).toBe(false);
expect(() => paletteContrastMatrix([])).toThrow(/164/u);
});
it("exports linear, radial, conic and repeating CSS gradients", () => {
const stops = [
{ colour: "red", position: 0 },
{ colour: "blue", position: 1 },
];
expect(buildCssGradient(stops, { angle: 45 })).toMatch(
/^linear-gradient\(45deg,/u,
);
expect(
buildCssGradient(stops, { kind: "radial", radialShape: "circle" }),
).toMatch(/^radial-gradient\(circle at center,/u);
expect(buildCssGradient(stops, { kind: "conic", repeating: true })).toMatch(
/^repeating-conic-gradient\(/u,
);
expect(JSON.parse(exportGradientRecipe(stops))).toMatchObject({
schemaVersion: 1,
type: "css-gradient",
stops: [{ position: 0 }, { position: 1 }],
});
});
});