@tenphi/glaze 0.10.0 → 0.11.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.d.cts CHANGED
@@ -376,16 +376,17 @@ interface GlazeColorOverrides {
376
376
  /** Saturation multiplier on the seed (0–1). Default: 1. */
377
377
  saturationFactor?: number;
378
378
  /**
379
- * Adaptation mode. Defaults vary by input form:
380
- * - String inputs (`'#1a1a1a'`, `'rgb(...)'`, etc.): `'auto'` (Möbius
381
- * curve pairs with the extended dark window to invert
382
- * `#000` `#fff` between light and dark).
383
- * - `OkhslColor` and `[r, g, b]` tuple inputs: `'fixed'` (linear,
384
- * preserves light lightness exactly).
379
+ * Adaptation mode. Defaults to `'auto'` for every input form, so
380
+ * colors automatically adapt between light and dark like an ordinary
381
+ * theme color. The default *scaling* snapshot differs by input form:
382
+ * string inputs preserve their light lightness and extend the dark
383
+ * window to `[lo, 100]` (`#000` `#fff` flip), while `OkhslColor`
384
+ * and `[r, g, b]` tuple inputs snapshot the full `globalConfig.
385
+ * lightLightness` / `globalConfig.darkLightness` windows.
385
386
  *
386
- * Pass `'fixed'` explicitly to opt a string input back into the
387
- * linear, non-inverting mapping; pass `'static'` to pin the same
388
- * lightness across every variant.
387
+ * Pass `'fixed'` explicitly to opt back into the legacy linear, non-
388
+ * inverting mapping; pass `'static'` to pin the same lightness
389
+ * across every variant.
389
390
  */
390
391
  mode?: AdaptationMode;
391
392
  /**
@@ -442,8 +443,9 @@ interface GlazeColorOverrides {
442
443
  * the way up to white.
443
444
  *
444
445
  * - **`OkhslColor` / `[r, g, b]` tuple / structured inputs**:
445
- * - `lightLightness: false` — preserve input exactly.
446
- * - `darkLightness: globalConfig.darkLightness` same window
446
+ * - `lightLightness: globalConfig.lightLightness` — same light window
447
+ * theme colors use, snapshotted at create time.
448
+ * - `darkLightness: globalConfig.darkLightness` — same dark window
447
449
  * theme colors use, snapshotted at create time.
448
450
  *
449
451
  * Passing this object replaces both fields at once. To keep one
@@ -451,7 +453,12 @@ interface GlazeColorOverrides {
451
453
  * explicitly.
452
454
  */
453
455
  interface GlazeColorScaling {
454
- /** Light-mode lightness window. `false` (default) preserves input. */
456
+ /**
457
+ * Light-mode lightness window. Snapshotted from `globalConfig` at
458
+ * create time: `false` (preserve input) for string inputs, plain
459
+ * `globalConfig.lightLightness` for object / tuple / structured
460
+ * inputs. Pass `false` to preserve input lightness in light mode.
461
+ */
455
462
  lightLightness?: false | [number, number];
456
463
  /**
457
464
  * Dark-mode lightness window. Snapshotted from `globalConfig` at
package/dist/index.d.mts CHANGED
@@ -376,16 +376,17 @@ interface GlazeColorOverrides {
376
376
  /** Saturation multiplier on the seed (0–1). Default: 1. */
377
377
  saturationFactor?: number;
378
378
  /**
379
- * Adaptation mode. Defaults vary by input form:
380
- * - String inputs (`'#1a1a1a'`, `'rgb(...)'`, etc.): `'auto'` (Möbius
381
- * curve pairs with the extended dark window to invert
382
- * `#000` `#fff` between light and dark).
383
- * - `OkhslColor` and `[r, g, b]` tuple inputs: `'fixed'` (linear,
384
- * preserves light lightness exactly).
379
+ * Adaptation mode. Defaults to `'auto'` for every input form, so
380
+ * colors automatically adapt between light and dark like an ordinary
381
+ * theme color. The default *scaling* snapshot differs by input form:
382
+ * string inputs preserve their light lightness and extend the dark
383
+ * window to `[lo, 100]` (`#000` `#fff` flip), while `OkhslColor`
384
+ * and `[r, g, b]` tuple inputs snapshot the full `globalConfig.
385
+ * lightLightness` / `globalConfig.darkLightness` windows.
385
386
  *
386
- * Pass `'fixed'` explicitly to opt a string input back into the
387
- * linear, non-inverting mapping; pass `'static'` to pin the same
388
- * lightness across every variant.
387
+ * Pass `'fixed'` explicitly to opt back into the legacy linear, non-
388
+ * inverting mapping; pass `'static'` to pin the same lightness
389
+ * across every variant.
389
390
  */
390
391
  mode?: AdaptationMode;
391
392
  /**
@@ -442,8 +443,9 @@ interface GlazeColorOverrides {
442
443
  * the way up to white.
443
444
  *
444
445
  * - **`OkhslColor` / `[r, g, b]` tuple / structured inputs**:
445
- * - `lightLightness: false` — preserve input exactly.
446
- * - `darkLightness: globalConfig.darkLightness` same window
446
+ * - `lightLightness: globalConfig.lightLightness` — same light window
447
+ * theme colors use, snapshotted at create time.
448
+ * - `darkLightness: globalConfig.darkLightness` — same dark window
447
449
  * theme colors use, snapshotted at create time.
448
450
  *
449
451
  * Passing this object replaces both fields at once. To keep one
@@ -451,7 +453,12 @@ interface GlazeColorOverrides {
451
453
  * explicitly.
452
454
  */
453
455
  interface GlazeColorScaling {
454
- /** Light-mode lightness window. `false` (default) preserves input. */
456
+ /**
457
+ * Light-mode lightness window. Snapshotted from `globalConfig` at
458
+ * create time: `false` (preserve input) for string inputs, plain
459
+ * `globalConfig.lightLightness` for object / tuple / structured
460
+ * inputs. Pass `false` to preserve input lightness in light mode.
461
+ */
455
462
  lightLightness?: false | [number, number];
456
463
  /**
457
464
  * Dark-mode lightness window. Snapshotted from `globalConfig` at
package/dist/index.mjs CHANGED
@@ -359,6 +359,12 @@ const oklabToOkhsl = (lab) => {
359
359
  0,
360
360
  toe(L)
361
361
  ];
362
+ const L_EXTREME_EPSILON = 1e-6;
363
+ if (L >= 1 - L_EXTREME_EPSILON || L <= L_EXTREME_EPSILON) return [
364
+ 0,
365
+ 0,
366
+ toe(L)
367
+ ];
362
368
  const a_ = a / C;
363
369
  const b_ = b / C;
364
370
  let h = Math.atan2(b, a) * (180 / Math.PI);
@@ -902,16 +908,25 @@ const STANDALONE_BASE = "externalBase";
902
908
  * retroactively change the resolved variants of an already-created
903
909
  * token (matches the documented "frozen at create time" semantics).
904
910
  *
905
- * String value-shorthand inputs use an extended dark window
911
+ * String value-shorthand inputs preserve their light lightness exactly
912
+ * (`lightLightness: false`) and use an extended dark window
906
913
  * `[globalConfig.darkLightness[0], 100]` so a totally-black input can
907
- * Möbius-invert to totally-white in dark mode; object / tuple /
908
- * structured inputs use `globalConfig.darkLightness` verbatim.
914
+ * Möbius-invert to totally-white in dark mode. Object / tuple /
915
+ * structured inputs snapshot both windows from `globalConfig` verbatim
916
+ * so they behave like an ordinary theme color (auto-adapted on both
917
+ * sides).
909
918
  */
910
- function defaultStandaloneScaling(extendDark) {
911
- const [lo, hi] = globalConfig.darkLightness;
919
+ function defaultStandaloneScaling(isString) {
920
+ if (isString) {
921
+ const [darkLo] = globalConfig.darkLightness;
922
+ return {
923
+ lightLightness: false,
924
+ darkLightness: [darkLo, 100]
925
+ };
926
+ }
912
927
  return {
913
- lightLightness: false,
914
- darkLightness: extendDark ? [lo, 100] : [lo, hi]
928
+ lightLightness: globalConfig.lightLightness,
929
+ darkLightness: globalConfig.darkLightness
915
930
  };
916
931
  }
917
932
  /** Reserved internal names that user-supplied `name` must not collide with. */
@@ -1940,17 +1955,18 @@ function extractOkhslFromValue(value) {
1940
1955
  * Build the `ColorMap` for a value-shorthand `glaze.color()` call.
1941
1956
  *
1942
1957
  * The user-facing color (`STANDALONE_VALUE`) defaults to `mode: 'auto'`
1943
- * for string inputs (Möbius-inverted dark variant pairs with the
1958
+ * across every value-shorthand form. String inputs pair with the
1944
1959
  * extended dark window so a totally-black input renders as totally-white
1945
- * in dark mode) and `mode: 'fixed'` for `OkhslColor` / RGB-tuple inputs
1946
- * (linear, no inversion).
1960
+ * in dark mode; `OkhslColor` / RGB-tuple inputs auto-adapt into the
1961
+ * snapshotted `globalConfig.lightLightness` / `globalConfig.darkLightness`
1962
+ * windows.
1947
1963
  *
1948
1964
  * When the user requests `contrast` or relative `lightness`, a hidden
1949
1965
  * `STANDALONE_SEED` def is synthesized at `mode: 'static'`. That keeps
1950
1966
  * the seed pinned to the literal user-provided color across all four
1951
1967
  * variants, so the contrast solver always anchors against it.
1952
1968
  */
1953
- function buildStandaloneValueDefs(main, options, inputIsString) {
1969
+ function buildStandaloneValueDefs(main, options) {
1954
1970
  const seedHue = typeof options?.hue === "number" ? options.hue : main.h;
1955
1971
  const seedSaturation = options?.saturation ?? main.s * 100;
1956
1972
  const relativeHue = typeof options?.hue === "string" ? options.hue : void 0;
@@ -1966,7 +1982,7 @@ function buildStandaloneValueDefs(main, options, inputIsString) {
1966
1982
  saturation: options?.saturationFactor,
1967
1983
  lightness: lightnessOption ?? main.l * 100,
1968
1984
  contrast: options?.contrast,
1969
- mode: options?.mode ?? (inputIsString ? "auto" : "fixed"),
1985
+ mode: options?.mode ?? "auto",
1970
1986
  opacity: options?.opacity,
1971
1987
  base: hasExternalBase ? STANDALONE_BASE : needsSeedAnchor ? STANDALONE_SEED : void 0
1972
1988
  };
@@ -2067,7 +2083,7 @@ function createColorToken(input, scaling) {
2067
2083
  const defs = { [primary]: {
2068
2084
  lightness: input.lightness,
2069
2085
  saturation: input.saturationFactor,
2070
- mode: input.mode ?? "fixed",
2086
+ mode: input.mode ?? "auto",
2071
2087
  contrast: input.contrast,
2072
2088
  opacity: input.opacity,
2073
2089
  base: hasExternalBase ? STANDALONE_BASE : needsSeedAnchor ? STANDALONE_SEED : void 0
@@ -2089,7 +2105,7 @@ function createColorTokenFromValue(value, options, scaling) {
2089
2105
  const inputIsString = typeof value === "string";
2090
2106
  const main = extractOkhslFromValue(value);
2091
2107
  const baseToken = resolveBaseToken(options?.base);
2092
- const { seedHue, seedSaturation, defs, primary } = buildStandaloneValueDefs(main, options, inputIsString);
2108
+ const { seedHue, seedSaturation, defs, primary } = buildStandaloneValueDefs(main, options);
2093
2109
  const effectiveScaling = scaling ?? defaultStandaloneScaling(inputIsString);
2094
2110
  const exportData = () => ({
2095
2111
  form: "value",
@@ -2209,17 +2225,23 @@ function isStructuredColorInput(input) {
2209
2225
  * emits (`rgb()`, `hsl()`, `okhsl()`, `oklch()`), an `OkhslColor`
2210
2226
  * object `{ h, s, l }` (0–1 ranges), or an `[r, g, b]` (0–255) tuple.
2211
2227
  *
2212
- * Defaults vary by input form:
2213
- * - String value-shorthand: `mode: 'auto'` with snapshotted scaling
2214
- * `{ lightLightness: false, darkLightness: [globalConfig.darkLightness[0], 100] }`.
2215
- * Light preserves the input exactly; dark Möbius-inverts up to 100, so
2216
- * `glaze.color('#000')` renders as `#fff` in dark mode (and
2217
- * `glaze.color('#fff')` falls to the dark `lo` floor).
2218
- * - `OkhslColor` object / RGB-tuple value-shorthand: `mode: 'fixed'`
2219
- * with `scaling: { lightLightness: false }` light preserves the
2220
- * input; dark linearly maps into `globalConfig.darkLightness`.
2221
- * - Structured form (`{ hue, saturation, lightness, ... }`):
2222
- * `mode: 'fixed'`; both windows come from `globalConfig`.
2228
+ * Defaults: every input form defaults to `mode: 'auto'` so colors
2229
+ * automatically adapt between light and dark like an ordinary theme
2230
+ * color. The scaling snapshot taken at create time differs by input
2231
+ * form:
2232
+ * - String value-shorthand: `{ lightLightness: false, darkLightness:
2233
+ * [globalConfig.darkLightness[0], 100] }`. Light preserves the input
2234
+ * exactly; dark Möbius-inverts up to 100, so `glaze.color('#000')`
2235
+ * renders as `#fff` in dark mode (and `glaze.color('#fff')` falls to
2236
+ * the dark `lo` floor).
2237
+ * - `OkhslColor` object / RGB-tuple / structured value-shorthand:
2238
+ * `{ lightLightness: globalConfig.lightLightness, darkLightness:
2239
+ * globalConfig.darkLightness }` — both windows come straight from
2240
+ * `globalConfig`, so the resulting token behaves like a theme color.
2241
+ *
2242
+ * Pass `{ mode: 'fixed' }` to opt back into the legacy linear, non-
2243
+ * inverting mapping, or `{ mode: 'static' }` to pin the same lightness
2244
+ * across every variant.
2223
2245
  *
2224
2246
  * Relative `lightness: '+N'` and `contrast: <ratio>` are anchored to
2225
2247
  * the literal seed (the value passed in) by default, pinned at