@tenphi/glaze 1.4.0 → 1.5.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/README.md CHANGED
@@ -32,7 +32,7 @@ contrast floor.
32
32
  - **Mix colors** — blend two colors with OKHSL or sRGB interpolation, opaque or transparent, with optional contrast solving
33
33
  - **Shadow colors** — OKHSL-native shadow computation with automatic alpha, fg/bg tinting, and per-scheme adaptation
34
34
  - **Light + Dark + High-Contrast** — all schemes from one definition
35
- - **Manual contrast level** — swap the high-contrast tier for a `0–100` slider; `0` is your normal palette, `100` the high-contrast one, and every level in between is solved, not approximated
35
+ - **Manual contrast level** — put your normal palette on a `0–100` slider between itself and the high-contrast one; every level in between is solved, not approximated, and the high-contrast tier stays available on top
36
36
  - **Per-color hue override** — absolute or relative hue shifts within a theme
37
37
  - **Multi-format output** — native `rgb`, `hsl`, and `oklch`, plus
38
38
  [Tasty](https://tasty.style)-compatible `okhsl` and `okhst`
package/dist/index.cjs CHANGED
@@ -906,10 +906,14 @@ function levelFraction(level) {
906
906
  }
907
907
  /**
908
908
  * Manual-contrast blend fraction (0–1) for a resolved config, or `undefined`
909
- * in `'auto'` mode (the two-tier normal + high-contrast model).
909
+ * in `'auto'` mode, where the normal variants take the authored normal entries
910
+ * as-is.
910
911
  *
911
- * Note `contrastLevel: 0` yields `0`, not `undefined`: "pinned to normal
912
- * contrast with no high-contrast tier" is a distinct state from `'auto'`.
912
+ * Note `contrastLevel: 0` yields `0`, not `undefined`. The two select different
913
+ * branches, and an authored `0` is a slider position that `.export()` freezes
914
+ * where an absent level is not — but they are not an *output* distinction: at
915
+ * fraction 0 every interpolation returns its normal entry verbatim, so level 0
916
+ * reproduces `'auto'` bit for bit.
913
917
  */
914
918
  function contrastFraction(config) {
915
919
  const level = config.contrastLevel;
@@ -917,6 +921,20 @@ function contrastFraction(config) {
917
921
  return levelFraction(level);
918
922
  }
919
923
  /**
924
+ * Whether the high-contrast variants are exact duplicates of the normal ones —
925
+ * true only at `contrastLevel: 100`, where the normal passes already resolve at
926
+ * full contrast.
927
+ *
928
+ * The level otherwise says nothing about high contrast: it positions the normal
929
+ * variants, while `lightContrast` / `darkContrast` stay the true high-contrast
930
+ * resolution at every level. This predicate marks the one level where that tier
931
+ * carries no new information, so callers can skip the two high-contrast passes,
932
+ * their drift checks, and the redundant output tier.
933
+ */
934
+ function hcMirrorsNormal(config) {
935
+ return contrastFraction(config) === 1;
936
+ }
937
+ /**
920
938
  * A numeric HC pair at a blend fraction (shadow `intensity`, mix `value`).
921
939
  *
922
940
  * The endpoints return the authored entry *by identity* rather than through
@@ -2276,12 +2294,14 @@ function warnContrastDrift(name, isDark, isHighContrast, contrast, yColor, yBase
2276
2294
  * Color resolution engine.
2277
2295
  *
2278
2296
  * Runs the four-pass solver (light → light-HC → dark → dark-HC) that
2279
- * turns a `ColorMap` into a fully resolved `ResolvedColor` per name — or,
2280
- * under a manual `contrastLevel`, a two-pass solver (light → dark) whose
2281
- * authored HC pairs, tone window, and contrast targets are interpolated at
2282
- * that level and whose high-contrast slots mirror the normal ones.
2283
- * Owns the per-scheme resolve helpers for regular, shadow, and mix
2284
- * color defs.
2297
+ * turns a `ColorMap` into a fully resolved `ResolvedColor` per name. A manual
2298
+ * `contrastLevel` interpolates the authored HC pairs, tone window, and contrast
2299
+ * targets at that level for the two *normal* passes only; the high-contrast
2300
+ * passes bypass the level entirely, so the high-contrast slots are the same
2301
+ * values `'auto'` resolves. At level 100 the normal passes already are the
2302
+ * high-contrast ones, so the solver drops to two passes (light → dark) and
2303
+ * mirrors the slots. Owns the per-scheme resolve helpers for regular, shadow,
2304
+ * and mix color defs.
2285
2305
  *
2286
2306
  * Variants are stored in OKHST: `h` / `s` are OKHSL hue/saturation and
2287
2307
  * `t` is the canonical contrast-uniform tone (0–1, reference eps). The
@@ -2538,7 +2558,7 @@ function resolveDependentColor(name, def, ctx, isHighContrast, isDark, channels,
2538
2558
  flip,
2539
2559
  pastel
2540
2560
  };
2541
- const level = contrastFraction(ctx.config);
2561
+ const level = isHighContrast ? void 0 : contrastFraction(config);
2542
2562
  let preferInitial = false;
2543
2563
  if (level !== void 0 && level > 0 && level < 1) {
2544
2564
  initialDirection = findToneForContrast({
@@ -2727,10 +2747,11 @@ function seedField(order, ctx, field, source) {
2727
2747
  * resolved with a `base` + `contrast` may land slightly under the contrast
2728
2748
  * its tone implies because chromatic luminance drifts from the gray tone.
2729
2749
  *
2730
- * Under a manual `contrastLevel` only the two emitted variants are checked
2731
- * (the high-contrast slots are mirrors), and the spec is resolved at the level
2732
- * so the check measures the output against the target it was actually solved
2733
- * for.
2750
+ * Under a manual `contrastLevel` the spec is resolved at the level, so the check
2751
+ * measures each normal variant against the target it was actually solved for. At
2752
+ * level 100 the high-contrast slots are mirrors and are skipped: the normal
2753
+ * schemes already carry that check, and repeating it would emit a byte-identical
2754
+ * advisory under a different scheme label, which dedupes as a separate key.
2734
2755
  */
2735
2756
  function verifyContrastDrift(order, defs, result, config) {
2736
2757
  const roles = /* @__PURE__ */ new Map();
@@ -2765,9 +2786,9 @@ function verifyContrastDrift(order, defs, result, config) {
2765
2786
  field: "darkContrast"
2766
2787
  }
2767
2788
  ];
2768
- const manual = contrastFraction(config) !== void 0;
2789
+ const mirrored = hcMirrorsNormal(config);
2769
2790
  for (const s of schemes) {
2770
- if (manual && s.isHighContrast) continue;
2791
+ if (mirrored && s.isHighContrast) continue;
2771
2792
  const spec = resolveContrastSpec(regDef.contrast, s.isHighContrast, config, polarity);
2772
2793
  const cVariant = color[s.field];
2773
2794
  const bVariant = base[s.field];
@@ -2792,10 +2813,10 @@ function resolveAllColors(seed, defs, config, externalBases) {
2792
2813
  roles: /* @__PURE__ */ new Map()
2793
2814
  };
2794
2815
  if (externalBases) for (const [name, color] of externalBases) ctx.resolved.set(name, color);
2795
- const manual = contrastFraction(config) !== void 0;
2816
+ const mirrored = hcMirrorsNormal(config);
2796
2817
  const lightMap = runPass(order, defs, ctx, false, false, "light");
2797
2818
  let lightHCMap = lightMap;
2798
- if (!manual) {
2819
+ if (!mirrored) {
2799
2820
  seedField(order, ctx, "lightContrast", lightMap);
2800
2821
  lightHCMap = runPass(order, defs, ctx, false, true, "lightContrast");
2801
2822
  }
@@ -2803,7 +2824,7 @@ function resolveAllColors(seed, defs, config, externalBases) {
2803
2824
  seedField(order, ctx, "darkContrast", lightHCMap);
2804
2825
  const darkMap = runPass(order, defs, ctx, true, false, "dark");
2805
2826
  let darkHCMap = darkMap;
2806
- if (!manual) {
2827
+ if (!mirrored) {
2807
2828
  seedField(order, ctx, "darkContrast", darkMap);
2808
2829
  darkHCMap = runPass(order, defs, ctx, true, true, "darkContrast");
2809
2830
  }
@@ -3037,23 +3058,28 @@ function formatColorValue(v, format, pastel, huePlan) {
3037
3058
  /**
3038
3059
  * Resolve the effective output modes for an export.
3039
3060
  *
3040
- * A global manual `contrastLevel` turns off high-contrast output outright: the
3041
- * level *is* the contrast preference, so a separate high-contrast tier has no
3042
- * meaning alongside it. `modes.highContrast` therefore reads as "emit a separate
3043
- * high-contrast set **when** contrast is automatic" — it goes inert under a
3044
- * manual level rather than fighting it, and silently, since switching a
3045
- * preference from auto to manual is normal use and not a mistake to report.
3061
+ * `modes.highContrast` and `contrastLevel` are independent: the level positions
3062
+ * the normal variants, the high-contrast tier stays the true high-contrast
3063
+ * resolution, and the two compose — a slider raises the baseline while a
3064
+ * `prefers-contrast: more` block still escalates on top of it.
3065
+ *
3066
+ * The one exception is a **global** level of 100, where the normal variants
3067
+ * already *are* the high-contrast ones. The tier would be an exact duplicate, so
3068
+ * it is dropped and a single light/dark set is emitted — including against an
3069
+ * explicit `highContrast: true` override, which has nothing different left to
3070
+ * ask for.
3046
3071
  *
3047
3072
  * A level set on a single theme or token does not change which modes are
3048
- * emitted: sibling themes in a palette may still have a real high-contrast
3049
- * tier, and the manual one correctly reports its own resolved values there —
3050
- * the alternative would drop its colors from that tier entirely.
3073
+ * emitted: `modes` is global-only, and a palette must not have one sibling
3074
+ * collapse the shared token structure. A per-instance level of 100 therefore
3075
+ * still reports into the tier, with values equal to its own normal ones.
3051
3076
  */
3052
3077
  function resolveModes(override) {
3053
3078
  const cfg = getConfig();
3079
+ const highContrast = override?.highContrast ?? cfg.modes.highContrast;
3054
3080
  return {
3055
3081
  dark: override?.dark ?? cfg.modes.dark,
3056
- highContrast: contrastFraction(cfg) !== void 0 ? false : override?.highContrast ?? cfg.modes.highContrast
3082
+ highContrast: highContrast && !hcMirrorsNormal(cfg)
3057
3083
  };
3058
3084
  }
3059
3085
  function buildTokenMap(resolved, prefix, states, modes, format = "oklch", pastel = false, channelCtx) {