@tenphi/glaze 0.16.2 → 0.17.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/docs/api.md CHANGED
@@ -345,7 +345,7 @@ type ColorDef = RegularColorDef | ShadowColorDef | MixColorDef;
345
345
  | `base` | `string` | Name of another color in the same theme — makes this a *dependent* color. |
346
346
  | `contrast` | `HCPair<ContrastSpec>` | Contrast floor against `base`. Requires `base`. See [`contrast`](#contrast-floor). |
347
347
  | `mode` | `'auto' \| 'fixed' \| 'static'` | Adaptation mode. Default: `'auto'`. See [Adaptation modes](#adaptation-modes). |
348
- | `flip` | `boolean` | Flip out-of-bounds results (relative `tone` overshoot / unmet `contrast`) to the opposite side instead of clamping. Default: the global `autoFlip` (`true`). See [`flip`](#flip). |
348
+ | `autoFlip` | `boolean` | Flip out-of-bounds results (relative `tone` overshoot / unmet `contrast`) to the opposite side instead of clamping. Default: the global `autoFlip` (`true`). See [`autoFlip`](#autoflip). |
349
349
  | `opacity` | `number` | Fixed alpha 0–1. Output includes alpha in the CSS value. Combining with `contrast` is not recommended (a `console.warn` is emitted). |
350
350
  | `pastel` | `boolean` | Per-color override for the hue-independent "safe" chroma limit used in OKHSL↔sRGB conversions (luminance, contrast solving, output formatting). Falls through to the global / per-theme `pastel` config when omitted. Default: unset. See [Per-color `pastel`](#per-color-pastel). |
351
351
  | `role` | `RoleInput` | Semantic role against `base` (`'text'` / `'surface'` / `'border'` or an alias). Fixes APCA contrast polarity. Resolved via: explicit `role` → name inference → opposite of the base's role → `'text'`. See [Roles](#roles). |
@@ -364,20 +364,20 @@ type ColorDef = RegularColorDef | ShadowColorDef | MixColorDef;
364
364
 
365
365
  **Absolute tone** on a dependent color (`base` set) positions the color independently. In dark mode it is tone-mapped (inverted + windowed) on its own. The `contrast` solver acts as a safety net.
366
366
 
367
- **Relative tone** applies a signed delta to the base color's resolved tone. Because tone is contrast-uniform, a fixed delta yields a fixed contrast step. In dark mode with `mode: 'auto'`, the offset is anchored to the base's per-scheme tone. If `base + delta` falls outside `[0, 100]`, the result is clamped to the boundary, or — with `flip` (default on) — mirrored to the other side of the base.
367
+ **Relative tone** applies a signed delta to the base color's resolved tone. Because tone is contrast-uniform, a fixed delta yields a fixed contrast step. In dark mode with `mode: 'auto'`, the offset is anchored to the base's per-scheme tone. If `base + delta` falls outside `[0, 100]`, the result is clamped to the boundary, or — with `autoFlip` (default on) — mirrored to the other side of the base.
368
368
 
369
369
  **Extreme tone** (`'max'` / `'min'`) forces the color to the scheme's tone extreme without a contrast hack or a magic number. `'max'` resolves to author tone 100 and `'min'` to 0; both flow through scheme mapping like an absolute tone, so under `mode: 'auto'` they invert in dark (`'max'` is lightest in light, darkest in dark). Use `mode: 'static'` to pin the same extreme across schemes, or `mode: 'fixed'` to keep the same end without inverting. No `base` required.
370
370
 
371
371
  A dependent color with `base` but no `tone` inherits the base's tone (equivalent to a delta of 0).
372
372
 
373
- #### `flip`
373
+ #### `autoFlip`
374
374
 
375
- `flip` governs what happens when a result would fall outside its valid range:
375
+ `autoFlip` governs what happens when a result would fall outside its valid range:
376
376
 
377
- - **Relative `tone` overshoot:** when `base ± delta` exceeds `[0, 100]`, `flip` mirrors the delta to the other side of the base (e.g. `'+30'` becomes `'-30'`) instead of clamping to the boundary.
378
- - **`contrast` direction:** when the requested tone direction can't meet the floor, `flip` lets the solver try the opposite side (the same behavior as the global `autoFlip`).
377
+ - **Relative `tone` overshoot:** when `base ± delta` exceeds `[0, 100]`, `autoFlip` mirrors the delta to the other side of the base (e.g. `'+30'` becomes `'-30'`) instead of clamping to the boundary.
378
+ - **`contrast` direction:** when the requested tone direction can't meet the floor, `autoFlip` lets the solver try the opposite side (the same behavior as the global `autoFlip`).
379
379
 
380
- `flip` defaults to the global `autoFlip` (`true`). Set `flip: false` on a color to clamp instead of mirror — useful when you want a relative offset to stay on the authored side of the base, or to keep an unmet contrast pinned to one direction's extreme.
380
+ `autoFlip` defaults to the global `autoFlip` (`true`). Set `autoFlip: false` on a color to clamp instead of mirror — useful when you want a relative offset to stay on the authored side of the base, or to keep an unmet contrast pinned to one direction's extreme.
381
381
 
382
382
  #### `contrast` (floor)
383
383
 
@@ -417,7 +417,7 @@ APCA preset keywords (Bronze Simple Mode conformance levels, role-independent):
417
417
 
418
418
  The floor is applied independently per scheme — if the `tone` already satisfies it the tone is kept, otherwise the solver searches in tone (contrast-uniform → a closed-form WCAG seed and fast convergence) until the target is met.
419
419
 
420
- By default, the solver crosses to the opposite side of the base color when the requested tone direction cannot satisfy the floor. This is controlled per-color by [`flip`](#flip) (which defaults to the global `autoFlip`). Set `glaze.configure({ autoFlip: false })` — or `flip: false` on a single color — to keep strict directionality: unmet colors pin to that direction's 0 or 100 tone extreme instead of falling back to the original requested value.
420
+ By default, the solver crosses to the opposite side of the base color when the requested tone direction cannot satisfy the floor. This is controlled per-color by [`autoFlip`](#autoflip) (which defaults to the global `autoFlip`). Set `glaze.configure({ autoFlip: false })` — or `autoFlip: false` on a single color — to keep strict directionality: unmet colors pin to that direction's 0 or 100 tone extreme instead of falling back to the original requested value.
421
421
 
422
422
  **Full tone spectrum in HC mode:** in high-contrast variants the `lightTone` and `darkTone` window constraints are bypassed entirely (the window is forced to `[0, 100]`). Colors can reach the full range, maximizing perceivable contrast.
423
423
 
@@ -576,7 +576,7 @@ glaze.color(color: GlazeFromInput | GlazeColorInput | GlazeColorValue, config?:
576
576
  | `tone` | `HCPair<number \| ExtremeValue>` | 0–100 (contrast-uniform) or `'max'`/`'min'`, optional HC pair. |
577
577
  | `saturationFactor` | `number` | Multiplier on the seed (0–1). Default: `1`. |
578
578
  | `mode` | `AdaptationMode` | Default: `'auto'`. |
579
- | `flip` | `boolean` | Flip out-of-bounds results instead of clamping. Default: global `autoFlip`. |
579
+ | `autoFlip` | `boolean` | Flip out-of-bounds results instead of clamping. Default: global `autoFlip`. |
580
580
  | `opacity` | `number` | Fixed alpha 0–1. |
581
581
  | `base` | `GlazeColorToken \| GlazeColorValue` | Optional dependency. See [Pairing colors](#pairing-colors). |
582
582
  | `contrast` | `HCPair<ContrastSpec>` | Contrast floor against `base` (WCAG or APCA). Without `base`, anchored to the literal seed. |
@@ -594,7 +594,7 @@ glaze.color(color: GlazeFromInput | GlazeColorInput | GlazeColorValue, config?:
594
594
  | `tone` | Number (absolute 0–100), `'+N'`/`'-N'`, or `'max'`/`'min'`. Without `base`, relative anchors to the seed; with `base`, anchors to `base`'s tone per scheme. |
595
595
  | `saturationFactor` | Multiplier on the seed (0–1). |
596
596
  | `mode` | `'auto'` (default) / `'fixed'` / `'static'`. |
597
- | `flip` | Flip out-of-bounds results instead of clamping. Default: global `autoFlip`. |
597
+ | `autoFlip` | Flip out-of-bounds results instead of clamping. Default: global `autoFlip`. |
598
598
  | `contrast` | Contrast floor (WCAG or APCA). Without `base`, anchored to the literal seed; with `base`, solved per scheme. |
599
599
  | `base` | `GlazeColorToken` or raw `GlazeColorValue`. See [Pairing colors](#pairing-colors). |
600
600
  | `opacity` | Fixed alpha 0–1. Combining with `contrast` is not recommended — `console.warn` is emitted. |
@@ -659,7 +659,7 @@ The optional `config` second argument (`GlazeConfigOverride`) overrides the reso
659
659
  | `lightTone` | `[10, 100]` | Light tone window: `[lo, hi]`, `{ lo, hi, eps }`, or `false` (disable clamping). |
660
660
  | `darkTone` | `[15, 95]` | Dark tone window: `[lo, hi]`, `{ lo, hi, eps }`, or `false` (disable clamping). |
661
661
  | `darkDesaturation` | `0.1` | Saturation reduction in dark scheme (0–1). |
662
- | `autoFlip` | `true` | Default for each color's `flip`: when solving `contrast` (or applying a relative `tone` that overshoots), allow crossing to the opposite side instead of clamping. |
662
+ | `autoFlip` | `true` | Default for each color's `autoFlip`: when solving `contrast` (or applying a relative `tone` that overshoots), allow crossing to the opposite side instead of clamping. |
663
663
  | `shadowTuning` | `undefined` | Default shadow tuning (meaningful for themes; harmless on color tokens). |
664
664
 
665
665
  Config overrides apply to both `glaze.color()` tokens and `glaze()` themes:
@@ -1216,7 +1216,7 @@ Light: accent-fill tone=52, accent-text tone='+20' → lighter than the fill
1216
1216
  Dark: accent-fill maps into the dark window, sign preserved
1217
1217
  ```
1218
1218
 
1219
- Offsets that would push past `[0, 100]` clamp to the boundary, or — with `flip` (default on) — mirror to the other side of the base. Set `flip: false` to keep the authored side and clamp instead.
1219
+ Offsets that would push past `[0, 100]` clamp to the boundary, or — with `autoFlip` (default on) — mirror to the other side of the base. Set `autoFlip: false` to keep the authored side and clamp instead.
1220
1220
 
1221
1221
  **`static`** — no adaptation, same tone in every scheme.
1222
1222
 
@@ -1304,7 +1304,7 @@ A `ToneWindow` is `[lo, hi]` (OKHSL-lightness endpoints, reference eps — the c
1304
1304
  | `modes.dark` | `true` | Include dark variants in exports. |
1305
1305
  | `modes.highContrast` | `false` | Include HC variants. |
1306
1306
  | `shadowTuning` | `undefined` | Default tuning for all shadow colors. Per-color tuning merges field-by-field. |
1307
- | `autoFlip` | `true` | Default for each color's `flip`. When solving `contrast` (or applying a relative `tone` that overshoots `[0, 100]`), allow crossing to the opposite side instead of clamping. With `false`, only the requested direction is considered; unmet contrasts pin the tone to that direction's extreme (and emit a warning) and overshooting offsets clamp to the boundary. Override per color via [`flip`](#flip). |
1307
+ | `autoFlip` | `true` | Default for each color's `autoFlip`. When solving `contrast` (or applying a relative `tone` that overshoots `[0, 100]`), allow crossing to the opposite side instead of clamping. With `false`, only the requested direction is considered; unmet contrasts pin the tone to that direction's extreme (and emit a warning) and overshooting offsets clamp to the boundary. Override per color via [`autoFlip`](#autoflip). |
1308
1308
  | `pastel` | `false` | Hue-independent "safe" chroma limit across all colors so scaling saturation never exceeds the sRGB boundary at any hue for the given lightness. Override per color via [`pastel`](#per-color-pastel). |
1309
1309
  | `inferRole` | `true` | Infer each color's [`role`](#roles) from its name when no explicit `role` is set. Set to `false` to opt out of name-based inference (the base-opposite and foreground-default fallbacks still apply). |
1310
1310
 
@@ -1350,7 +1350,7 @@ Resolution priority (highest first):
1350
1350
  | Relative `tone` without `base` in a **theme** color | Validation error |
1351
1351
  | `contrast` without `base` in `glaze.color()` | Anchors against the literal seed (no error) |
1352
1352
  | Relative `tone` without `base` in `glaze.color()` | Anchors against the literal seed (no error) |
1353
- | Relative `tone` overshoots `[0, 100]` | Mirror to the other side of the base (`flip` on, default), or clamp to the boundary (`flip` off) |
1353
+ | Relative `tone` overshoots `[0, 100]` | Mirror to the other side of the base (`autoFlip` on, default), or clamp to the boundary (`autoFlip` off) |
1354
1354
  | `tone` resolves outside 0–100 | Clamp silently |
1355
1355
  | `'max'` / `'min'` without `base` | Allowed — resolves to the scheme's tone extreme (root color) |
1356
1356
  | `saturation` outside 0–1 | Clamp silently |
@@ -209,14 +209,14 @@ defaultTheme.colors({
209
209
  base: 'disabled-surface',
210
210
  tone: '+18',
211
211
  saturation: 0.3,
212
- flip: false,
212
+ autoFlip: false,
213
213
  inherit: false,
214
214
  },
215
215
  });
216
216
  ```
217
217
 
218
218
  This says exactly what the pair should do: the chip sits a few tone steps off
219
- the page, and the label sits a muted distance from the chip. `flip: false` keeps
219
+ the page, and the label sits a muted distance from the chip. `autoFlip: false` keeps
220
220
  the relative label offset on the authored side when it reaches the edge.
221
221
 
222
222
  Use contrast instead when the chip must hit an explicit accessibility floor:
@@ -324,7 +324,7 @@ defaultTheme.colors({
324
324
  base: 'accent-disabled-surface',
325
325
  tone: '+18',
326
326
  saturation: 0.4,
327
- flip: false,
327
+ autoFlip: false,
328
328
  },
329
329
  });
330
330
  ```
package/docs/migration.md CHANGED
@@ -59,12 +59,14 @@ For colors that should sit at the scheme's tone extreme (pure-white knockouts, n
59
59
  // before — low contrast as a proxy for "stay near the surface"
60
60
  'disabled-text': { base: 'chip', tone: '+1', contrast: 1.51, mode: 'fixed' }
61
61
  // after — say it directly with tone
62
- 'disabled-text': { base: 'chip', tone: '+18', saturation: 0.4, flip: false }
62
+ 'disabled-text': { base: 'chip', tone: '+18', saturation: 0.4, autoFlip: false }
63
63
  ```
64
64
 
65
- ### The `flip` prop
65
+ ### The `autoFlip` prop (previously `flip`)
66
66
 
67
- Relative `tone` offsets that overshoot `[0, 100]` now **mirror to the other side of the base by default** (controlled by the new per-color `flip`, which inherits the global `autoFlip`, default `true`). Previously such offsets always clamped to the boundary. If you relied on clamping — e.g. `tone: '+48'` to stack a color up to 100 set `flip: false` on that color (or `glaze.configure({ autoFlip: false })` globally) to restore the clamping behavior. `flip` also governs the contrast solver's direction (its previous sole role).
67
+ The per-color configuration property `flip` has been renamed to `autoFlip` to align with the global `autoFlip` configuration option and to avoid confusion with dark-mode/scheme tone inversion (which is handled automatically by the system).
68
+
69
+ Relative `tone` offsets that overshoot `[0, 100]` now **mirror to the other side of the base by default** (controlled by the per-color `autoFlip`, which inherits the global `autoFlip`, default `true`). Previously such offsets always clamped to the boundary. If you relied on clamping — e.g. `tone: '+48'` to stack a color up to 100 — set `autoFlip: false` on that color (or `glaze.configure({ autoFlip: false })` globally) to restore the clamping behavior. `autoFlip` also governs the contrast solver's direction (its previous sole role).
68
70
 
69
71
  ### Resolved variants store tone
70
72
 
@@ -362,7 +364,7 @@ After migration, mark every default-only token (borders, shadows, disabled chip,
362
364
  | Brand color flips to its complement in dark mode. | Default `mode: 'auto'` inverts the tone. | Set `mode: 'fixed'` so the tone is remapped (not inverted). |
363
365
  | Brand text washes out against the dark surface. | Foreground was anchored to `accent-surface` (the brand fill), so contrast was only enforced against that fill — not the actual surface. | Anchor `accent-text` etc. to `surface` with `mode: 'auto'`. |
364
366
  | Tokens look right in light, broken in HC. | The HC pass bypasses the tone window — solver runs over the full `[0, 100]` range. | Add explicit `[normal, hc]` pairs to `tone` / `contrast` for the affected tokens. |
365
- | A relative `tone` like `'+48'` lands on the *wrong* (darker) side of its base. | Overshooting offsets now mirror to the other side of the base by default (`flip` inherits `autoFlip`). | Set `flip: false` on the color to clamp to the boundary instead, or use `tone: 'max'`/`'min'` to force the extreme. |
367
+ | A relative `tone` like `'+48'` lands on the *wrong* (darker) side of its base. | Overshooting offsets now mirror to the other side of the base by default (`autoFlip` inherits `autoFlip`). | Set `autoFlip: false` on the color to clamp to the boundary instead, or use `tone: 'max'`/`'min'` to force the extreme. |
366
368
  | `palette.tokens()` emits unexpected unprefixed names. | A `primary` was set on the palette (or per-call) and is duplicating the theme's tokens without prefix. | Pass `primary: false` to disable for that export, or rename `glaze.palette(themes, { primary })`. |
367
369
  | `console.warn: token "foo" collides with theme "bar"`. | Two themes resolved to the same output key under your prefix config. | Adjust the prefix map so each token is unique, or accept the first-write-wins behavior. |
368
370
  | `console.warn: color "X" cannot meet contrast`. | The requested contrast target is physically unreachable for the color's hue/saturation against its base. | Lower the floor, change the base, or accept the closest passing variant. Use the `name` override on standalone colors to make the warning identifiable. |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tenphi/glaze",
3
- "version": "0.16.2",
3
+ "version": "0.17.0",
4
4
  "description": "OKHST-based color theme generator with WCAG contrast solving for light, dark, and high-contrast schemes",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",