Sign In

@protolabsai/design

Package Overview
Dependencies
Maintainers
1
Versions
10
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@protolabsai/design - npm Package Compare versions

Comparing version
0.7.1
to
0.7.2
+163
src/tokens.test.mjs
import { describe, it } from "node:test";
import assert from "node:assert/strict";
import { tokens, light } from "./tokens.js";
const CHART_KEYS = [
"series1",
"series2",
"series3",
"series4",
"series5",
"series6",
"series7",
"series8",
"grid",
"axis",
];
const OKLCH_RE = /^oklch\(\s*([\d.]+)\s+([\d.]+)\s+([\d.]+)\s*\)$/;
function parseOklch(value, label) {
const m = value.match(OKLCH_RE);
assert.ok(m, `${label}: expected oklch(L C H), got "${value}"`);
return { l: Number(m[1]), c: Number(m[2]), h: Number(m[3]) };
}
describe("tokens.color.chart", () => {
it("exists and has all 10 chart keys", () => {
assert.ok(tokens.color.chart, "tokens.color.chart must exist");
for (const key of CHART_KEYS) {
assert.ok(
key in tokens.color.chart,
`tokens.color.chart.${key} is missing`,
);
}
assert.equal(
Object.keys(tokens.color.chart).length,
CHART_KEYS.length,
"tokens.color.chart has unexpected extra keys",
);
});
describe("series (dark)", () => {
for (let i = 1; i <= 8; i++) {
const key = `series${i}`;
it(`${key} is a valid OKLCH color with L >= 0.65 on dark ground`, () => {
const oklch = parseOklch(tokens.color.chart[key], key);
assert.ok(
oklch.l >= 0.65,
`${key} luminance ${oklch.l} is below 0.65 — won't read on dark ground`,
);
assert.ok(
oklch.c <= 0.16,
`${key} chroma ${oklch.c} is too high — low-chroma palette only`,
);
});
}
});
it("grid is a valid rgba color on dark", () => {
const v = tokens.color.chart.grid;
assert.match(
v,
/^rgba\(/,
`chart.grid expected rgba(), got "${v}"`,
);
});
it("axis matches fgMuted on dark", () => {
assert.equal(
tokens.color.chart.axis,
tokens.color.fgMuted,
"chart.axis must equal fgMuted",
);
});
});
describe("light.color.chart", () => {
it("exists and has all 10 chart keys", () => {
assert.ok(light.color.chart, "light.color.chart must exist");
for (const key of CHART_KEYS) {
assert.ok(
key in light.color.chart,
`light.color.chart.${key} is missing`,
);
}
assert.equal(
Object.keys(light.color.chart).length,
CHART_KEYS.length,
"light.color.chart has unexpected extra keys",
);
});
describe("series (light)", () => {
for (let i = 1; i <= 8; i++) {
const key = `series${i}`;
it(`${key} is a valid OKLCH color with L <= 0.60 on light ground`, () => {
const oklch = parseOklch(light.color.chart[key], key);
assert.ok(
oklch.l <= 0.60,
`${key} luminance ${oklch.l} is above 0.60 — may wash out on light ground`,
);
assert.ok(
oklch.l >= 0.42,
`${key} luminance ${oklch.l} is below 0.42 — too dark for light ground`,
);
assert.ok(
oklch.c <= 0.18,
`${key} chroma ${oklch.c} is too high — low-chroma palette only`,
);
});
}
});
it("grid is a valid rgba color on light", () => {
const v = light.color.chart.grid;
assert.match(
v,
/^rgba\(/,
`chart.grid expected rgba(), got "${v}"`,
);
});
it("axis matches fgMuted on light", () => {
assert.equal(
light.color.chart.axis,
light.color.fgMuted,
"chart.axis must equal light fgMuted",
);
});
});
describe("chart hue distinctiveness", () => {
it("dark series hues are spread across the spectrum (no two within 20° hue)", () => {
const hues = [];
for (let i = 1; i <= 8; i++) {
const oklch = parseOklch(tokens.color.chart[`series${i}`], `series${i}`);
hues.push(oklch.h);
}
for (let i = 0; i < hues.length; i++) {
for (let j = i + 1; j < hues.length; j++) {
const diff = Math.abs(hues[i] - hues[j]);
const circularDiff = Math.min(diff, 360 - diff);
assert.ok(
circularDiff >= 20,
`series${i + 1} (${hues[i]}°) and series${j + 1} (${hues[j]}°) are only ${circularDiff.toFixed(1)}° apart — not distinguishable enough`,
);
}
}
});
it("light series hues match dark series hues (same anchor)", () => {
for (let i = 1; i <= 8; i++) {
const key = `series${i}`;
const darkHue = parseOklch(tokens.color.chart[key], key).h;
const lightHue = parseOklch(light.color.chart[key], key).h;
assert.equal(
darkHue,
lightHue,
`${key} hue drift: dark=${darkHue}°, light=${lightHue}°`,
);
}
});
});
+40
-0

@@ -30,2 +30,12 @@ /* GENERATED from src/tokens.js by scripts/build.mjs — do not edit by hand. */

--pl-color-status-info: oklch(0.68 0.12 230);
--pl-color-chart-series1: oklch(0.72 0.14 290);
--pl-color-chart-series2: oklch(0.70 0.12 250);
--pl-color-chart-series3: oklch(0.72 0.13 170);
--pl-color-chart-series4: oklch(0.74 0.14 135);
--pl-color-chart-series5: oklch(0.78 0.12 80);
--pl-color-chart-series6: oklch(0.70 0.14 30);
--pl-color-chart-series7: oklch(0.68 0.12 210);
--pl-color-chart-series8: oklch(0.70 0.13 320);
--pl-color-chart-grid: rgba(255, 255, 255, 0.06);
--pl-color-chart-axis: #a1a1aa;
--pl-gradient-brand: linear-gradient(135deg, var(--pl-color-accent) 0%, var(--pl-color-brand-indigo-bright) 50%, var(--pl-color-brand-indigo) 100%);

@@ -89,2 +99,12 @@ --pl-gradient-brand-light: linear-gradient(135deg, var(--pl-color-accent) 0%, var(--pl-color-brand-indigo) 50%, var(--pl-color-brand-indigo-deep) 100%);

--pl-color-status-info: oklch(0.58 0.17 255);
--pl-color-chart-series1: oklch(0.52 0.16 290);
--pl-color-chart-series2: oklch(0.50 0.14 250);
--pl-color-chart-series3: oklch(0.52 0.15 170);
--pl-color-chart-series4: oklch(0.54 0.16 135);
--pl-color-chart-series5: oklch(0.58 0.14 80);
--pl-color-chart-series6: oklch(0.50 0.16 30);
--pl-color-chart-series7: oklch(0.48 0.14 210);
--pl-color-chart-series8: oklch(0.50 0.15 320);
--pl-color-chart-grid: rgba(0, 0, 0, 0.06);
--pl-color-chart-axis: #52525b;
--pl-gradient-brand: linear-gradient(135deg, var(--pl-color-accent) 0%, var(--pl-color-brand-indigo) 50%, var(--pl-color-brand-indigo-deep) 100%);

@@ -118,2 +138,12 @@ --pl-shadow-popover: 0 8px 24px rgba(0, 0, 0, 0.12);

--pl-color-status-info: oklch(0.58 0.17 255);
--pl-color-chart-series1: oklch(0.52 0.16 290);
--pl-color-chart-series2: oklch(0.50 0.14 250);
--pl-color-chart-series3: oklch(0.52 0.15 170);
--pl-color-chart-series4: oklch(0.54 0.16 135);
--pl-color-chart-series5: oklch(0.58 0.14 80);
--pl-color-chart-series6: oklch(0.50 0.16 30);
--pl-color-chart-series7: oklch(0.48 0.14 210);
--pl-color-chart-series8: oklch(0.50 0.15 320);
--pl-color-chart-grid: rgba(0, 0, 0, 0.06);
--pl-color-chart-axis: #52525b;
--pl-gradient-brand: linear-gradient(135deg, var(--pl-color-accent) 0%, var(--pl-color-brand-indigo) 50%, var(--pl-color-brand-indigo-deep) 100%);

@@ -144,4 +174,14 @@ --pl-shadow-popover: 0 8px 24px rgba(0, 0, 0, 0.12);

--pl-color-status-info: oklch(0.68 0.12 230);
--pl-color-chart-series1: oklch(0.72 0.14 290);
--pl-color-chart-series2: oklch(0.70 0.12 250);
--pl-color-chart-series3: oklch(0.72 0.13 170);
--pl-color-chart-series4: oklch(0.74 0.14 135);
--pl-color-chart-series5: oklch(0.78 0.12 80);
--pl-color-chart-series6: oklch(0.70 0.14 30);
--pl-color-chart-series7: oklch(0.68 0.12 210);
--pl-color-chart-series8: oklch(0.70 0.13 320);
--pl-color-chart-grid: rgba(255, 255, 255, 0.06);
--pl-color-chart-axis: #a1a1aa;
--pl-gradient-brand: linear-gradient(135deg, var(--pl-color-accent) 0%, var(--pl-color-brand-indigo-bright) 50%, var(--pl-color-brand-indigo) 100%);
--pl-shadow-popover: 0 8px 28px rgba(0, 0, 0, 0.5);
}

@@ -32,2 +32,14 @@ {

"info": "oklch(0.68 0.12 230)"
},
"chart": {
"series1": "oklch(0.72 0.14 290)",
"series2": "oklch(0.70 0.12 250)",
"series3": "oklch(0.72 0.13 170)",
"series4": "oklch(0.74 0.14 135)",
"series5": "oklch(0.78 0.12 80)",
"series6": "oklch(0.70 0.14 30)",
"series7": "oklch(0.68 0.12 210)",
"series8": "oklch(0.70 0.13 320)",
"grid": "rgba(255, 255, 255, 0.06)",
"axis": "#a1a1aa"
}

@@ -100,2 +112,14 @@ },

"info": "oklch(0.58 0.17 255)"
},
"chart": {
"series1": "oklch(0.52 0.16 290)",
"series2": "oklch(0.50 0.14 250)",
"series3": "oklch(0.52 0.15 170)",
"series4": "oklch(0.54 0.16 135)",
"series5": "oklch(0.58 0.14 80)",
"series6": "oklch(0.50 0.16 30)",
"series7": "oklch(0.48 0.14 210)",
"series8": "oklch(0.50 0.15 320)",
"grid": "rgba(0, 0, 0, 0.06)",
"axis": "#52525b"
}

@@ -102,0 +126,0 @@ },

+3
-2
{
"name": "@protolabsai/design",
"version": "0.7.1",
"version": "0.7.2",
"publishConfig": {

@@ -36,4 +36,5 @@ "access": "public",

"scripts": {
"build": "node scripts/build.mjs"
"build": "node scripts/build.mjs",
"test": "node --test src/tokens.test.mjs"
}
}

@@ -63,2 +63,18 @@ /**

},
// ── Charts — 8 categorical series + semantic helpers ──
// Luminance >= 0.65 so every swatch reads against the dark ground (#0a0a0c).
// series1 anchors on brand lavender; the rest span distinguishable hues.
// Low-chroma OKLCH throughout — never neon.
chart: {
series1: "oklch(0.72 0.14 290)", // brand lavender anchor
series2: "oklch(0.70 0.12 250)", // sky
series3: "oklch(0.72 0.13 170)", // teal
series4: "oklch(0.74 0.14 135)", // emerald
series5: "oklch(0.78 0.12 80)", // amber
series6: "oklch(0.70 0.14 30)", // warm rose
series7: "oklch(0.68 0.12 210)", // steel
series8: "oklch(0.70 0.13 320)", // violet
grid: "rgba(255, 255, 255, 0.06)", // subtle axis lines, ~border opacity
axis: "#a1a1aa", // tick text — matches fgMuted
},
},

@@ -137,2 +153,15 @@ gradient: {

},
// Light-ground chart — lower luminance so swatches read on white.
chart: {
series1: "oklch(0.52 0.16 290)", // brand lavender anchor
series2: "oklch(0.50 0.14 250)", // sky
series3: "oklch(0.52 0.15 170)", // teal
series4: "oklch(0.54 0.16 135)", // emerald
series5: "oklch(0.58 0.14 80)", // amber
series6: "oklch(0.50 0.16 30)", // warm rose
series7: "oklch(0.48 0.14 210)", // steel
series8: "oklch(0.50 0.15 320)", // violet
grid: "rgba(0, 0, 0, 0.06)", // subtle axis lines
axis: "#52525b", // tick text — matches light fgMuted
},
},

@@ -139,0 +168,0 @@ gradient: { brand: tokens.gradient.brandLight },