@siyahi/tools 0.1.9-next.24 → 0.1.9-next.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/contrast.mjs CHANGED
@@ -23,10 +23,14 @@ import { readTheme } from "./theme.mjs";
23
23
  * What a component paints, written as Tailwind writes it:
24
24
  * `muted` is the token, `muted/50` the token at 50% over what lies under it,
25
25
  * and `positive@60` is `color-mix(in oklab, var(--positive) 60%, var(--card))`,
26
- * a treemap tile.
26
+ * a treemap tile; `positive@heat-5` takes the share from the theme's --heat-5.
27
27
  * @typedef {string} Paint
28
28
  */
29
- /** @typedef {{ fg: Paint, bg: Paint, min: number, scheme?: Scheme }} Pair */
29
+ /**
30
+ * `surface` limits a pair to the canvas or the card, for a paint only one of them ever holds, such as a chart card's
31
+ * hover band.
32
+ * @typedef {{ fg: Paint, bg: Paint, min: number, scheme?: Scheme, surface?: "background" | "card" }} Pair
33
+ */
30
34
 
31
35
  const TEXT = 4.5;
32
36
  const MARK = 3;
@@ -34,14 +38,12 @@ const SURFACES = ["background", "card"];
34
38
  const CHARTS = ["chart-1", "chart-2", "chart-3", "chart-4", "chart-5"];
35
39
 
36
40
  /**
37
- * The light label on each treemap heat step, both sides.
38
- * @param {Scheme} scheme @param {number[]} shares
39
- * @returns {Pair[]}
41
+ * The label on each heat step, both sides, at the shares the theme's --heat-1 to --heat-5 set.
42
+ * @type {Pair[]}
40
43
  */
41
- const heatLabels = (scheme, shares) =>
42
- ["positive", "negative"].flatMap((side) =>
43
- shares.map((share) => ({ fg: "foreground", bg: `${side}@${share}`, min: TEXT, scheme })),
44
- );
44
+ const HEAT_LABELS = ["positive", "negative"].flatMap((side) =>
45
+ [1, 2, 3, 4, 5].map((step) => ({ fg: "foreground", bg: `${side}@heat-${step}`, min: TEXT })),
46
+ );
45
47
 
46
48
  /** @type {Pair[]} */
47
49
  const PAIRS = [
@@ -95,16 +97,27 @@ const PAIRS = [
95
97
  ["positive", "positive/8"],
96
98
  ["negative", "negative/8"],
97
99
  ].map(([fg = "", bg = ""]) => ({ fg, bg, min: MARK })),
100
+ // A StatusHeatmap's degraded and critical days against a healthy one, its
101
+ // default 30% grey, so a bad day stands out of a run of good ones.
102
+ ...["warning", "destructive"].map((fg) => ({ fg, bg: "muted-foreground/30", min: MARK })),
98
103
  // The selected tab's fill against its list.
99
104
  { fg: "foreground", bg: "muted", min: MARK },
100
105
  // Chart marks on the canvas and the card. A dimmed bar keeps its full colour
101
106
  // as its outline, so these are the outlines too.
102
107
  ...[...CHARTS, "positive", "negative"].map((fg) => ({ fg, bg: "", min: MARK })),
103
108
  ...CHARTS.slice(0, -1).map((fg, i) => ({ fg, bg: CHARTS[i + 1] ?? "", min: MARK })),
104
- // Treemap labels on every heat step (lib/treemap.ts heatFill): 12% to 60% of
105
- // the meaning colour in the card in light, 25% to 60% in dark.
106
- ...heatLabels("light", [12, 24, 36, 48, 60]),
107
- ...heatLabels("dark", [25, 33.75, 42.5, 51.25, 60]),
109
+ // A waterfall's rise and fall: the full-colour edge against the tint it
110
+ // encloses, on the bar and its legend chip; and every bar colour against the
111
+ // 40% muted band a chart card lays behind a hovered category (lib/chart-frame).
112
+ ...["positive", "negative"].map((side) => ({ fg: side, bg: `${side}@35`, min: MARK })),
113
+ ...[...CHARTS, "positive", "negative"].map((fg) => ({ fg, bg: "muted/40", min: MARK, surface: "card" })),
114
+ // A chart card's footer text, on the footer's muted wash.
115
+ { fg: "foreground", bg: "muted/50", min: TEXT },
116
+ // Treemap, ChangeGrid and signed-calendar labels on every heat step
117
+ // (lib/heat.ts heatFill), at the theme's own --heat-N shares. A ChangeGrid
118
+ // cell with no change is muted, with its figure in the text colour.
119
+ { fg: "foreground", bg: "muted", min: TEXT },
120
+ ...HEAT_LABELS,
108
121
  ];
109
122
 
110
123
  /**
@@ -115,6 +128,12 @@ export function checkContrast(theme) {
115
128
  /** @type {Map<string, Check>} */
116
129
  const checks = new Map();
117
130
  for (const scheme of /** @type {const} */ (["light", "dark"])) {
131
+ const percentage = (/** @type {string} */ name) => {
132
+ const value = theme[scheme][name];
133
+ const match = value === undefined ? null : /^([\d.]+)%$/.exec(value);
134
+ if (!match) throw new Error(`${scheme}: --${name} is "${value}", not a percentage`);
135
+ return Number(match[1]);
136
+ };
118
137
  const token = (/** @type {string} */ name) => {
119
138
  const value = theme[scheme][name];
120
139
  if (value === undefined) throw new Error(`${scheme}: --${name} is not set in the ${scheme} block`);
@@ -127,11 +146,13 @@ export function checkContrast(theme) {
127
146
  * @param {Paint} paint @param {{ rgb: [number, number, number], name: string }} under
128
147
  */
129
148
  const paintOver = (paint, under) => {
130
- const [, name = "", sep, amount] = /^([\w-]+)(?:([/@])([\d.]+))?$/.exec(paint) ?? [];
149
+ const [, name = "", sep, amount = ""] = /^([\w-]+)(?:([/@])([\w.-]+))?$/.exec(paint) ?? [];
131
150
  const base = token(name);
132
151
  if (sep === "@") {
133
- const rgb = oklchToSrgb(mixOklab(base, token("card"), Number(amount) / 100));
134
- return { rgb, name: `--${name} ${amount}% in --card` };
152
+ // A share is a number, or the name of a theme token holding a percentage, such as heat-5.
153
+ const share = /^[\d.]+$/.test(amount) ? Number(amount) : percentage(amount);
154
+ const rgb = oklchToSrgb(mixOklab(base, token("card"), share / 100));
155
+ return { rgb, name: `--${name} ${share}% in --card` };
135
156
  }
136
157
  const alpha = base.alpha * (sep === "/" ? Number(amount) / 100 : 1);
137
158
  if (alpha === 1) return { rgb: oklchToSrgb(base), name: `--${paint}` };
@@ -140,6 +161,7 @@ export function checkContrast(theme) {
140
161
  for (const pair of PAIRS) {
141
162
  if (pair.scheme && pair.scheme !== scheme) continue;
142
163
  for (const surface of SURFACES) {
164
+ if (pair.surface && pair.surface !== surface) continue;
143
165
  const page = { rgb: oklchToSrgb(token(surface)), name: `--${surface}` };
144
166
  const bg = pair.bg ? paintOver(pair.bg, page) : page;
145
167
  const fg = paintOver(pair.fg, bg);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@siyahi/tools",
3
- "version": "0.1.9-next.24",
3
+ "version": "0.1.9-next.25",
4
4
  "description": "Checks and build helpers for Siyahi pages: a lint plugin, a font preload plugin, a theme contrast check, and screenshot and layout shift scripts.",
5
5
  "homepage": "https://siyahi.jatinbansal.com",
6
6
  "license": "MIT",
@@ -17,6 +17,7 @@ export type Pair = {
17
17
  bg: Paint;
18
18
  min: number;
19
19
  scheme?: Scheme;
20
+ surface?: "background" | "card";
20
21
  };
21
22
  /**
22
23
  * @param {Theme} theme