@tenphi/glaze 0.14.0 → 0.15.1
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 +1 -1
- package/dist/index.cjs +206 -159
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +83 -28
- package/dist/index.d.mts +83 -28
- package/dist/index.mjs +206 -160
- package/dist/index.mjs.map +1 -1
- package/docs/api.md +38 -9
- package/docs/migration.md +1 -1
- package/docs/okhst.md +1 -9
- package/package.json +6 -2
package/docs/api.md
CHANGED
|
@@ -219,6 +219,7 @@ type ColorDef = RegularColorDef | ShadowColorDef | MixColorDef;
|
|
|
219
219
|
| `mode` | `'auto' \| 'fixed' \| 'static'` | Adaptation mode. Default: `'auto'`. See [Adaptation modes](#adaptation-modes). |
|
|
220
220
|
| `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). |
|
|
221
221
|
| `opacity` | `number` | Fixed alpha 0–1. Output includes alpha in the CSS value. Combining with `contrast` is not recommended (a `console.warn` is emitted). |
|
|
222
|
+
| `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). |
|
|
222
223
|
| `inherit` | `boolean` | Whether this color is inherited by child themes via `extend()`. Default: `true`. Set to `false` to make the color local to the current theme. |
|
|
223
224
|
|
|
224
225
|
#### Tone values
|
|
@@ -299,6 +300,38 @@ theme.colors({
|
|
|
299
300
|
|
|
300
301
|
Relative hue is always relative to the **theme seed hue**, not to a base color.
|
|
301
302
|
|
|
303
|
+
#### Per-color `pastel`
|
|
304
|
+
|
|
305
|
+
`pastel: true` on a single color def overrides the global / per-theme `pastel` config for that color only. It toggles the hue-independent "safe" chroma limit used in every OKHSL↔sRGB conversion that touches this color: luminance calculations during contrast solving, gamut clamping during sRGB blend / mix edges, and output formatting. The effective flag is carried on the resolved variant (`ResolvedColorVariant.pastel`) so formatting matches the gamut mapping applied during resolution.
|
|
306
|
+
|
|
307
|
+
```ts
|
|
308
|
+
const theme = glaze(280, 80);
|
|
309
|
+
theme.colors({
|
|
310
|
+
plain: { tone: 50, saturation: 1 },
|
|
311
|
+
soft: { tone: 50, saturation: 1, pastel: true },
|
|
312
|
+
});
|
|
313
|
+
// theme.resolve().get('soft')!.light.pastel === true
|
|
314
|
+
// theme.css().light contains different rgb() triples for `--plain` and `--soft`
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
Omit the field to inherit the global / per-theme `pastel` config — useful for keeping the default behavior while opting a single accent into the pastel gamut.
|
|
318
|
+
|
|
319
|
+
The flag is part of the def object, so `extend()` copies it through to child themes alongside the rest of the def. Override it again on the child to flip a single color back:
|
|
320
|
+
|
|
321
|
+
```ts
|
|
322
|
+
const parent = glaze(280, 80);
|
|
323
|
+
parent.colors({ soft: { tone: 50, saturation: 1, pastel: true } });
|
|
324
|
+
|
|
325
|
+
const child = parent.extend({
|
|
326
|
+
colors: { soft: { tone: 50, saturation: 1, pastel: false } },
|
|
327
|
+
});
|
|
328
|
+
// child.resolve().get('soft')!.light.pastel === false
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
> **Note:** Per-color `pastel` is also supported on `ShadowColorDef` and `MixColorDef` (see the tables above). For shadows the math itself happens in OKHSL space, so the flag mainly controls the gamut-mapped output formatting and any luminance verification for that variant.
|
|
332
|
+
>
|
|
333
|
+
> Standalone `glaze.color()` tokens accept the same `pastel` field on both the structured (`GlazeColorInput`) and value-shorthand (`GlazeColorOverrides`) forms, and it survives the `export()` / `glaze.colorFrom()` round-trip.
|
|
334
|
+
|
|
302
335
|
### `ShadowColorDef`
|
|
303
336
|
|
|
304
337
|
| Field | Type | Description |
|
|
@@ -308,6 +341,7 @@ Relative hue is always relative to the **theme seed hue**, not to a base color.
|
|
|
308
341
|
| `fg` | `string` | Optional foreground color name for tinting and intensity modulation. Must reference a non-shadow color. Omit for an achromatic shadow at full user-specified intensity. |
|
|
309
342
|
| `intensity` | `HCPair<number>` | Shadow intensity, 0–100. Supports HC pairs. |
|
|
310
343
|
| `tuning` | `ShadowTuning` | Per-color tuning overrides. Merged field-by-field with the global `shadowTuning`. |
|
|
344
|
+
| `pastel` | `boolean` | Per-color `pastel` override. See [Per-color `pastel`](#per-color-pastel). |
|
|
311
345
|
| `inherit` | `boolean` | Inheritance flag, default `true`. |
|
|
312
346
|
|
|
313
347
|
See [Shadows](#shadows) below for the algorithm and tuning details.
|
|
@@ -323,6 +357,7 @@ See [Shadows](#shadows) below for the algorithm and tuning details.
|
|
|
323
357
|
| `blend` | `'opaque' \| 'transparent'` | Default `'opaque'`. |
|
|
324
358
|
| `space` | `'okhsl' \| 'srgb'` | Interpolation space for opaque blending. Default `'okhsl'`. Ignored for `'transparent'` (always composites in linear sRGB). |
|
|
325
359
|
| `contrast` | `HCPair<ContrastSpec>` | Optional contrast floor against `base` (WCAG or APCA — see [`contrast`](#contrast-floor)). The solver adjusts the mix ratio (opaque) or opacity (transparent). |
|
|
360
|
+
| `pastel` | `boolean` | Per-color `pastel` override. See [Per-color `pastel`](#per-color-pastel). |
|
|
326
361
|
| `inherit` | `boolean` | Inheritance flag, default `true`. |
|
|
327
362
|
|
|
328
363
|
See [Mix colors](#mix-colors) below.
|
|
@@ -378,6 +413,7 @@ glaze.color(color: GlazeFromInput | GlazeColorInput | GlazeColorValue, config?:
|
|
|
378
413
|
| `opacity` | `number` | Fixed alpha 0–1. |
|
|
379
414
|
| `base` | `GlazeColorToken \| GlazeColorValue` | Optional dependency. See [Pairing colors](#pairing-colors). |
|
|
380
415
|
| `contrast` | `HCPair<ContrastSpec>` | Contrast floor against `base` (WCAG or APCA). Without `base`, anchored to the literal seed. |
|
|
416
|
+
| `pastel` | `boolean` | Per-color `pastel` override. Falls through to the global / per-theme `pastel` config when omitted. See [Per-color `pastel`](#per-color-pastel). |
|
|
381
417
|
| `name` | `string` | Debug label for warnings; doesn't change output keys. Reserved names (`'value'`, `'seed'`, `'externalBase'`) are rejected. |
|
|
382
418
|
|
|
383
419
|
`GlazeFromInput` (from form) is `{ from: GlazeColorValue, ...colorOverrides }`:
|
|
@@ -394,6 +430,7 @@ glaze.color(color: GlazeFromInput | GlazeColorInput | GlazeColorValue, config?:
|
|
|
394
430
|
| `contrast` | Contrast floor (WCAG or APCA). Without `base`, anchored to the literal seed; with `base`, solved per scheme. |
|
|
395
431
|
| `base` | `GlazeColorToken` or raw `GlazeColorValue`. See [Pairing colors](#pairing-colors). |
|
|
396
432
|
| `opacity` | Fixed alpha 0–1. Combining with `contrast` is not recommended — `console.warn` is emitted. |
|
|
433
|
+
| `pastel` | Per-color `pastel` override. Falls through to the global / per-theme `pastel` config when omitted. See [Per-color `pastel`](#per-color-pastel). |
|
|
397
434
|
| `name` | Debug label only — surfaces in warnings/errors. Does not change output keys. |
|
|
398
435
|
|
|
399
436
|
Named CSS colors (`'red'`, `'blueviolet'`) are not supported.
|
|
@@ -404,7 +441,7 @@ Every input form defaults to `mode: 'auto'` so the resolved token adapts between
|
|
|
404
441
|
|
|
405
442
|
- **Value-shorthand** (bare strings, value objects, and `{ from, ...overrides }`):
|
|
406
443
|
- Light variant preserves the input tone exactly (`lightTone: false`).
|
|
407
|
-
- All other config fields (`darkTone`, `darkDesaturation`, `
|
|
444
|
+
- All other config fields (`darkTone`, `darkDesaturation`, `autoFlip`) snapshot from `globalConfig` at create time.
|
|
408
445
|
- **Structured input** (`{ hue, saturation, tone, ... }`):
|
|
409
446
|
- Both tone windows snapshot from `globalConfig` at create time (same as a theme color).
|
|
410
447
|
- All fields are **snapshotted at color-creation time** — later `glaze.configure()` calls don't retroactively change existing tokens.
|
|
@@ -450,7 +487,6 @@ The optional `config` second argument (`GlazeConfigOverride`) overrides the reso
|
|
|
450
487
|
| `lightTone` | `[10, 100]` | Light tone window: `[lo, hi]`, `{ lo, hi, eps }`, or `false` (disable clamping). |
|
|
451
488
|
| `darkTone` | `[15, 95]` | Dark tone window: `[lo, hi]`, `{ lo, hi, eps }`, or `false` (disable clamping). |
|
|
452
489
|
| `darkDesaturation` | `0.1` | Saturation reduction in dark scheme (0–1). |
|
|
453
|
-
| `saturationTaper` | `0.15` | Saturation taper strength toward the tone extremes (0–1). |
|
|
454
490
|
| `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. |
|
|
455
491
|
| `shadowTuning` | `undefined` | Default shadow tuning (meaningful for themes; harmless on color tokens). |
|
|
456
492
|
|
|
@@ -989,10 +1025,6 @@ S_dark = S_light * (1 - darkDesaturation) // default: 0.1
|
|
|
989
1025
|
|
|
990
1026
|
`static` mode skips desaturation.
|
|
991
1027
|
|
|
992
|
-
### Saturation taper
|
|
993
|
-
|
|
994
|
-
`saturationTaper` (default `0.15`) gently reduces saturation toward the tone extremes, where in-gamut chroma collapses. It is the *strength* (0–1) — the maximum fraction of saturation removed at the very edges — ramped in smoothly over the outer ~15% of tone on each end. Mid-tones are untouched; `0` disables it.
|
|
995
|
-
|
|
996
1028
|
---
|
|
997
1029
|
|
|
998
1030
|
## Configuration
|
|
@@ -1002,7 +1034,6 @@ glaze.configure({
|
|
|
1002
1034
|
lightTone: [10, 100], // [lo, hi]; or { lo, hi, eps } / false to disable clamping
|
|
1003
1035
|
darkTone: [15, 95], // [lo, hi]; or { lo, hi, eps } / false to disable clamping
|
|
1004
1036
|
darkDesaturation: 0.1,
|
|
1005
|
-
saturationTaper: 0.15,
|
|
1006
1037
|
states: {
|
|
1007
1038
|
dark: '@dark',
|
|
1008
1039
|
highContrast: '@high-contrast',
|
|
@@ -1027,7 +1058,6 @@ A `ToneWindow` is `[lo, hi]` (OKHSL-lightness endpoints, reference eps — the c
|
|
|
1027
1058
|
| `lightTone` | `[10, 100]` | Light scheme tone window: `[lo, hi]`, `{ lo, hi, eps }`, or `false` to disable clamping. Bypassed in HC. |
|
|
1028
1059
|
| `darkTone` | `[15, 95]` | Dark scheme tone window: `[lo, hi]`, `{ lo, hi, eps }`, or `false` to disable clamping. Bypassed in HC. |
|
|
1029
1060
|
| `darkDesaturation` | `0.1` | Saturation reduction in dark scheme (0–1). |
|
|
1030
|
-
| `saturationTaper` | `0.15` | Saturation taper strength toward the tone extremes (0–1). `0` disables. |
|
|
1031
1061
|
| `states.dark` | `'@dark'` | State alias for dark mode tokens (Tasty export). |
|
|
1032
1062
|
| `states.highContrast` | `'@high-contrast'` | State alias for HC tokens. |
|
|
1033
1063
|
| `modes.dark` | `true` | Include dark variants in exports. |
|
|
@@ -1210,6 +1240,5 @@ import {
|
|
|
1210
1240
|
| `maxIterations` | `18` | Max binary-search iterations per branch. |
|
|
1211
1241
|
| `initialDirection` | higher-contrast side | Direction to search first (`'lighter'` or `'darker'`). |
|
|
1212
1242
|
| `flip` | `false` | When `true`, try the opposite direction if the initial one doesn't meet the target. When `false`, only the initial direction is searched — unmet contrasts pin the result to that direction's extreme. |
|
|
1213
|
-
| `saturationTaper` | `0` | When `> 0`, candidate saturation rolls off toward the tone extremes (same envelope the renderer applies), so the solved tone meets the floor at its rendered saturation. |
|
|
1214
1243
|
|
|
1215
1244
|
Result: `{ tone, contrast, met, branch: 'lighter' | 'darker' | 'preferred', flipped? }`. `flipped: true` indicates the initial direction failed and the opposite direction satisfied the target.
|
package/docs/migration.md
CHANGED
|
@@ -34,7 +34,7 @@ glaze.configure({ lightLightness: [10, 100], darkLightness: [15, 95], darkCurve:
|
|
|
34
34
|
glaze.configure({
|
|
35
35
|
lightTone: [10, 100],
|
|
36
36
|
darkTone: [15, 95],
|
|
37
|
-
// darkCurve removed
|
|
37
|
+
// darkCurve removed
|
|
38
38
|
});
|
|
39
39
|
```
|
|
40
40
|
|
package/docs/okhst.md
CHANGED
|
@@ -155,19 +155,11 @@ A window is authored as `[lo, hi]` (reference eps — the common form),
|
|
|
155
155
|
to disable clamping. `false` is the full range `[0, 100]` at the reference eps —
|
|
156
156
|
it removes the **boundaries**, not the tone curve.
|
|
157
157
|
|
|
158
|
-
Other defaults: `darkDesaturation = 0.1` (unchanged),
|
|
158
|
+
Other defaults: `darkDesaturation = 0.1` (unchanged),
|
|
159
159
|
`autoFlip = true`.
|
|
160
160
|
|
|
161
161
|
Reference: `REF_EPS = 0.05`.
|
|
162
162
|
|
|
163
|
-
## Saturation taper
|
|
164
|
-
|
|
165
|
-
At the tone extremes the in-gamut chroma collapses, so high saturation near
|
|
166
|
-
white/black reads as noise. `saturationEnvelope(s, toneFinal, taper)` applies a
|
|
167
|
-
smoothstep rolloff over the outer `taper` fraction of the tone range (default
|
|
168
|
-
`0.15` → outer 15% on each end). `taper = 0` disables it. It is conservative by
|
|
169
|
-
design: mid-tones are untouched, so existing ramps barely shift.
|
|
170
|
-
|
|
171
163
|
## Contrast metric (unified)
|
|
172
164
|
|
|
173
165
|
`contrast` is a single prop with a pluggable metric:
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tenphi/glaze",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.15.1",
|
|
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",
|
|
7
7
|
"module": "./dist/index.mjs",
|
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
"keywords": [
|
|
34
34
|
"color",
|
|
35
35
|
"theme",
|
|
36
|
+
"okhst",
|
|
36
37
|
"okhsl",
|
|
37
38
|
"contrast",
|
|
38
39
|
"wcag",
|
|
@@ -57,10 +58,13 @@
|
|
|
57
58
|
"tsdown": "^0.20.3",
|
|
58
59
|
"typescript": "^5.9.3",
|
|
59
60
|
"typescript-eslint": "^8.56.0",
|
|
61
|
+
"vite": "^8.0.16",
|
|
60
62
|
"vitest": "^4.0.18"
|
|
61
63
|
},
|
|
62
64
|
"scripts": {
|
|
63
65
|
"build": "tsdown",
|
|
66
|
+
"playground": "vite playground",
|
|
67
|
+
"build:playground": "vite build playground",
|
|
64
68
|
"test": "vitest run",
|
|
65
69
|
"test:watch": "vitest",
|
|
66
70
|
"test:coverage": "vitest run --coverage",
|