@tenphi/glaze 1.5.0 → 2.0.0

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/dist/index.cjs CHANGED
@@ -372,6 +372,7 @@ const sRGBGammaToLinear = (val) => {
372
372
  };
373
373
  /**
374
374
  * Convert OKHSL to gamma-encoded sRGB (clamped to 0–1).
375
+ * h: 0–360, s: 0–1, l: 0–1. Returns [r, g, b] with each channel 0–1.
375
376
  */
376
377
  function okhslToSrgb(h, s, l, pastel = false) {
377
378
  const lin = okhslToLinearSrgb(h, s, l, pastel);
@@ -581,47 +582,72 @@ function fmt$1(value, decimals) {
581
582
  return parseFloat(value.toFixed(decimals)).toString();
582
583
  }
583
584
  /**
585
+ * Every `format*` writer takes `s` / `l` / `t` on the **0–1 factor scale** —
586
+ * the same scale every producer in the library hands back (`resolve()`,
587
+ * `variantToOkhsl`, `srgbToOkhsl`, `oklabToOkhsl`, `okhslToOklab`,
588
+ * `okhslToSrgb`). The percentages are an output detail: the writers multiply
589
+ * by 100 themselves when the CSS syntax asks for a percentage.
590
+ *
591
+ * Before 2.0 the writers took 0–100 while everything producing those values
592
+ * returned 0–1, so the obvious composition was off by 100x and failed
593
+ * silently — `0.7` is a legal percentage, so the result was a valid CSS string
594
+ * naming a near-black color. A value above 1 can only be that old
595
+ * percentage-scale input, so warn about it loudly rather than emit a wrong
596
+ * color quietly.
597
+ */
598
+ const scaleWarnCache = /* @__PURE__ */ new Set();
599
+ function warnPercentScale(fn, ...values) {
600
+ if (!values.some((v) => v > 1.000001)) return;
601
+ if (!scaleWarnCache.has(fn)) {
602
+ scaleWarnCache.add(fn);
603
+ console.warn(`glaze: ${fn}() got a value above 1 — it takes the 0-1 factors every Glaze producer returns (resolve(), variantToOkhsl, srgbToOkhsl), not 0-100 percentages. Divide by 100, or drop the "* 100" left over from the pre-2.0 signature.`);
604
+ }
605
+ }
606
+ /**
584
607
  * Format OKHSL values as a CSS `okhsl(H S% L%)` string.
585
- * h: 0–360, s: 0–100, l: 0–100 (percentage scale for s and l).
608
+ * h: 0–360, s: 0–1, l: 0–1 (factor scale, as returned by every converter).
586
609
  */
587
610
  function formatOkhsl(h, s, l, pastel = false) {
611
+ warnPercentScale("formatOkhsl", s, l);
588
612
  let outS = s;
589
- if (pastel) outS = oklabToOkhsl(okhslToOklab(h, s / 100, l / 100, true), false)[1] * 100;
590
- return `okhsl(${fmt$1(h, 2)} ${fmt$1(outS, 2)}% ${fmt$1(l, 2)}%)`;
613
+ if (pastel) outS = oklabToOkhsl(okhslToOklab(h, s, l, true), false)[1];
614
+ return `okhsl(${fmt$1(h, 2)} ${fmt$1(outS * 100, 2)}% ${fmt$1(l * 100, 2)}%)`;
591
615
  }
592
616
  /**
593
617
  * Format OKHST values as a CSS `okhst(H S% T%)` string.
594
- * h: 0–360, s: 0–100, t: 0–100 (percentage scale for s and t).
618
+ * h: 0–360, s: 0–1, t: 0–1 (factor scale, matching `ResolvedColorVariant`).
595
619
  *
596
620
  * Pastel recompute matches `formatOkhsl`: convert via OKLab so external
597
621
  * parsers that only understand non-pastel OKHST render identically.
598
622
  */
599
623
  function formatOkhst(h, s, t, pastel = false) {
624
+ warnPercentScale("formatOkhst", s, t);
600
625
  let outS = s;
601
626
  if (pastel) {
602
627
  const REF_EPS = .05;
603
628
  const den = Math.log(1 + REF_EPS) - Math.log(REF_EPS);
604
- const y = Math.exp(t / 100 * den + Math.log(REF_EPS)) - REF_EPS;
605
- const l = toe(Math.cbrt(Math.max(0, y)));
606
- outS = oklabToOkhsl(okhslToOklab(h, s / 100, l, true), false)[1] * 100;
629
+ const y = Math.exp(t * den + Math.log(REF_EPS)) - REF_EPS;
630
+ outS = oklabToOkhsl(okhslToOklab(h, s, toe(Math.cbrt(Math.max(0, y))), true), false)[1];
607
631
  }
608
- return `okhst(${fmt$1(h, 2)} ${fmt$1(outS, 2)}% ${fmt$1(t, 2)}%)`;
632
+ return `okhst(${fmt$1(h, 2)} ${fmt$1(outS * 100, 2)}% ${fmt$1(t * 100, 2)}%)`;
609
633
  }
610
634
  /**
611
635
  * Format OKHSL values as a CSS `rgb(R G B)` string.
612
636
  * Uses 2 decimal places to avoid 8-bit quantization contrast loss.
613
- * h: 0–360, s: 0–100, l: 0–100 (percentage scale for s and l).
637
+ * h: 0–360, s: 0–1, l: 0–1 (factor scale, as returned by every converter).
614
638
  */
615
639
  function formatRgb(h, s, l, pastel = false) {
616
- const [r, g, b] = okhslToSrgb(h, s / 100, l / 100, pastel);
640
+ warnPercentScale("formatRgb", s, l);
641
+ const [r, g, b] = okhslToSrgb(h, s, l, pastel);
617
642
  return `rgb(${parseFloat((r * 255).toFixed(2))} ${parseFloat((g * 255).toFixed(2))} ${parseFloat((b * 255).toFixed(2))})`;
618
643
  }
619
644
  /**
620
645
  * Format OKHSL values as a CSS `hsl(H S% L%)` string.
621
- * h: 0–360, s: 0–100, l: 0–100 (percentage scale for s and l).
646
+ * h: 0–360, s: 0–1, l: 0–1 (factor scale, as returned by every converter).
622
647
  */
623
648
  function formatHsl(h, s, l, pastel = false) {
624
- const [r, g, b] = okhslToSrgb(h, s / 100, l / 100, pastel);
649
+ warnPercentScale("formatHsl", s, l);
650
+ const [r, g, b] = okhslToSrgb(h, s, l, pastel);
625
651
  const max = Math.max(r, g, b);
626
652
  const min = Math.min(r, g, b);
627
653
  const delta = max - min;
@@ -638,10 +664,11 @@ function formatHsl(h, s, l, pastel = false) {
638
664
  }
639
665
  /**
640
666
  * Format OKHSL values as a CSS `oklch(L C H)` string.
641
- * h: 0–360, s: 0–100, l: 0–100 (percentage scale for s and l).
667
+ * h: 0–360, s: 0–1, l: 0–1 (factor scale, as returned by every converter).
642
668
  */
643
669
  function formatOklch(h, s, l, pastel = false) {
644
- const [L, C, hh] = okhslToOklch(h, s / 100, l / 100, pastel);
670
+ warnPercentScale("formatOklch", s, l);
671
+ const [L, C, hh] = okhslToOklch(h, s, l, pastel);
645
672
  return `oklch(${fmt$1(L, 4)} ${fmt$1(C, 4)} ${fmt$1(hh, 2)})`;
646
673
  }
647
674
  /**
@@ -1119,7 +1146,11 @@ function okhslToOkhst(c) {
1119
1146
  }
1120
1147
  /**
1121
1148
  * Edge adapter: a resolved variant stores canonical tone `t` (0–1). Convert
1122
- * it to the OKHSL `{ h, s, l }` the formatters and luminance pipeline expect.
1149
+ * it to the OKHSL `{ h, s, l }` the formatters and luminance pipeline expect —
1150
+ * `s` and `l` on the same 0–1 factor scale, so `const { h, s, l } =
1151
+ * variantToOkhsl(v)` destructures straight into `formatOkhsl` / `formatRgb` /
1152
+ * `okhslToLinearSrgb` with no rescaling. `alpha` stays on the variant; use
1153
+ * `glaze.format(v, format)` for a string that carries it.
1123
1154
  */
1124
1155
  function variantToOkhsl(v) {
1125
1156
  return {
@@ -3026,10 +3057,10 @@ function fmt(value, decimals) {
3026
3057
  function formatVariant(v, format = "oklch", pastel = false) {
3027
3058
  const effectivePastel = v.pastel ?? pastel;
3028
3059
  let base;
3029
- if (format === "okhst") base = formatOkhst(v.h, v.s * 100, v.t * 100, effectivePastel);
3060
+ if (format === "okhst") base = formatOkhst(v.h, v.s, v.t, effectivePastel);
3030
3061
  else {
3031
3062
  const { l } = variantToOkhsl(v);
3032
- base = formatters[format](v.h, v.s * 100, l * 100, effectivePastel);
3063
+ base = formatters[format](v.h, v.s, l, effectivePastel);
3033
3064
  }
3034
3065
  if (v.alpha >= 1) return base;
3035
3066
  const closing = base.lastIndexOf(")");
@@ -3045,7 +3076,7 @@ function formatVariantHue(v, plan, pastel = false) {
3045
3076
  const [L, C] = okhslToOklch(v.h, v.s, l, effectivePastel);
3046
3077
  let base;
3047
3078
  if (plan.inline) if (v.s <= 1e-6) base = `oklch(${fmt(L, 4)} 0 0)`;
3048
- else base = formatOklch(v.h, v.s * 100, l * 100, effectivePastel);
3079
+ else base = formatOklch(v.h, v.s, l, effectivePastel);
3049
3080
  else base = `oklch(${fmt(L, 4)} ${fmt(C, 4)} ${plan.hueVar})`;
3050
3081
  if (v.alpha >= 1) return base;
3051
3082
  const closing = base.lastIndexOf(")");