@tenphi/glaze 0.10.1 → 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/README.md CHANGED
@@ -282,27 +282,33 @@ adapt to both lightness windows. The defaults vary by input form,
282
282
  because string inputs are typically end-user values (color pickers,
283
283
  theme settings) where natural light/dark inversion is the expectation:
284
284
 
285
+ Every input form defaults to **`mode: 'auto'`** so the resolved token
286
+ adapts between light and dark like an ordinary theme color. The
287
+ *scaling* snapshot taken at create time differs by input form:
288
+
285
289
  - **String value-shorthand** (hex, `rgb()`, `hsl()`, `okhsl()`,
286
290
  `oklch()`):
287
291
  - Light variant preserves the input lightness exactly.
288
292
  - Dark variant is **Möbius-inverted** into `[globalConfig.darkLightness[0], 100]`,
289
293
  so `glaze.color('#000')` renders as `#fff` in dark mode and
290
294
  `glaze.color('#fff')` falls to the dark `lo` floor (default `0.15`).
291
- - Adaptation mode defaults to `'auto'`.
292
295
  - The dark `lo` is snapshotted from `globalConfig` at color-creation
293
296
  time, matching how an explicit `scaling.darkLightness: [lo, hi]`
294
297
  behaves.
295
298
 
296
299
  - **Object / tuple value-shorthand** (`{ h, s, l }`, `[r, g, b]`) and
297
300
  the **structured form** (`{ hue, saturation, lightness, ... }`):
298
- - Light variant preserves the input lightness exactly.
299
- - Dark variant is linearly mapped into `globalConfig.darkLightness`
300
- (default `[15, 95]`), snapshotted at color-creation time so later
301
+ - Both light and dark variants are mapped through
302
+ `globalConfig.lightLightness` / `globalConfig.darkLightness`
303
+ (defaults `[10, 100]` / `[15, 95]`) the same windows a theme color
304
+ uses. With the `'auto'` default the dark variant is Möbius-inverted
305
+ into that dark window, so a near-white seed lands at a near-dark
306
+ dark variant.
307
+ - Both windows are snapshotted at color-creation time so later
301
308
  `glaze.configure()` calls don't retroactively change exported tokens.
302
- - Adaptation mode defaults to `'fixed'` (linear, no Möbius curve).
303
309
 
304
- To opt back into the old fixed-linear default for string inputs, pass
305
- either `{ mode: 'fixed' }` as the second arg, or supply an explicit
310
+ To opt back into the legacy fixed-linear default (no Möbius inversion),
311
+ pass `{ mode: 'fixed' }` as the second arg, or supply an explicit
306
312
  `scaling` as the third arg (see [Lightness scaling](#lightness-scaling)).
307
313
 
308
314
  ```ts
@@ -373,7 +379,7 @@ All overrides:
373
379
  | `saturation` | Override seed saturation (0–100) |
374
380
  | `lightness` | Number (absolute 0–100) or `'+N'`/`'-N'`. Without `base`, relative is anchored to the literal seed; with `base`, anchored to `base`'s lightness per scheme. Supports `[normal, hc]` pairs |
375
381
  | `saturationFactor` | Multiplier on seed (0–1, default 1) |
376
- | `mode` | `'auto'` (default for string inputs) / `'fixed'` (default for object / tuple / structured inputs) / `'static'` — see [Adaptation Modes](#adaptation-modes) |
382
+ | `mode` | `'auto'` (default for every input form) / `'fixed'` / `'static'` — see [Adaptation Modes](#adaptation-modes) |
377
383
  | `contrast` | WCAG floor. Without `base`, anchored to the literal seed; with `base`, solved per scheme against `base`'s resolved variant. Same shape as `RegularColorDef.contrast`. When the target can't be physically met, `glaze` emits a `console.warn` and returns the closest passing variant |
378
384
  | `base` | Another `glaze.color()` token **or** a raw `GlazeColorValue` (hex / `rgb()` / `OkhslColor` / `[r, g, b]`). Raw values are auto-wrapped via `glaze.color(value)` so they pick up the same auto-invert defaults as an explicit wrap. When set, `contrast` and relative `lightness` anchor to it per scheme; relative `hue` still anchors to the seed |
379
385
  | `opacity` | Fixed alpha 0–1 applied to every variant. Surfaces in `rgb(... / A)`, `okhsl(... / A)`, etc. Combining with `contrast` is not recommended (perceived lightness becomes unpredictable) — `glaze` emits a `console.warn` |
@@ -1423,7 +1429,7 @@ brand.colors({ surface: { lightness: 97 }, text: { base: 'surface', lightness: '
1423
1429
  | `glaze.fromHex(hex)` | Create a theme from a hex color (`#rgb` or `#rrggbb`) |
1424
1430
  | `glaze.fromRgb(r, g, b)` | Create a theme from RGB values (0–255) |
1425
1431
  | `glaze.color(input, scaling?)` | Create a standalone color token from `{ hue, saturation, lightness, opacity?, contrast?, base?, name?, ... }`. Optional `scaling` overrides the lightness windows |
1426
- | `glaze.color(value, overrides?, scaling?)` | Create a standalone color token from a hex string (3/6/8 digits), an `rgb()` / `hsl()` / `okhsl()` / `oklch()` string, an `{ h, s, l }` OKHSL object, or an `[r, g, b]` (0–255) tuple. Overrides accept absolute or relative `hue` / `lightness`, `saturation`, `mode`, `contrast`, `opacity`, `name`, and `base` (a `GlazeColorToken` or any `GlazeColorValue`; raw values are auto-wrapped). When `base` is set, `contrast` and relative `lightness` are anchored to the base per scheme — see [Pairing Colors](#pairing-colors). String inputs default to `mode: 'auto'` with the dark window extended to upper `100`; object / tuple inputs default to `mode: 'fixed'`. |
1432
+ | `glaze.color(value, overrides?, scaling?)` | Create a standalone color token from a hex string (3/6/8 digits), an `rgb()` / `hsl()` / `okhsl()` / `oklch()` string, an `{ h, s, l }` OKHSL object, or an `[r, g, b]` (0–255) tuple. Overrides accept absolute or relative `hue` / `lightness`, `saturation`, `mode`, `contrast`, `opacity`, `name`, and `base` (a `GlazeColorToken` or any `GlazeColorValue`; raw values are auto-wrapped). When `base` is set, `contrast` and relative `lightness` are anchored to the base per scheme — see [Pairing Colors](#pairing-colors). Every input form defaults to `mode: 'auto'`; string inputs additionally preserve light lightness exactly and extend the dark window to `[lo, 100]`, while object / tuple / structured inputs snapshot both windows from `globalConfig.lightLightness` / `globalConfig.darkLightness`. |
1427
1433
  | `glaze.colorFrom(data)` | Rehydrate a `glaze.color()` token from a `.export()` snapshot. Inverse of `token.export()` — see [Persisting Standalone Colors](#persisting-standalone-colors) |
1428
1434
  | `glaze.shadow(input)` | Compute a standalone shadow color (returns `ResolvedColorVariant`). `bg` / `fg` accept any `GlazeColorValue` form |
1429
1435
  | `glaze.format(variant, format?)` | Format any `ResolvedColorVariant` as a CSS string |
package/dist/index.cjs CHANGED
@@ -910,16 +910,25 @@ const STANDALONE_BASE = "externalBase";
910
910
  * retroactively change the resolved variants of an already-created
911
911
  * token (matches the documented "frozen at create time" semantics).
912
912
  *
913
- * String value-shorthand inputs use an extended dark window
913
+ * String value-shorthand inputs preserve their light lightness exactly
914
+ * (`lightLightness: false`) and use an extended dark window
914
915
  * `[globalConfig.darkLightness[0], 100]` so a totally-black input can
915
- * Möbius-invert to totally-white in dark mode; object / tuple /
916
- * structured inputs use `globalConfig.darkLightness` verbatim.
916
+ * Möbius-invert to totally-white in dark mode. Object / tuple /
917
+ * structured inputs snapshot both windows from `globalConfig` verbatim
918
+ * so they behave like an ordinary theme color (auto-adapted on both
919
+ * sides).
917
920
  */
918
- function defaultStandaloneScaling(extendDark) {
919
- const [lo, hi] = globalConfig.darkLightness;
921
+ function defaultStandaloneScaling(isString) {
922
+ if (isString) {
923
+ const [darkLo] = globalConfig.darkLightness;
924
+ return {
925
+ lightLightness: false,
926
+ darkLightness: [darkLo, 100]
927
+ };
928
+ }
920
929
  return {
921
- lightLightness: false,
922
- darkLightness: extendDark ? [lo, 100] : [lo, hi]
930
+ lightLightness: globalConfig.lightLightness,
931
+ darkLightness: globalConfig.darkLightness
923
932
  };
924
933
  }
925
934
  /** Reserved internal names that user-supplied `name` must not collide with. */
@@ -1948,17 +1957,18 @@ function extractOkhslFromValue(value) {
1948
1957
  * Build the `ColorMap` for a value-shorthand `glaze.color()` call.
1949
1958
  *
1950
1959
  * The user-facing color (`STANDALONE_VALUE`) defaults to `mode: 'auto'`
1951
- * for string inputs (Möbius-inverted dark variant pairs with the
1960
+ * across every value-shorthand form. String inputs pair with the
1952
1961
  * extended dark window so a totally-black input renders as totally-white
1953
- * in dark mode) and `mode: 'fixed'` for `OkhslColor` / RGB-tuple inputs
1954
- * (linear, no inversion).
1962
+ * in dark mode; `OkhslColor` / RGB-tuple inputs auto-adapt into the
1963
+ * snapshotted `globalConfig.lightLightness` / `globalConfig.darkLightness`
1964
+ * windows.
1955
1965
  *
1956
1966
  * When the user requests `contrast` or relative `lightness`, a hidden
1957
1967
  * `STANDALONE_SEED` def is synthesized at `mode: 'static'`. That keeps
1958
1968
  * the seed pinned to the literal user-provided color across all four
1959
1969
  * variants, so the contrast solver always anchors against it.
1960
1970
  */
1961
- function buildStandaloneValueDefs(main, options, inputIsString) {
1971
+ function buildStandaloneValueDefs(main, options) {
1962
1972
  const seedHue = typeof options?.hue === "number" ? options.hue : main.h;
1963
1973
  const seedSaturation = options?.saturation ?? main.s * 100;
1964
1974
  const relativeHue = typeof options?.hue === "string" ? options.hue : void 0;
@@ -1974,7 +1984,7 @@ function buildStandaloneValueDefs(main, options, inputIsString) {
1974
1984
  saturation: options?.saturationFactor,
1975
1985
  lightness: lightnessOption ?? main.l * 100,
1976
1986
  contrast: options?.contrast,
1977
- mode: options?.mode ?? (inputIsString ? "auto" : "fixed"),
1987
+ mode: options?.mode ?? "auto",
1978
1988
  opacity: options?.opacity,
1979
1989
  base: hasExternalBase ? STANDALONE_BASE : needsSeedAnchor ? STANDALONE_SEED : void 0
1980
1990
  };
@@ -2075,7 +2085,7 @@ function createColorToken(input, scaling) {
2075
2085
  const defs = { [primary]: {
2076
2086
  lightness: input.lightness,
2077
2087
  saturation: input.saturationFactor,
2078
- mode: input.mode ?? "fixed",
2088
+ mode: input.mode ?? "auto",
2079
2089
  contrast: input.contrast,
2080
2090
  opacity: input.opacity,
2081
2091
  base: hasExternalBase ? STANDALONE_BASE : needsSeedAnchor ? STANDALONE_SEED : void 0
@@ -2097,7 +2107,7 @@ function createColorTokenFromValue(value, options, scaling) {
2097
2107
  const inputIsString = typeof value === "string";
2098
2108
  const main = extractOkhslFromValue(value);
2099
2109
  const baseToken = resolveBaseToken(options?.base);
2100
- const { seedHue, seedSaturation, defs, primary } = buildStandaloneValueDefs(main, options, inputIsString);
2110
+ const { seedHue, seedSaturation, defs, primary } = buildStandaloneValueDefs(main, options);
2101
2111
  const effectiveScaling = scaling ?? defaultStandaloneScaling(inputIsString);
2102
2112
  const exportData = () => ({
2103
2113
  form: "value",
@@ -2217,17 +2227,23 @@ function isStructuredColorInput(input) {
2217
2227
  * emits (`rgb()`, `hsl()`, `okhsl()`, `oklch()`), an `OkhslColor`
2218
2228
  * object `{ h, s, l }` (0–1 ranges), or an `[r, g, b]` (0–255) tuple.
2219
2229
  *
2220
- * Defaults vary by input form:
2221
- * - String value-shorthand: `mode: 'auto'` with snapshotted scaling
2222
- * `{ lightLightness: false, darkLightness: [globalConfig.darkLightness[0], 100] }`.
2223
- * Light preserves the input exactly; dark Möbius-inverts up to 100, so
2224
- * `glaze.color('#000')` renders as `#fff` in dark mode (and
2225
- * `glaze.color('#fff')` falls to the dark `lo` floor).
2226
- * - `OkhslColor` object / RGB-tuple value-shorthand: `mode: 'fixed'`
2227
- * with `scaling: { lightLightness: false }` light preserves the
2228
- * input; dark linearly maps into `globalConfig.darkLightness`.
2229
- * - Structured form (`{ hue, saturation, lightness, ... }`):
2230
- * `mode: 'fixed'`; both windows come from `globalConfig`.
2230
+ * Defaults: every input form defaults to `mode: 'auto'` so colors
2231
+ * automatically adapt between light and dark like an ordinary theme
2232
+ * color. The scaling snapshot taken at create time differs by input
2233
+ * form:
2234
+ * - String value-shorthand: `{ lightLightness: false, darkLightness:
2235
+ * [globalConfig.darkLightness[0], 100] }`. Light preserves the input
2236
+ * exactly; dark Möbius-inverts up to 100, so `glaze.color('#000')`
2237
+ * renders as `#fff` in dark mode (and `glaze.color('#fff')` falls to
2238
+ * the dark `lo` floor).
2239
+ * - `OkhslColor` object / RGB-tuple / structured value-shorthand:
2240
+ * `{ lightLightness: globalConfig.lightLightness, darkLightness:
2241
+ * globalConfig.darkLightness }` — both windows come straight from
2242
+ * `globalConfig`, so the resulting token behaves like a theme color.
2243
+ *
2244
+ * Pass `{ mode: 'fixed' }` to opt back into the legacy linear, non-
2245
+ * inverting mapping, or `{ mode: 'static' }` to pin the same lightness
2246
+ * across every variant.
2231
2247
  *
2232
2248
  * Relative `lightness: '+N'` and `contrast: <ratio>` are anchored to
2233
2249
  * the literal seed (the value passed in) by default, pinned at