@tenphi/glaze 0.19.0 → 1.1.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 +85 -31
- package/dist/index.cjs +391 -233
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +99 -35
- package/dist/index.d.mts +99 -35
- package/dist/index.mjs +388 -234
- package/dist/index.mjs.map +1 -1
- package/docs/api.md +589 -413
- package/docs/methodology.md +125 -50
- package/docs/migration.md +81 -144
- package/docs/okhst.md +116 -254
- package/package.json +1 -1
package/docs/api.md
CHANGED
|
@@ -6,12 +6,17 @@ Full reference for every public method, option, and type exported by `@tenphi/gl
|
|
|
6
6
|
|
|
7
7
|
- [Theme creation](#theme-creation)
|
|
8
8
|
- [Theme methods](#theme-methods)
|
|
9
|
+
- [DTCG](#themedtcgoptions)
|
|
10
|
+
- [DTCG Resolver-Module](#themedtcgresolveroptions)
|
|
11
|
+
- [Tailwind CSS](#themetailwindoptions)
|
|
12
|
+
- [High-contrast pairs](#high-contrast-pairs)
|
|
9
13
|
- [Color definitions](#color-definitions)
|
|
10
14
|
- [Standalone color tokens](#standalone-color-tokens)
|
|
11
15
|
- [Shadows](#shadows)
|
|
12
16
|
- [Mix colors](#mix-colors)
|
|
13
17
|
- [Palette](#palette)
|
|
14
18
|
- [Output formats](#output-formats)
|
|
19
|
+
- [Hue channel splitting](#hue-channel-splitting-splithue)
|
|
15
20
|
- [Adaptation modes](#adaptation-modes)
|
|
16
21
|
- [Light / dark scheme mapping](#light--dark-scheme-mapping)
|
|
17
22
|
- [Configuration](#configuration)
|
|
@@ -23,25 +28,41 @@ Full reference for every public method, option, and type exported by `@tenphi/gl
|
|
|
23
28
|
|
|
24
29
|
## Theme creation
|
|
25
30
|
|
|
26
|
-
| Method
|
|
27
|
-
|
|
28
|
-
| `glaze(hue, saturation?, config?)`
|
|
29
|
-
| `glaze({ hue, saturation }, config?)` | Create a theme from an options object, with optional per-theme config override.
|
|
30
|
-
| `glaze.
|
|
31
|
-
| `glaze.
|
|
32
|
-
| `glaze.
|
|
31
|
+
| Method | Description |
|
|
32
|
+
| ------------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
|
|
33
|
+
| `glaze(hue, saturation?, config?)` | Create a theme from hue (0–360) and saturation (0–100). Optional `config` overrides the global config for this theme. |
|
|
34
|
+
| `glaze({ hue, saturation }, config?)` | Create a theme from an options object, with optional per-theme config override. |
|
|
35
|
+
| `glaze.themeFrom(data)` | Create a theme from a `theme.export()` snapshot (`kind: 'theme'`). |
|
|
36
|
+
| `glaze.from(data)` | Compat alias for `glaze.themeFrom`. |
|
|
37
|
+
| `glaze.fromHex(hex)` | Create a theme from a hex color (`#rgb` or `#rrggbb`). Extracts hue and saturation. |
|
|
38
|
+
| `glaze.fromRgb(r, g, b)` | Create a theme from RGB values (0–255). Extracts hue and saturation. |
|
|
39
|
+
| `glaze.paletteFrom(data)` | Create a palette from a `palette.export()` snapshot (`kind: 'palette'`). |
|
|
40
|
+
| `glaze.colorFrom(data)` | Create a color token from a `token.export()` snapshot (`kind: 'color'`). |
|
|
41
|
+
| `glaze.isThemeExport(data)` | Type guard for theme authoring snapshots. |
|
|
42
|
+
| `glaze.isColorTokenExport(data)` | Type guard for color-token authoring snapshots. |
|
|
43
|
+
| `glaze.isPaletteExport(data)` | Type guard for palette authoring snapshots. |
|
|
33
44
|
|
|
34
45
|
```ts
|
|
35
46
|
const a = glaze(280, 80);
|
|
36
47
|
const b = glaze({ hue: 280, saturation: 80 });
|
|
37
48
|
const c = glaze.fromHex('#7a4dbf');
|
|
38
49
|
const d = glaze.fromRgb(122, 77, 191);
|
|
39
|
-
const e = glaze.
|
|
50
|
+
const e = glaze.themeFrom(a.export());
|
|
40
51
|
|
|
41
52
|
// Per-theme config override:
|
|
42
53
|
const rawTheme = glaze(280, 80, { lightTone: false, darkTone: false });
|
|
43
54
|
```
|
|
44
55
|
|
|
56
|
+
Authoring restore triad (parallel to `.export()` on each instance):
|
|
57
|
+
|
|
58
|
+
| Export | Restore |
|
|
59
|
+
| ------ | ------- |
|
|
60
|
+
| `theme.export()` | `glaze.themeFrom()` |
|
|
61
|
+
| `token.export()` | `glaze.colorFrom()` |
|
|
62
|
+
| `palette.export()` | `glaze.paletteFrom()` |
|
|
63
|
+
|
|
64
|
+
Every snapshot includes `kind` + `version` (`GLAZE_EXPORT_VERSION`, currently `1`). Legacy snapshots without those fields still restore. Wrong `kind`, or a `version` outside `1..=GLAZE_EXPORT_VERSION`, throws.
|
|
65
|
+
|
|
45
66
|
The optional `config` parameter is a `GlazeConfigOverride` — see [Per-instance config override](#per-instance-config-override).
|
|
46
67
|
|
|
47
68
|
---
|
|
@@ -50,24 +71,27 @@ The optional `config` parameter is a `GlazeConfigOverride` — see [Per-instance
|
|
|
50
71
|
|
|
51
72
|
A `GlazeTheme` exposes:
|
|
52
73
|
|
|
53
|
-
| Method
|
|
54
|
-
|
|
55
|
-
| `theme.hue` (readonly)
|
|
56
|
-
| `theme.saturation` (readonly)
|
|
57
|
-
| `theme.colors(defs)`
|
|
58
|
-
| `theme.color(name)`
|
|
59
|
-
| `theme.color(name, def)`
|
|
60
|
-
| `theme.remove(name \| names[])` | Remove one or more color definitions.
|
|
61
|
-
| `theme.has(name)`
|
|
62
|
-
| `theme.list()`
|
|
63
|
-
| `theme.reset()`
|
|
64
|
-
| `theme.export()`
|
|
65
|
-
| `theme.extend(options)`
|
|
66
|
-
| `theme.resolve()`
|
|
67
|
-
| `theme.tokens(options?)`
|
|
68
|
-
| `theme.tasty(options?)`
|
|
69
|
-
| `theme.json(options?)`
|
|
70
|
-
| `theme.css(options?)`
|
|
74
|
+
| Method | Description |
|
|
75
|
+
| ------------------------------- | --------------------------------------------------------------------------------------------------- |
|
|
76
|
+
| `theme.hue` (readonly) | The hue seed (0–360). |
|
|
77
|
+
| `theme.saturation` (readonly) | The saturation seed (0–100). |
|
|
78
|
+
| `theme.colors(defs)` | Add/replace colors (additive merge — adds new, overwrites existing by name, doesn't remove others). |
|
|
79
|
+
| `theme.color(name)` | Get a color definition by name. |
|
|
80
|
+
| `theme.color(name, def)` | Set a single color definition. |
|
|
81
|
+
| `theme.remove(name \| names[])` | Remove one or more color definitions. |
|
|
82
|
+
| `theme.has(name)` | Check if a color is defined. |
|
|
83
|
+
| `theme.list()` | List all defined color names. |
|
|
84
|
+
| `theme.reset()` | Clear all color definitions. |
|
|
85
|
+
| `theme.export(override?)` | Export the theme configuration as a JSON-safe object (optional config override at export time). |
|
|
86
|
+
| `theme.extend(options)` | Create a child theme inheriting all color definitions (see [`extend`](#themeextendoptions) below). |
|
|
87
|
+
| `theme.resolve()` | Resolve all colors and return a `Map<string, ResolvedColor>`. |
|
|
88
|
+
| `theme.tokens(options?)` | Export as a flat token map grouped by scheme variant. |
|
|
89
|
+
| `theme.tasty(options?)` | Export as [Tasty](https://tasty.style) style-to-state bindings. |
|
|
90
|
+
| `theme.json(options?)` | Export as plain JSON. |
|
|
91
|
+
| `theme.css(options?)` | Export as CSS custom property declarations. |
|
|
92
|
+
| `theme.dtcg(options?)` | Export one W3C DTCG token tree per scheme. |
|
|
93
|
+
| `theme.dtcgResolver(options?)` | Export one DTCG Resolver-Module document containing every scheme. |
|
|
94
|
+
| `theme.tailwind(options?)` | Export a Tailwind CSS v4 theme and scheme overrides. |
|
|
71
95
|
|
|
72
96
|
### `theme.colors(defs)`
|
|
73
97
|
|
|
@@ -80,8 +104,8 @@ theme.colors({ text: { tone: 30 } });
|
|
|
80
104
|
### `theme.color(name) / theme.color(name, def)`
|
|
81
105
|
|
|
82
106
|
```ts
|
|
83
|
-
theme.color('surface', { tone: 97, saturation: 0.75 });
|
|
84
|
-
const def = theme.color('surface');
|
|
107
|
+
theme.color('surface', { tone: 97, saturation: 0.75 }); // set
|
|
108
|
+
const def = theme.color('surface'); // get
|
|
85
109
|
```
|
|
86
110
|
|
|
87
111
|
### `theme.extend(options)`
|
|
@@ -100,54 +124,66 @@ const highSat = base.extend({ config: { darkTone: [10, 100] } });
|
|
|
100
124
|
|
|
101
125
|
`GlazeExtendOptions`:
|
|
102
126
|
|
|
103
|
-
| Field
|
|
104
|
-
|
|
105
|
-
| `hue`
|
|
106
|
-
| `saturation` | `number`
|
|
107
|
-
| `colors`
|
|
108
|
-
| `config`
|
|
127
|
+
| Field | Type | Description |
|
|
128
|
+
| ------------ | --------------------- | -------------------------------------------------------------------------------------------- |
|
|
129
|
+
| `hue` | `number` | Replace the hue seed. Defaults to the parent's hue. |
|
|
130
|
+
| `saturation` | `number` | Replace the saturation seed. Defaults to the parent's saturation. |
|
|
131
|
+
| `colors` | `ColorMap` | Per-theme overrides (additive merge over the inherited map). |
|
|
132
|
+
| `config` | `GlazeConfigOverride` | Config override for the child. Shallow-merged with the parent's override — child fields win. |
|
|
109
133
|
|
|
110
134
|
Colors marked with `inherit: false` on the parent are **not** copied into the child.
|
|
111
135
|
|
|
136
|
+
### `theme.resolve()`
|
|
137
|
+
|
|
138
|
+
Resolves the dependency graph and returns a
|
|
139
|
+
`Map<string, ResolvedColor>`. Export methods call it automatically; use it
|
|
140
|
+
directly for tests, diagnostics, or a custom output pipeline.
|
|
141
|
+
|
|
142
|
+
```ts
|
|
143
|
+
const resolved = theme.resolve();
|
|
144
|
+
const surface = resolved.get('surface');
|
|
145
|
+
// surface?.light.t is canonical tone on 0–1.
|
|
146
|
+
```
|
|
147
|
+
|
|
112
148
|
### `theme.tokens(options?)`
|
|
113
149
|
|
|
114
150
|
Flat token map grouped by scheme variant.
|
|
115
151
|
|
|
116
152
|
```ts
|
|
117
|
-
theme.tokens()
|
|
153
|
+
theme.tokens();
|
|
118
154
|
// → { light: { surface: 'oklch(...)' }, dark: { surface: 'oklch(...)' } }
|
|
119
155
|
```
|
|
120
156
|
|
|
121
157
|
`GlazeJsonOptions`:
|
|
122
158
|
|
|
123
|
-
| Option
|
|
124
|
-
|
|
125
|
-
| `format` | `'oklch'`
|
|
126
|
-
| `modes`
|
|
159
|
+
| Option | Default | Description |
|
|
160
|
+
| -------- | -------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
|
|
161
|
+
| `format` | `'oklch'` | Output color format. One of `'rgb' \| 'hsl' \| 'oklch'`. `'okhsl'` and `'okhst'` throw — use `tasty()` for those. |
|
|
162
|
+
| `modes` | `{ dark: true, highContrast: false }` (or global config) | Which scheme variants to include. |
|
|
127
163
|
|
|
128
164
|
### `theme.tasty(options?)`
|
|
129
165
|
|
|
130
|
-
|
|
166
|
+
Style-to-state bindings for the [Tasty](https://tasty.style) style system. Uses `#name` color token keys and state aliases. By default the dark and high-contrast variants are keyed by media-query states (`'@media(prefers-color-scheme: dark)'`, `'@media(prefers-contrast: more)'`) so tokens work without registering custom states.
|
|
131
167
|
|
|
132
168
|
```ts
|
|
133
|
-
theme.tasty()
|
|
169
|
+
theme.tasty();
|
|
134
170
|
// → {
|
|
135
|
-
// '#surface': { '': '
|
|
171
|
+
// '#surface': { '': 'oklch(...)', '@media(prefers-color-scheme: dark)': 'oklch(...)' },
|
|
136
172
|
// ...
|
|
137
173
|
// }
|
|
138
174
|
```
|
|
139
175
|
|
|
140
176
|
`GlazeTokenOptions`:
|
|
141
177
|
|
|
142
|
-
| Option
|
|
143
|
-
|
|
144
|
-
| `format`
|
|
145
|
-
| `modes`
|
|
146
|
-
| `states.dark`
|
|
147
|
-
| `states.highContrast` | `'@media(prefers-contrast: more)'` (or global config)
|
|
148
|
-
| `splitHue`
|
|
149
|
-
| `name`
|
|
150
|
-
| `prefix`
|
|
178
|
+
| Option | Default | Description |
|
|
179
|
+
| --------------------- | --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
180
|
+
| `format` | `'oklch'` | Output color format. `'okhsl'` and `'okhst'` are also supported here ([Tasty](https://tasty.style)-only spaces). |
|
|
181
|
+
| `modes` | global config | Which scheme variants to include. |
|
|
182
|
+
| `states.dark` | `'@media(prefers-color-scheme: dark)'` (or global config) | State alias for dark mode tokens. |
|
|
183
|
+
| `states.highContrast` | `'@media(prefers-contrast: more)'` (or global config) | State alias for high-contrast tokens. |
|
|
184
|
+
| `splitHue` | `false` | Emit hue as a separate custom property (`$name-hue` token + `var()` in `oklch` values). Requires `format: 'oklch'` and every color to be pastel. |
|
|
185
|
+
| `name` | `'theme'` | Base name for the theme-level hue var (`$theme-hue` / `--theme-hue`). Palette export auto-derives this from the theme name. |
|
|
186
|
+
| `prefix` | (palette only) | See [Palette](#palette). |
|
|
151
187
|
|
|
152
188
|
When both `dark` and `highContrast` modes are enabled, dark high-contrast variants are emitted under the combined key `<dark> & <highContrast>` (e.g. `'@media(prefers-color-scheme: dark) & @media(prefers-contrast: more)'`).
|
|
153
189
|
|
|
@@ -156,7 +192,7 @@ When both `dark` and `highContrast` modes are enabled, dark high-contrast varian
|
|
|
156
192
|
Per-color JSON map.
|
|
157
193
|
|
|
158
194
|
```ts
|
|
159
|
-
theme.json()
|
|
195
|
+
theme.json();
|
|
160
196
|
// → {
|
|
161
197
|
// surface: { light: 'oklch(...)', dark: 'oklch(...)' },
|
|
162
198
|
// text: { light: 'oklch(...)', dark: 'oklch(...)' },
|
|
@@ -172,8 +208,8 @@ CSS custom property declaration strings, grouped by scheme variant.
|
|
|
172
208
|
```ts
|
|
173
209
|
theme.css();
|
|
174
210
|
// → {
|
|
175
|
-
// light: '--surface-color:
|
|
176
|
-
// dark: '--surface-color:
|
|
211
|
+
// light: '--surface-color: oklch(...);\n--text-color: oklch(...);',
|
|
212
|
+
// dark: '--surface-color: oklch(...);\n--text-color: oklch(...);',
|
|
177
213
|
// lightContrast: '...',
|
|
178
214
|
// darkContrast: '...',
|
|
179
215
|
// }
|
|
@@ -181,12 +217,12 @@ theme.css();
|
|
|
181
217
|
|
|
182
218
|
`GlazeCssOptions`:
|
|
183
219
|
|
|
184
|
-
| Option
|
|
185
|
-
|
|
186
|
-
| `format`
|
|
187
|
-
| `suffix`
|
|
188
|
-
| `splitHue` | `false`
|
|
189
|
-
| `name`
|
|
220
|
+
| Option | Default | Description |
|
|
221
|
+
| ---------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
222
|
+
| `format` | `'oklch'` | Output color format. One of `'rgb' \| 'hsl' \| 'oklch'`. `'okhsl'` and `'okhst'` throw — use `tasty()` for those. |
|
|
223
|
+
| `suffix` | `'-color'` | Suffix appended to each CSS property name. Pass `''` for bare property names. |
|
|
224
|
+
| `splitHue` | `false` | Emit hue as a separate `--*-hue` custom property referenced via `var()` in `oklch` color values. Requires `format: 'oklch'` and every color to be pastel. Shadow/mix colors stay inline (blended hue; they do not follow `--hue` rotation). |
|
|
225
|
+
| `name` | `'theme'` | Base name for the theme-level hue var (`--theme-hue`). Palette export auto-derives this from the theme name. |
|
|
190
226
|
|
|
191
227
|
`GlazeCssResult` always contains all four keys (`light`, `dark`, `lightContrast`, `darkContrast`); empty if no colors are defined for that variant.
|
|
192
228
|
|
|
@@ -195,7 +231,7 @@ theme.css();
|
|
|
195
231
|
W3C [Design Tokens Format Module (2025.10)](https://www.designtokens.org/) documents — the vendor-neutral JSON format consumed by Figma, Tokens Studio, Style Dictionary v4+, Terrazzo, Penpot, and every DTCG-compatible tool. Returns one spec-conformant token tree per scheme variant.
|
|
196
232
|
|
|
197
233
|
```ts
|
|
198
|
-
theme.dtcg()
|
|
234
|
+
theme.dtcg();
|
|
199
235
|
// → {
|
|
200
236
|
// light: {
|
|
201
237
|
// surface: {
|
|
@@ -216,10 +252,10 @@ Write each document to its own `.tokens.json` file — one file per scheme is th
|
|
|
216
252
|
|
|
217
253
|
`GlazeDtcgOptions`:
|
|
218
254
|
|
|
219
|
-
| Option
|
|
220
|
-
|
|
221
|
-
| `colorSpace` | `'srgb'`
|
|
222
|
-
| `modes`
|
|
255
|
+
| Option | Default | Description |
|
|
256
|
+
| ------------ | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
257
|
+
| `colorSpace` | `'srgb'` | Color space for `$value`. `'srgb'` emits gamma sRGB `components` (0–1) plus a `hex` hint — universally understood. `'oklch'` emits `[L, C, H]` components with no hex — Glaze-native, wide-gamut. |
|
|
258
|
+
| `modes` | global config | Which scheme variants to include. `light` is always present. |
|
|
223
259
|
|
|
224
260
|
`alpha` is included on `$value` only when the color's opacity is below 1. `$type` is always `'color'`.
|
|
225
261
|
|
|
@@ -228,7 +264,7 @@ Write each document to its own `.tokens.json` file — one file per scheme is th
|
|
|
228
264
|
A single W3C [DTCG Resolver-Module](https://www.designtokens.org/) document describing **every scheme variant in one file** — an alternative to `dtcg()`'s per-scheme files for tools that resolve sets + modifiers (e.g. Dispersa). The light document becomes `sets.base.sources[0]` (the default context); each other variant becomes a context override on a single `scheme` modifier.
|
|
229
265
|
|
|
230
266
|
```ts
|
|
231
|
-
theme.dtcgResolver({ modes: { highContrast: true } })
|
|
267
|
+
theme.dtcgResolver({ modes: { highContrast: true } });
|
|
232
268
|
// → {
|
|
233
269
|
// version: '2025.10',
|
|
234
270
|
// sets: {
|
|
@@ -272,14 +308,14 @@ theme.dtcgResolver({ modes: { highContrast: true } })
|
|
|
272
308
|
|
|
273
309
|
`GlazeDtcgResolverOptions` (extends `GlazeDtcgOptions`, so `modes` and `colorSpace` pass through):
|
|
274
310
|
|
|
275
|
-
| Option
|
|
276
|
-
|
|
277
|
-
| `colorSpace`
|
|
278
|
-
| `modes`
|
|
279
|
-
| `setName`
|
|
280
|
-
| `modifierName` | `'scheme'`
|
|
281
|
-
| `contextNames` | identity
|
|
282
|
-
| `version`
|
|
311
|
+
| Option | Default | Description |
|
|
312
|
+
| -------------- | ------------- | ----------------------------------------------------------------------------------------------------------------- |
|
|
313
|
+
| `colorSpace` | `'srgb'` | Same as `dtcg()` — flows through to every source and context. |
|
|
314
|
+
| `modes` | global config | Which scheme variants to emit as contexts. `light` is always present (the default); absent variants are omitted. |
|
|
315
|
+
| `setName` | `'base'` | Name of the single set holding the default (light) token tree. |
|
|
316
|
+
| `modifierName` | `'scheme'` | Name of the modifier describing the scheme axis. |
|
|
317
|
+
| `contextNames` | identity | Override the four context names (`light` / `dark` / `lightContrast` / `darkContrast`) — e.g. `{ dark: 'night' }`. |
|
|
318
|
+
| `version` | `'2025.10'` | Resolver document version. |
|
|
283
319
|
|
|
284
320
|
### `theme.tailwind(options?)`
|
|
285
321
|
|
|
@@ -306,24 +342,63 @@ A Tailwind CSS v4 `@theme` block (light baseline) plus dark / high-contrast over
|
|
|
306
342
|
|
|
307
343
|
`GlazeTailwindOptions`:
|
|
308
344
|
|
|
309
|
-
| Option
|
|
310
|
-
|
|
311
|
-
| `format`
|
|
312
|
-
| `namespace`
|
|
313
|
-
| `darkSelector`
|
|
314
|
-
| `highContrastSelector` | `'.high-contrast'` | Selector wrapping the light high-contrast overrides. The combined dark + high-contrast block uses `${darkSelector}${highContrastSelector}` (e.g. `.dark.high-contrast`).
|
|
315
|
-
| `modes`
|
|
345
|
+
| Option | Default | Description |
|
|
346
|
+
| ---------------------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
347
|
+
| `format` | `'oklch'` | Output color format for the values. |
|
|
348
|
+
| `namespace` | `'color-'` | CSS custom property namespace, forming `--<namespace><name>` (e.g. `--color-surface`). Named `namespace` to avoid clashing with the palette theme-prefix option. |
|
|
349
|
+
| `darkSelector` | `'.dark'` | Selector wrapping the dark overrides. Pass an at-rule like `'@media (prefers-color-scheme: dark)'` to drive dark mode from the OS preference (it nests `:root` automatically). |
|
|
350
|
+
| `highContrastSelector` | `'.high-contrast'` | Selector wrapping the light high-contrast overrides. The combined dark + high-contrast block uses `${darkSelector}${highContrastSelector}` (e.g. `.dark.high-contrast`). |
|
|
351
|
+
| `modes` | global config | Which scheme variants to include. The `@theme` block (light) is always emitted when colors exist. |
|
|
316
352
|
|
|
317
|
-
### `theme.export()`
|
|
353
|
+
### `theme.export(override?)`
|
|
318
354
|
|
|
319
355
|
```ts
|
|
320
356
|
const snapshot = theme.export();
|
|
321
|
-
// → {
|
|
357
|
+
// → {
|
|
358
|
+
// kind: 'theme',
|
|
359
|
+
// version: 1,
|
|
360
|
+
// hue: 280,
|
|
361
|
+
// saturation: 80,
|
|
362
|
+
// colors: { surface: { ... }, ... },
|
|
363
|
+
// config: { lightTone: {...}, darkTone: {...}, pastel: false, ... },
|
|
364
|
+
// }
|
|
322
365
|
|
|
323
|
-
const restored = glaze.
|
|
366
|
+
const restored = glaze.themeFrom(snapshot);
|
|
324
367
|
```
|
|
325
368
|
|
|
326
|
-
|
|
369
|
+
Returns a deep-cloned, JSON-safe authoring snapshot (definitions + frozen
|
|
370
|
+
effective config — not resolved color strings). Freezes
|
|
371
|
+
`getConfig() ∪ instance local ∪ override` at call time. Restored themes pin
|
|
372
|
+
that freeze as their local override. Distinct from `theme.json()`, which
|
|
373
|
+
emits resolved color strings.
|
|
374
|
+
|
|
375
|
+
---
|
|
376
|
+
|
|
377
|
+
## High-contrast pairs
|
|
378
|
+
|
|
379
|
+
`HCPair<T>` means either one value used in both ordinary and high-contrast
|
|
380
|
+
schemes, or an explicit `[normal, highContrast]` pair:
|
|
381
|
+
|
|
382
|
+
```ts
|
|
383
|
+
type HCPair<T> = T | [T, T];
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
It is used by `tone`, `contrast`, shadow `intensity`, and mix `value`:
|
|
387
|
+
|
|
388
|
+
```ts
|
|
389
|
+
tone: '-8'; // -8 in normal and HC
|
|
390
|
+
tone: ['-8', '-16']; // -8 normal, -16 HC
|
|
391
|
+
contrast: {
|
|
392
|
+
apca: 'content';
|
|
393
|
+
} // preset with automatic HC enhancement
|
|
394
|
+
contrast: {
|
|
395
|
+
apca: ['content', 'body'];
|
|
396
|
+
} // explicit normal/HC targets
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
For `contrast`, the pair may wrap the whole spec or live inside the selected
|
|
400
|
+
metric. An explicit HC value disables automatic APCA enhancement or WCAG preset
|
|
401
|
+
promotion for that color.
|
|
327
402
|
|
|
328
403
|
---
|
|
329
404
|
|
|
@@ -337,34 +412,42 @@ type ColorDef = RegularColorDef | ShadowColorDef | MixColorDef;
|
|
|
337
412
|
|
|
338
413
|
### `RegularColorDef`
|
|
339
414
|
|
|
340
|
-
| Field
|
|
341
|
-
|
|
342
|
-
| `tone`
|
|
343
|
-
| `saturation` | `number`
|
|
344
|
-
| `hue`
|
|
345
|
-
| `base`
|
|
346
|
-
| `contrast`
|
|
347
|
-
| `mode`
|
|
348
|
-
| `autoFlip`
|
|
349
|
-
| `opacity`
|
|
350
|
-
| `pastel`
|
|
351
|
-
| `role`
|
|
352
|
-
| `inherit`
|
|
415
|
+
| Field | Type | Description |
|
|
416
|
+
| ------------ | ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
417
|
+
| `tone` | `HCPair<ToneValue>` | Number = absolute (0–100). `'+N'`/`'-N'` = a signed **tone delta** from the base (requires `base`). `'max'`/`'min'` = forced to the scheme's tone extreme (no `base`). Optional HC pair `[normal, hc]`. |
|
|
418
|
+
| `saturation` | `number` | Saturation factor applied to the seed saturation (0–1). Default: `1`. |
|
|
419
|
+
| `hue` | `number \| RelativeValue` | Number = absolute (0–360). String (`'+N'`/`'-N'`) = relative to the **theme seed hue** (never to a base color). |
|
|
420
|
+
| `base` | `string` | Name of another color in the same theme — makes this a _dependent_ color. |
|
|
421
|
+
| `contrast` | `HCPair<ContrastSpec>` | Contrast floor against `base`. Requires `base`. See [`contrast`](#contrast-floor). |
|
|
422
|
+
| `mode` | `'auto' \| 'fixed' \| 'static'` | Adaptation mode. Default: `'auto'`. See [Adaptation modes](#adaptation-modes). |
|
|
423
|
+
| `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). |
|
|
424
|
+
| `opacity` | `number` | Fixed alpha 0–1. Output includes alpha in the CSS value. Combining with `contrast` is not recommended (a `console.warn` is emitted). |
|
|
425
|
+
| `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 per-theme / per-token `pastel` override when omitted. Default: unset. See [Per-color `pastel`](#per-color-pastel). |
|
|
426
|
+
| `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). |
|
|
427
|
+
| `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. |
|
|
353
428
|
|
|
354
429
|
#### Tone values
|
|
355
430
|
|
|
356
|
-
`tone` (0–100) replaces OKHSL lightness with a contrast-
|
|
431
|
+
`tone` (0–100) replaces authored OKHSL lightness with a contrast-shaped axis.
|
|
432
|
+
Equal tone differences give equal WCAG contrast for neutrals; chromatic results
|
|
433
|
+
can drift in measured luminance. See [OKHST in Glaze](okhst.md). To port old
|
|
434
|
+
`lightness` values, see [migration.md](migration.md).
|
|
357
435
|
|
|
358
|
-
| Form
|
|
359
|
-
|
|
360
|
-
| Number (absolute)
|
|
361
|
-
| String (
|
|
362
|
-
| Extreme
|
|
363
|
-
| HC pair
|
|
436
|
+
| Form | Example | Meaning |
|
|
437
|
+
| ------------------- | ----------------------- | --------------------------------------------------------------------------------------------- |
|
|
438
|
+
| Number (absolute) | `tone: 45` | Absolute tone 0–100. |
|
|
439
|
+
| String (tone delta) | `tone: '-52'` | Signed difference from the base color's resolved tone (requires `base`). |
|
|
440
|
+
| Extreme | `tone: 'max'` / `'min'` | Force to the scheme's highest (`'max'` = 100) or lowest (`'min'` = 0) tone. No `base` needed. |
|
|
441
|
+
| HC pair | `tone: ['-7', '-20']` | `[normal, high-contrast]`. A single value applies to both. |
|
|
364
442
|
|
|
365
443
|
**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
444
|
|
|
367
|
-
**
|
|
445
|
+
**A tone delta** applies a signed difference to the base color's resolved tone.
|
|
446
|
+
It gives an exact contrast step for neutrals and a stable visual progression for
|
|
447
|
+
chromatic colors. In dark mode with `mode: 'auto'`, it is anchored to the
|
|
448
|
+
base's per-scheme tone. If `base + delta` falls outside `[0, 100]`, the result
|
|
449
|
+
is clamped to the boundary, or — with `autoFlip` (default on) — mirrored to the
|
|
450
|
+
other side of the base.
|
|
368
451
|
|
|
369
452
|
**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
453
|
|
|
@@ -384,30 +467,42 @@ A dependent color with `base` but no `tone` inherits the base's tone (equivalent
|
|
|
384
467
|
```ts
|
|
385
468
|
type ContrastPreset = 'AA' | 'AAA' | 'AA-large' | 'AAA-large';
|
|
386
469
|
type ContrastSpec =
|
|
387
|
-
| number
|
|
388
|
-
| ContrastPreset
|
|
470
|
+
| number // bare WCAG ratio
|
|
471
|
+
| ContrastPreset // named WCAG preset
|
|
389
472
|
| { wcag: HCPair<number | ContrastPreset> }
|
|
390
|
-
| { apca: HCPair<number> };
|
|
473
|
+
| { apca: HCPair<number> }; // APCA Lc target
|
|
391
474
|
```
|
|
392
475
|
|
|
393
|
-
| Preset
|
|
394
|
-
|
|
395
|
-
| `'AA-large'`
|
|
396
|
-
| `'AA'`
|
|
397
|
-
| `'AAA-large'` | 4.5
|
|
398
|
-
| `'AAA'`
|
|
476
|
+
| Preset | WCAG ratio |
|
|
477
|
+
| ------------- | ---------- |
|
|
478
|
+
| `'AA-large'` | 3 |
|
|
479
|
+
| `'AA'` | 4.5 |
|
|
480
|
+
| `'AAA-large'` | 4.5 |
|
|
481
|
+
| `'AAA'` | 7 |
|
|
399
482
|
|
|
400
483
|
A bare number or preset means **WCAG**. Use `{ wcag }` / `{ apca }` to pick the metric explicitly. The `[normal, highContrast]` pair may live at the outer level (`[4.5, 7]`, `[{ wcag: 4.5 }, { wcag: 7 }]`) or inside the metric (`{ wcag: [4.5, 7] }`, `{ apca: [45, 60] }`).
|
|
401
484
|
|
|
402
485
|
```ts
|
|
403
|
-
contrast: 4.5
|
|
404
|
-
contrast: 'AAA'
|
|
405
|
-
contrast: {
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
contrast: {
|
|
409
|
-
|
|
410
|
-
|
|
486
|
+
contrast: 4.5; // WCAG 4.5
|
|
487
|
+
contrast: 'AAA'; // WCAG 7
|
|
488
|
+
contrast: {
|
|
489
|
+
wcag: 6;
|
|
490
|
+
} // WCAG 6
|
|
491
|
+
contrast: {
|
|
492
|
+
wcag: [4.5, 7];
|
|
493
|
+
} // WCAG 4.5 normal / 7 high-contrast (explicit)
|
|
494
|
+
contrast: {
|
|
495
|
+
apca: 60;
|
|
496
|
+
} // APCA Lc 60 normal / 75 high-contrast (auto)
|
|
497
|
+
contrast: {
|
|
498
|
+
apca: [45, 60];
|
|
499
|
+
} // APCA Lc 45 normal / 60 high-contrast (explicit)
|
|
500
|
+
contrast: {
|
|
501
|
+
apca: 'content';
|
|
502
|
+
} // APCA preset -> Lc 60 normal / 75 high-contrast (auto)
|
|
503
|
+
contrast: {
|
|
504
|
+
apca: ['content', 'body'];
|
|
505
|
+
} // Lc 60 normal / 75 high-contrast (explicit)
|
|
411
506
|
```
|
|
412
507
|
|
|
413
508
|
**WCAG HC auto-promotion:** a bare WCAG preset (no `[normal, hc]` pair at either
|
|
@@ -424,30 +519,30 @@ boosted by **+15 Lc** in high-contrast mode, the APCA analog of WCAG's
|
|
|
424
519
|
AAA-over-AA step. On by default; an explicit HC value via either pair
|
|
425
520
|
overrides it and skips the boost. The enhanced target is clamped to 106 Lc.
|
|
426
521
|
For large/bold text (where APCA caps contrast at Lc 90 to avoid glare), pass
|
|
427
|
-
an explicit HC pair to hold that ceiling
|
|
428
|
-
[`docs/okhst.md`](okhst.md) §Enhanced Level.
|
|
522
|
+
an explicit HC pair to hold that ceiling.
|
|
429
523
|
|
|
430
524
|
APCA preset keywords (Bronze Simple Mode conformance levels, role-independent):
|
|
431
525
|
`'preferred'` (Lc 90), `'body'` (75), `'content'` (60, ~AA), `'large'` (45, ~3:1),
|
|
432
|
-
`'non-text'` (30), `'min'` (15, point of invisibility).
|
|
433
|
-
[`docs/okhst.md`](okhst.md) §APCA.
|
|
526
|
+
`'non-text'` (30), `'min'` (15, point of invisibility).
|
|
434
527
|
|
|
435
|
-
The floor is applied independently per scheme
|
|
528
|
+
The floor is applied independently per scheme. If the preferred `tone` already
|
|
529
|
+
satisfies it, the tone is kept; otherwise the solver uses the tone-shaped scale
|
|
530
|
+
for a closed-form WCAG seed and fast search until the target is met.
|
|
436
531
|
|
|
437
532
|
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.
|
|
438
533
|
|
|
439
534
|
**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.
|
|
440
535
|
|
|
441
|
-
**Chromatic drift (verification):** tone is contrast-uniform for grays. A chromatic swatch at a given tone shares its OKHSL lightness with the equivalent gray but drifts in real luminance, so a contrast-floored color may land slightly under its gray-tone expectation. Glaze measures the resolved result against the base and emits a deduped advisory `console.warn` when it drifts below the target. See [
|
|
536
|
+
**Chromatic drift (verification):** tone is contrast-uniform for grays. A chromatic swatch at a given tone shares its OKHSL lightness with the equivalent gray but drifts in real luminance, so a contrast-floored color may land slightly under its gray-tone expectation. Glaze measures the resolved result against the base and emits a deduped advisory `console.warn` when it drifts below the target. See [Contrast verification](okhst.md#contrast-verification).
|
|
442
537
|
|
|
443
538
|
#### Per-color hue override
|
|
444
539
|
|
|
445
540
|
```ts
|
|
446
541
|
const theme = glaze(280, 80);
|
|
447
542
|
theme.colors({
|
|
448
|
-
surface:
|
|
449
|
-
gradientEnd: { tone: 90, hue: '+20' },
|
|
450
|
-
warning:
|
|
543
|
+
surface: { tone: 97 },
|
|
544
|
+
gradientEnd: { tone: 90, hue: '+20' }, // 280 + 20 = 300
|
|
545
|
+
warning: { tone: 60, hue: 40 }, // absolute
|
|
451
546
|
});
|
|
452
547
|
```
|
|
453
548
|
|
|
@@ -455,19 +550,19 @@ Relative hue is always relative to the **theme seed hue**, not to a base color.
|
|
|
455
550
|
|
|
456
551
|
#### Per-color `pastel`
|
|
457
552
|
|
|
458
|
-
`pastel: true` on a single color def overrides the
|
|
553
|
+
`pastel: true` on a single color def overrides the per-theme / per-token `pastel` override 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.
|
|
459
554
|
|
|
460
555
|
```ts
|
|
461
556
|
const theme = glaze(280, 80);
|
|
462
557
|
theme.colors({
|
|
463
558
|
plain: { tone: 50, saturation: 1 },
|
|
464
|
-
soft:
|
|
559
|
+
soft: { tone: 50, saturation: 1, pastel: true },
|
|
465
560
|
});
|
|
466
561
|
// theme.resolve().get('soft')!.light.pastel === true
|
|
467
562
|
// theme.css().light contains different rgb() triples for `--plain` and `--soft`
|
|
468
563
|
```
|
|
469
564
|
|
|
470
|
-
Omit the field to inherit the
|
|
565
|
+
Omit the field to inherit the theme/token `pastel` override (default `false`) — useful for keeping the default behavior while opting a single accent into the pastel gamut.
|
|
471
566
|
|
|
472
567
|
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:
|
|
473
568
|
|
|
@@ -489,11 +584,11 @@ const child = parent.extend({
|
|
|
489
584
|
|
|
490
585
|
A color's `role` describes how it is used against its `base` and fixes **APCA contrast polarity** — which side is the foreground vs the background. APCA is asymmetric (`|apca(a,b)| ≠ |apca(b,a)|`), so the role picks the correct argument order; WCAG is symmetric and unaffected.
|
|
491
586
|
|
|
492
|
-
| Role
|
|
493
|
-
|
|
494
|
-
| `'text'`
|
|
495
|
-
| `'border'`
|
|
496
|
-
| `'surface'` | bg
|
|
587
|
+
| Role | Polarity | Use | Aliases (name inference) |
|
|
588
|
+
| ----------- | -------- | ---------------------------------------------------- | ----------------------------------------------------------------- |
|
|
589
|
+
| `'text'` | fg | Text / icons / foreground content | `text`, `fg`, `foreground`, `content`, `ink`, `label`, `stroke` |
|
|
590
|
+
| `'border'` | fg | Non-text spot elements (borders, dividers, outlines) | `border`, `divider`, `outline`, `separator`, `hairline`, `rule` |
|
|
591
|
+
| `'surface'` | bg | Backgrounds / fills | `surface`, `bg`, `background`, `fill`, `canvas`, `paper`, `layer` |
|
|
497
592
|
|
|
498
593
|
Resolution chain (per color):
|
|
499
594
|
|
|
@@ -506,8 +601,8 @@ Resolution chain (per color):
|
|
|
506
601
|
const theme = glaze(280, 60);
|
|
507
602
|
theme.colors({
|
|
508
603
|
surface: { tone: 90 },
|
|
509
|
-
text:
|
|
510
|
-
border:
|
|
604
|
+
text: { base: 'surface', contrast: { apca: 'content' } }, // inferred text
|
|
605
|
+
border: { base: 'surface', tone: '-10' }, // inferred border
|
|
511
606
|
});
|
|
512
607
|
// role fixes APCA polarity; set `pastel: true` explicitly if a border
|
|
513
608
|
// needs the hue-independent safe chroma limit.
|
|
@@ -517,32 +612,32 @@ Disable name inference with `glaze.configure({ inferRole: false })` (the base-op
|
|
|
517
612
|
|
|
518
613
|
### `ShadowColorDef`
|
|
519
614
|
|
|
520
|
-
| Field
|
|
521
|
-
|
|
522
|
-
| `type`
|
|
523
|
-
| `bg`
|
|
524
|
-
| `fg`
|
|
525
|
-
| `intensity` | `HCPair<number>` | Shadow intensity, 0–100. Supports HC pairs.
|
|
526
|
-
| `tuning`
|
|
527
|
-
| `pastel`
|
|
528
|
-
| `inherit`
|
|
615
|
+
| Field | Type | Description |
|
|
616
|
+
| ----------- | ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
617
|
+
| `type` | `'shadow'` | Discriminator. |
|
|
618
|
+
| `bg` | `string` | Background color name — must reference a non-shadow color in the same theme. |
|
|
619
|
+
| `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. |
|
|
620
|
+
| `intensity` | `HCPair<number>` | Shadow intensity, 0–100. Supports HC pairs. |
|
|
621
|
+
| `tuning` | `ShadowTuning` | Per-color tuning overrides. Merged field-by-field with the global `shadowTuning`. |
|
|
622
|
+
| `pastel` | `boolean` | Per-color `pastel` override. See [Per-color `pastel`](#per-color-pastel). |
|
|
623
|
+
| `inherit` | `boolean` | Inheritance flag, default `true`. |
|
|
529
624
|
|
|
530
625
|
See [Shadows](#shadows) below for the algorithm and tuning details.
|
|
531
626
|
|
|
532
627
|
### `MixColorDef`
|
|
533
628
|
|
|
534
|
-
| Field
|
|
535
|
-
|
|
536
|
-
| `type`
|
|
537
|
-
| `base`
|
|
538
|
-
| `target`
|
|
539
|
-
| `value`
|
|
540
|
-
| `blend`
|
|
541
|
-
| `space`
|
|
542
|
-
| `contrast` | `HCPair<ContrastSpec>`
|
|
543
|
-
| `pastel`
|
|
544
|
-
| `role`
|
|
545
|
-
| `inherit`
|
|
629
|
+
| Field | Type | Description |
|
|
630
|
+
| ---------- | --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
631
|
+
| `type` | `'mix'` | Discriminator. |
|
|
632
|
+
| `base` | `string` | "From" color name. |
|
|
633
|
+
| `target` | `string` | "To" color name. |
|
|
634
|
+
| `value` | `HCPair<number>` | Mix ratio 0–100 (0 = pure base, 100 = pure target). In `'transparent'` blend, this becomes the target's opacity. Supports HC pairs. |
|
|
635
|
+
| `blend` | `'opaque' \| 'transparent'` | Default `'opaque'`. |
|
|
636
|
+
| `space` | `'okhsl' \| 'srgb'` | Interpolation space for opaque blending. Default `'okhsl'`. Ignored for `'transparent'` (always composites in linear sRGB). |
|
|
637
|
+
| `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). |
|
|
638
|
+
| `pastel` | `boolean` | Per-color `pastel` override. See [Per-color `pastel`](#per-color-pastel). |
|
|
639
|
+
| `role` | `RoleInput` | Semantic role of the mixed result against `base`. Same semantics as `RegularColorDef.role` (see [Roles](#roles)). |
|
|
640
|
+
| `inherit` | `boolean` | Inheritance flag, default `true`. |
|
|
546
641
|
|
|
547
642
|
See [Mix colors](#mix-colors) below.
|
|
548
643
|
|
|
@@ -562,143 +657,149 @@ glaze.color(color: GlazeFromInput | GlazeColorInput | GlazeColorValue, config?:
|
|
|
562
657
|
|
|
563
658
|
`glaze.color()` accepts **four input shapes**, discriminated by structure:
|
|
564
659
|
|
|
565
|
-
| Shape
|
|
566
|
-
|
|
567
|
-
| **Bare string**
|
|
568
|
-
| **Value object**
|
|
569
|
-
| **`{ from, ...overrides }`** | `{ from: '#1a1a2e', base: bg, contrast: 'AA' }` | Value + color overrides in one object.
|
|
570
|
-
| **Structured**
|
|
660
|
+
| Shape | Example | Notes |
|
|
661
|
+
| ---------------------------- | ----------------------------------------------- | ----------------------------------------------------------------------------------- |
|
|
662
|
+
| **Bare string** | `'#26fcb2'` | Hex or CSS color function (`rgb()`, `hsl()`, `okhsl()`, `okhst()`, `oklch()`). |
|
|
663
|
+
| **Value object** | `{ h: 152, s: 0.95, l: 0.74 }` | OKHSL, OKHST (`{ h, s, t }`), `{ r, g, b }` (sRGB 0–255), or `{ l, c, h }` (OKLCh). |
|
|
664
|
+
| **`{ from, ...overrides }`** | `{ from: '#1a1a2e', base: bg, contrast: 'AA' }` | Value + color overrides in one object. |
|
|
665
|
+
| **Structured** | `{ hue: 152, saturation: 95, tone: 74 }` | Full theme-style token (hue/saturation in 0–100, tone in 0–100). |
|
|
571
666
|
|
|
572
667
|
`GlazeColorValue` (bare string or value-object forms) accepts:
|
|
573
668
|
|
|
574
|
-
| Form
|
|
575
|
-
|
|
576
|
-
| Hex
|
|
577
|
-
| `rgb()`
|
|
578
|
-
| `hsl()`
|
|
579
|
-
| `okhsl()`
|
|
580
|
-
| `okhst()`
|
|
581
|
-
| `oklch()`
|
|
582
|
-
| `OkhslColor` object | `{ h: 152, s: 0.95, l: 0.74 }`
|
|
583
|
-
| `OkhstColor` object | `{ h: 152, s: 0.95, t: 0.70 }`
|
|
584
|
-
| `RgbColor` object
|
|
585
|
-
| `OklchColor` object | `{ l: 0.85, c: 0.18, h: 152 }`
|
|
669
|
+
| Form | Example | Notes |
|
|
670
|
+
| ------------------- | ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
|
|
671
|
+
| Hex | `'#26fcb2'`, `'#26fcb2ff'`, `'#abc'` | 3, 6, or 8 digits. Alpha is dropped with a `console.warn` — use `opacity` instead. |
|
|
672
|
+
| `rgb()` | `'rgb(38 252 178)'`, `'rgb(38 252 178 / 0.8)'` | Modern space syntax. Alpha dropped with warning. |
|
|
673
|
+
| `hsl()` | `'hsl(152 97% 57%)'` | Modern space syntax. Alpha dropped with warning. |
|
|
674
|
+
| `okhsl()` | `'okhsl(152 95% 74%)'` | Glaze's own emit format. Alpha dropped with warning. |
|
|
675
|
+
| `okhst()` | `'okhst(152 95% 70%)'` | OKHST tone input (third value is tone 0–100). Not native CSS; it can also be serialized by [Tasty](https://tasty.style) exports. Alpha dropped with warning. |
|
|
676
|
+
| `oklch()` | `'oklch(0.85 0.18 152)'` | Glaze's own emit format. Alpha dropped with warning. |
|
|
677
|
+
| `OkhslColor` object | `{ h: 152, s: 0.95, l: 0.74 }` | OKHSL shape (h: 0–360, s/l: 0–1). Passing 0–100 for `s`/`l` throws with a hint to use the structured form. |
|
|
678
|
+
| `OkhstColor` object | `{ h: 152, s: 0.95, t: 0.70 }` | Direct OKHST input shape (h: 0–360, s/t: 0–1). The `t` key disambiguates it from `{ h, s, l }`. |
|
|
679
|
+
| `RgbColor` object | `{ r: 38, g: 252, b: 178 }` | sRGB 0–255. RGB tuple `[r, g, b]` is not supported — use this object form. |
|
|
680
|
+
| `OklchColor` object | `{ l: 0.85, c: 0.18, h: 152 }` | OKLCh (L/C: 0–1, H: degrees), same semantics as `oklch()` strings. |
|
|
586
681
|
|
|
587
682
|
`GlazeColorInput` (structured form) is `{ hue, saturation, tone, ... }`:
|
|
588
683
|
|
|
589
|
-
| Field
|
|
590
|
-
|
|
591
|
-
| `hue`
|
|
592
|
-
| `saturation`
|
|
593
|
-
| `tone`
|
|
594
|
-
| `saturationFactor` | `number`
|
|
595
|
-
| `mode`
|
|
596
|
-
| `autoFlip`
|
|
597
|
-
| `opacity`
|
|
598
|
-
| `base`
|
|
599
|
-
| `contrast`
|
|
600
|
-
| `pastel`
|
|
601
|
-
| `role`
|
|
602
|
-
| `name`
|
|
684
|
+
| Field | Type | Description |
|
|
685
|
+
| ------------------ | ------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
686
|
+
| `hue` | `number` | 0–360. |
|
|
687
|
+
| `saturation` | `number` | 0–100. |
|
|
688
|
+
| `tone` | `HCPair<number \| ExtremeValue>` | 0–100 (contrast-shaped) or `'max'`/`'min'`, optional HC pair. |
|
|
689
|
+
| `saturationFactor` | `number` | Multiplier on the seed (0–1). Default: `1`. |
|
|
690
|
+
| `mode` | `AdaptationMode` | Default: `'auto'`. |
|
|
691
|
+
| `autoFlip` | `boolean` | Flip out-of-bounds results instead of clamping. Default: global `autoFlip`. |
|
|
692
|
+
| `opacity` | `number` | Fixed alpha 0–1. |
|
|
693
|
+
| `base` | `GlazeColorToken \| GlazeColorValue` | Optional dependency. See [Pairing colors](#pairing-colors). |
|
|
694
|
+
| `contrast` | `HCPair<ContrastSpec>` | Contrast floor against `base` (WCAG or APCA). Without `base`, anchored to the literal seed. |
|
|
695
|
+
| `pastel` | `boolean` | Per-color `pastel` override. Falls through to the per-theme / per-token `pastel` override when omitted. See [Per-color `pastel`](#per-color-pastel). |
|
|
696
|
+
| `role` | `RoleInput` | Semantic role against `base` / the seed (see [Roles](#roles)). Fixes APCA polarity. |
|
|
697
|
+
| `name` | `string` | Debug label for warnings; doesn't change output keys. Reserved names (`'value'`, `'seed'`, `'externalBase'`) are rejected. |
|
|
603
698
|
|
|
604
699
|
`GlazeFromInput` (from form) is `{ from: GlazeColorValue, ...colorOverrides }`:
|
|
605
700
|
|
|
606
|
-
| Field
|
|
607
|
-
|
|
608
|
-
| `from`
|
|
609
|
-
| `hue`
|
|
610
|
-
| `saturation`
|
|
611
|
-
| `tone`
|
|
612
|
-
| `saturationFactor` | Multiplier on the seed (0–1).
|
|
613
|
-
| `mode`
|
|
614
|
-
| `autoFlip`
|
|
615
|
-
| `contrast`
|
|
616
|
-
| `base`
|
|
617
|
-
| `opacity`
|
|
618
|
-
| `pastel`
|
|
619
|
-
| `role`
|
|
620
|
-
| `name`
|
|
701
|
+
| Field | Notes |
|
|
702
|
+
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
703
|
+
| `from` | **Required.** The source color value — same forms as `GlazeColorValue`. |
|
|
704
|
+
| `hue` | Number (absolute 0–360) or `'+N'`/`'-N'` (relative to seed, never to `base`). |
|
|
705
|
+
| `saturation` | Override seed saturation (0–100). |
|
|
706
|
+
| `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. |
|
|
707
|
+
| `saturationFactor` | Multiplier on the seed (0–1). |
|
|
708
|
+
| `mode` | `'auto'` (default) / `'fixed'` / `'static'`. |
|
|
709
|
+
| `autoFlip` | Flip out-of-bounds results instead of clamping. Default: global `autoFlip`. |
|
|
710
|
+
| `contrast` | Contrast floor (WCAG or APCA). Without `base`, anchored to the literal seed; with `base`, solved per scheme. |
|
|
711
|
+
| `base` | `GlazeColorToken` or raw `GlazeColorValue`. See [Pairing colors](#pairing-colors). |
|
|
712
|
+
| `opacity` | Fixed alpha 0–1. Combining with `contrast` is not recommended — `console.warn` is emitted. |
|
|
713
|
+
| `pastel` | Per-color `pastel` override. Falls through to the per-theme / per-token `pastel` override when omitted. See [Per-color `pastel`](#per-color-pastel). |
|
|
714
|
+
| `role` | Semantic role against `base` / the seed (see [Roles](#roles)). Fixes APCA polarity. |
|
|
715
|
+
| `name` | Debug label only — surfaces in warnings/errors. Does not change output keys. |
|
|
621
716
|
|
|
622
717
|
Named CSS colors (`'red'`, `'blueviolet'`) are not supported.
|
|
623
718
|
|
|
624
719
|
### Defaults
|
|
625
720
|
|
|
626
|
-
Every input form defaults to `mode: 'auto'` so the resolved token adapts between light and dark like an ordinary theme color.
|
|
721
|
+
Every input form defaults to `mode: 'auto'` so the resolved token adapts between light and dark like an ordinary theme color. Tokens store a **sparse local** config override; omitted fields fall through to the live global at resolve time (same as themes). Authoring `.export(override?)` freezes `getConfig() ∪ local ∪ override` at call time.
|
|
627
722
|
|
|
628
723
|
- **Value-shorthand** (bare strings, value objects, and `{ from, ...overrides }`):
|
|
629
|
-
- Light variant preserves the input tone exactly (`lightTone: false`).
|
|
630
|
-
-
|
|
724
|
+
- Light variant preserves the input tone exactly (`lightTone: false` as a local default).
|
|
725
|
+
- Other omitted fields track the live global config.
|
|
631
726
|
- **Structured input** (`{ hue, saturation, tone, ... }`):
|
|
632
|
-
-
|
|
633
|
-
-
|
|
727
|
+
- Omitted tone windows and other fields track the live global config (same as a theme color).
|
|
728
|
+
- `pastel` is instance-only — set via the config override or per-color `pastel`, not `glaze.configure()`.
|
|
634
729
|
|
|
635
730
|
```ts
|
|
636
731
|
// Bare string — adapts automatically
|
|
637
|
-
glaze.color('#26fcb2')
|
|
732
|
+
glaze.color('#26fcb2');
|
|
638
733
|
|
|
639
734
|
// Value-object — same behavior
|
|
640
|
-
glaze.color({ h: 152, s: 0.95, l: 0.74 })
|
|
735
|
+
glaze.color({ h: 152, s: 0.95, l: 0.74 });
|
|
641
736
|
|
|
642
737
|
// OKHST value-object — tone axis
|
|
643
|
-
glaze.color({ h: 152, s: 0.95, t: 0.
|
|
738
|
+
glaze.color({ h: 152, s: 0.95, t: 0.7 });
|
|
644
739
|
|
|
645
740
|
// From form — value + color overrides
|
|
646
|
-
glaze.color({ from: '#1a1a2e', hue: '+20', contrast: 'AA' })
|
|
741
|
+
glaze.color({ from: '#1a1a2e', hue: '+20', contrast: 'AA' });
|
|
647
742
|
|
|
648
743
|
// Structured form — explicit hue/saturation/tone (0–100)
|
|
649
|
-
glaze.color({ hue: 152, saturation: 95, tone: 74 })
|
|
744
|
+
glaze.color({ hue: 152, saturation: 95, tone: 74 });
|
|
650
745
|
```
|
|
651
746
|
|
|
652
747
|
### Token methods
|
|
653
748
|
|
|
654
749
|
A `GlazeColorToken` exposes:
|
|
655
750
|
|
|
656
|
-
| Method
|
|
657
|
-
|
|
658
|
-
| `token.resolve()`
|
|
659
|
-
| `token.token(options?)`
|
|
660
|
-
| `token.tasty(options?)`
|
|
661
|
-
| `token.json(options?)`
|
|
751
|
+
| Method | Description |
|
|
752
|
+
| --------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
753
|
+
| `token.resolve()` | Resolve as a `ResolvedColor` (light/dark/lightContrast/darkContrast variants). |
|
|
754
|
+
| `token.token(options?)` | Flat token map (no color-name key). Options: `format`, `modes`, `states`. |
|
|
755
|
+
| `token.tasty(options?)` | [Tasty](https://tasty.style) state map (no color-name key). Same options as `token.token`. |
|
|
756
|
+
| `token.json(options?)` | JSON map (no color-name key). Options: `format`, `modes`. |
|
|
662
757
|
| `token.css({ name, format?, suffix? })` | CSS custom property declarations grouped by scheme variant. `name` is **required** and becomes the variable identifier (`'brand'` → `--brand-color`). Defaults: `format: 'rgb'`, `suffix: '-color'` (matches `theme.css`). |
|
|
663
|
-
| `token.dtcg(options?)`
|
|
664
|
-
| `token.dtcgResolver({ name, ... })`
|
|
665
|
-
| `token.tailwind({ name, ... })`
|
|
666
|
-
| `token.export()`
|
|
758
|
+
| `token.dtcg(options?)` | DTCG color tokens, one per scheme variant (no color-name key). Each entry is a full `{ $type: 'color', $value }` token. Options: `colorSpace` (`'srgb'` \| `'oklch'`), `modes`. |
|
|
759
|
+
| `token.dtcgResolver({ name, ... })` | A single DTCG Resolver-Module document for this color, keyed by `name` across all scheme variants. `name` is **required**. Same options as `theme.dtcgResolver()` plus `name`. |
|
|
760
|
+
| `token.tailwind({ name, ... })` | Tailwind v4 `@theme` block + dark / high-contrast overrides for this color. `name` is **required** (forms `--color-<name>`). Same options as `theme.tailwind()` plus `name`. |
|
|
761
|
+
| `token.export(override?)` | JSON-safe snapshot — freezes effective config at call time; pass to `glaze.colorFrom(...)` to rehydrate. Optional `override` merges over the instance local (and nested `base` exports). |
|
|
667
762
|
|
|
668
763
|
### Per-instance config override
|
|
669
764
|
|
|
670
|
-
The optional `config`
|
|
765
|
+
The optional `config` argument (`GlazeConfigOverride`) overrides
|
|
766
|
+
resolve-relevant fields for a token or theme. A tone window can be
|
|
767
|
+
`[lo, hi]`, `{ lo, hi, eps }`, or `false` (full range). Both themes and
|
|
768
|
+
standalone tokens keep a sparse local override — omitted fields (except
|
|
769
|
+
instance-only `pastel`) fall through to the live global at resolve time.
|
|
671
770
|
|
|
672
771
|
`GlazeConfigOverride`:
|
|
673
772
|
|
|
674
|
-
| Field
|
|
675
|
-
|
|
676
|
-
| `lightTone`
|
|
677
|
-
| `darkTone`
|
|
678
|
-
| `darkDesaturation` | `0.1`
|
|
679
|
-
| `autoFlip`
|
|
680
|
-
| `
|
|
773
|
+
| Field | Default (from global / fixed) | Description |
|
|
774
|
+
| ------------------ | ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
775
|
+
| `lightTone` | `[10, 100]` | Light tone window: `[lo, hi]`, `{ lo, hi, eps }`, or `false` (disable clamping). |
|
|
776
|
+
| `darkTone` | `[15, 95]` | Dark tone window: `[lo, hi]`, `{ lo, hi, eps }`, or `false` (disable clamping). |
|
|
777
|
+
| `darkDesaturation` | `0.1` | Saturation reduction in dark scheme (0–1). |
|
|
778
|
+
| `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. |
|
|
779
|
+
| `pastel` | `false` (instance-only) | Theme/token-level pastel default for colors that omit per-color `pastel`. Not available on `glaze.configure()`. |
|
|
780
|
+
| `inferRole` | `true` | Infer color `role` from the name when unset. |
|
|
781
|
+
| `shadowTuning` | `undefined` | Default shadow tuning (meaningful for themes; harmless on color tokens). |
|
|
681
782
|
|
|
682
783
|
Config overrides apply to both `glaze.color()` tokens and `glaze()` themes:
|
|
683
784
|
|
|
684
785
|
```ts
|
|
685
786
|
// Standalone color — preserve raw tone in both schemes
|
|
686
|
-
glaze.color('#26fcb2', { darkTone: false })
|
|
787
|
+
glaze.color('#26fcb2', { darkTone: false });
|
|
687
788
|
|
|
688
789
|
// Restore the #000 → white dark flip (full dark range)
|
|
689
790
|
glaze.color('#000000', {
|
|
690
791
|
lightTone: false,
|
|
691
792
|
darkTone: [15, 100],
|
|
692
|
-
})
|
|
793
|
+
});
|
|
693
794
|
|
|
694
795
|
// Structured form with config override
|
|
695
|
-
glaze.color({ hue: 152, saturation: 95, tone: 74 }, { darkTone: false })
|
|
796
|
+
glaze.color({ hue: 152, saturation: 95, tone: 74 }, { darkTone: false, pastel: true });
|
|
696
797
|
|
|
697
798
|
// Theme with config override
|
|
698
|
-
const rawTheme = glaze(280, 80, { lightTone: false })
|
|
799
|
+
const rawTheme = glaze(280, 80, { lightTone: false, pastel: true });
|
|
699
800
|
```
|
|
700
801
|
|
|
701
|
-
|
|
802
|
+
See [Theme config override](#theme-config-override).
|
|
702
803
|
|
|
703
804
|
### Theme config override
|
|
704
805
|
|
|
@@ -722,11 +823,17 @@ const child = t.extend({ config: { darkTone: false } });
|
|
|
722
823
|
// child: lightTone { lo: 0, hi: 50 } (inherited) + darkTone: false (added)
|
|
723
824
|
```
|
|
724
825
|
|
|
725
|
-
`theme.export()`
|
|
826
|
+
`theme.export(override?)` freezes `getConfig() ∪ instance local ∪ override` at
|
|
827
|
+
call time. Restoring via `glaze.themeFrom(data)` (or the compat alias
|
|
828
|
+
`glaze.from`) pins that freeze as the restored theme's local override — matching
|
|
829
|
+
standalone color-token behavior.
|
|
726
830
|
|
|
727
831
|
### `glaze.colorFrom(data)`
|
|
728
832
|
|
|
729
|
-
Inverse of `token.export()`. The exported snapshot includes the original input,
|
|
833
|
+
Inverse of `token.export()`. The exported snapshot includes the original input,
|
|
834
|
+
all overrides (with any `base` token recursively serialized), and the effective
|
|
835
|
+
config freeze from export time — so later `glaze.configure()` calls don't change
|
|
836
|
+
rehydrated tokens.
|
|
730
837
|
|
|
731
838
|
```ts
|
|
732
839
|
const text = glaze.color({ from: '#1a1a1a', contrast: 'AA' });
|
|
@@ -748,7 +855,8 @@ const bg = glaze.color('#1a1a2e');
|
|
|
748
855
|
const text = glaze.color({ from: '#ffffff', base: bg, contrast: 'AA' });
|
|
749
856
|
|
|
750
857
|
// Border 8 tone units lighter than `bg` in each scheme.
|
|
751
|
-
const border = glaze.color({
|
|
858
|
+
const border = glaze.color({
|
|
859
|
+
from: '#000000',
|
|
752
860
|
base: bg,
|
|
753
861
|
tone: '+8',
|
|
754
862
|
mode: 'fixed',
|
|
@@ -789,7 +897,7 @@ The `name` override appears in `console.warn` / Error messages but **does not**
|
|
|
789
897
|
```ts
|
|
790
898
|
theme.colors({
|
|
791
899
|
surface: { tone: 95 },
|
|
792
|
-
text:
|
|
900
|
+
text: { base: 'surface', tone: '-52', contrast: 'AAA' },
|
|
793
901
|
|
|
794
902
|
'shadow-sm': { type: 'shadow', bg: 'surface', fg: 'text', intensity: 5 },
|
|
795
903
|
'shadow-md': { type: 'shadow', bg: 'surface', fg: 'text', intensity: 10 },
|
|
@@ -828,20 +936,22 @@ theme.colors({
|
|
|
828
936
|
|
|
829
937
|
Fine-tune behavior per-color or globally via `glaze.configure({ shadowTuning })`. Per-color `tuning` is merged field-by-field with the global one.
|
|
830
938
|
|
|
831
|
-
| Parameter
|
|
832
|
-
|
|
833
|
-
| `saturationFactor` | `0.18`
|
|
834
|
-
| `maxSaturation`
|
|
835
|
-
| `lightnessFactor`
|
|
836
|
-
| `lightnessBounds`
|
|
837
|
-
| `minGapTarget`
|
|
838
|
-
| `alphaMax`
|
|
839
|
-
| `bgHueBlend`
|
|
939
|
+
| Parameter | Default | Description |
|
|
940
|
+
| ------------------ | -------------- | ------------------------------------------------------------------------------------- |
|
|
941
|
+
| `saturationFactor` | `0.18` | Fraction of fg saturation kept in pigment. |
|
|
942
|
+
| `maxSaturation` | `0.25` | Upper clamp on pigment saturation. |
|
|
943
|
+
| `lightnessFactor` | `0.25` | Multiplier for bg lightness → pigment lightness. |
|
|
944
|
+
| `lightnessBounds` | `[0.05, 0.20]` | Clamp range for pigment lightness. |
|
|
945
|
+
| `minGapTarget` | `0.05` | Target minimum gap between pigment and bg lightness. |
|
|
946
|
+
| `alphaMax` | `1.0` | Asymptotic maximum alpha. |
|
|
947
|
+
| `bgHueBlend` | `0.2` | Blend weight pulling pigment hue toward bg hue. `0` = pure fg hue, `1` = pure bg hue. |
|
|
840
948
|
|
|
841
949
|
```ts
|
|
842
950
|
theme.colors({
|
|
843
951
|
'shadow-soft': {
|
|
844
|
-
type: 'shadow',
|
|
952
|
+
type: 'shadow',
|
|
953
|
+
bg: 'surface',
|
|
954
|
+
intensity: 10,
|
|
845
955
|
tuning: { alphaMax: 0.3, saturationFactor: 0.1 },
|
|
846
956
|
},
|
|
847
957
|
});
|
|
@@ -869,12 +979,12 @@ const css = glaze.format(v, 'oklch');
|
|
|
869
979
|
|
|
870
980
|
`GlazeShadowInput`:
|
|
871
981
|
|
|
872
|
-
| Field
|
|
873
|
-
|
|
874
|
-
| `bg`
|
|
875
|
-
| `fg`
|
|
876
|
-
| `intensity` | `number`
|
|
877
|
-
| `tuning`
|
|
982
|
+
| Field | Type | Description |
|
|
983
|
+
| ----------- | ----------------- | ------------------------------------------------------------------------------ |
|
|
984
|
+
| `bg` | `GlazeColorValue` | Background. Any `GlazeColorValue` form. Alpha components dropped with warning. |
|
|
985
|
+
| `fg` | `GlazeColorValue` | Optional foreground. Same forms as `bg`. |
|
|
986
|
+
| `intensity` | `number` | 0–100. |
|
|
987
|
+
| `tuning` | `ShadowTuning` | Optional. |
|
|
878
988
|
|
|
879
989
|
### Fixed opacity (regular colors)
|
|
880
990
|
|
|
@@ -898,8 +1008,8 @@ Produces a solid color by interpolating between `base` and `target`:
|
|
|
898
1008
|
```ts
|
|
899
1009
|
theme.colors({
|
|
900
1010
|
surface: { tone: 95 },
|
|
901
|
-
accent:
|
|
902
|
-
tint:
|
|
1011
|
+
accent: { tone: 30 },
|
|
1012
|
+
tint: { type: 'mix', base: 'surface', target: 'accent', value: 30 },
|
|
903
1013
|
});
|
|
904
1014
|
```
|
|
905
1015
|
|
|
@@ -914,10 +1024,13 @@ Produces the target color with controlled opacity — useful for hover overlays:
|
|
|
914
1024
|
```ts
|
|
915
1025
|
theme.colors({
|
|
916
1026
|
surface: { tone: 95 },
|
|
917
|
-
black:
|
|
1027
|
+
black: { tone: 0, saturation: 0 },
|
|
918
1028
|
hover: {
|
|
919
|
-
type: 'mix',
|
|
920
|
-
|
|
1029
|
+
type: 'mix',
|
|
1030
|
+
base: 'surface',
|
|
1031
|
+
target: 'black',
|
|
1032
|
+
value: 8,
|
|
1033
|
+
blend: 'transparent',
|
|
921
1034
|
},
|
|
922
1035
|
});
|
|
923
1036
|
// hover → black with alpha = 0.08
|
|
@@ -927,10 +1040,10 @@ The output color has `h`, `s`, `l` from the target and `alpha = value / 100`.
|
|
|
927
1040
|
|
|
928
1041
|
### Blend space (opaque only)
|
|
929
1042
|
|
|
930
|
-
| `space`
|
|
931
|
-
|
|
932
|
-
| `'okhsl'` (default) | Perceptually uniform OKHSL interpolation. | Design token derivation.
|
|
933
|
-
| `'srgb'`
|
|
1043
|
+
| `space` | Behavior | Best for |
|
|
1044
|
+
| ------------------- | ----------------------------------------- | -------------------------------------------------------- |
|
|
1045
|
+
| `'okhsl'` (default) | Perceptually uniform OKHSL interpolation. | Design token derivation. |
|
|
1046
|
+
| `'srgb'` | Linear sRGB channel interpolation. | Matching browser compositing of CSS color-mix / overlay. |
|
|
934
1047
|
|
|
935
1048
|
Transparent blending always composites in linear sRGB (matches browser alpha compositing).
|
|
936
1049
|
|
|
@@ -963,8 +1076,20 @@ Mix colors can reference other mix colors:
|
|
|
963
1076
|
theme.colors({
|
|
964
1077
|
white: { tone: 100, saturation: 0 },
|
|
965
1078
|
black: { tone: 0, saturation: 0 },
|
|
966
|
-
gray:
|
|
967
|
-
|
|
1079
|
+
gray: {
|
|
1080
|
+
type: 'mix',
|
|
1081
|
+
base: 'white',
|
|
1082
|
+
target: 'black',
|
|
1083
|
+
value: 50,
|
|
1084
|
+
space: 'srgb',
|
|
1085
|
+
},
|
|
1086
|
+
lightGray: {
|
|
1087
|
+
type: 'mix',
|
|
1088
|
+
base: 'white',
|
|
1089
|
+
target: 'gray',
|
|
1090
|
+
value: 50,
|
|
1091
|
+
space: 'srgb',
|
|
1092
|
+
},
|
|
968
1093
|
});
|
|
969
1094
|
```
|
|
970
1095
|
|
|
@@ -986,35 +1111,67 @@ const palette = glaze.palette(
|
|
|
986
1111
|
|
|
987
1112
|
`GlazePaletteOptions`:
|
|
988
1113
|
|
|
989
|
-
| Option
|
|
990
|
-
|
|
1114
|
+
| Option | Description |
|
|
1115
|
+
| --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
991
1116
|
| `primary` | Name of the primary theme. The primary's tokens are duplicated **without** prefix in all exports, providing convenient short aliases alongside the prefixed versions. Throws if the name doesn't match any theme. |
|
|
992
1117
|
|
|
993
1118
|
A `GlazePalette` exposes:
|
|
994
1119
|
|
|
995
|
-
| Method
|
|
996
|
-
|
|
997
|
-
| `palette.
|
|
998
|
-
| `palette.
|
|
999
|
-
| `palette.
|
|
1000
|
-
| `palette.
|
|
1120
|
+
| Method | Description |
|
|
1121
|
+
| -------------------------------- | ---------------------------------------------------------------------------------------------------- |
|
|
1122
|
+
| `palette.list()` | Theme names in insertion order. |
|
|
1123
|
+
| `palette.primary` | Primary theme name, if set. |
|
|
1124
|
+
| `palette.theme(name)` | Get a live theme instance by name. |
|
|
1125
|
+
| `palette.themes()` | Shallow copy of the theme map (same instances the palette holds). |
|
|
1126
|
+
| `palette.export(override?)` | Authoring snapshot — restorable via `glaze.paletteFrom()`. Optional override forwarded to themes. |
|
|
1127
|
+
| `palette.tokens(options?)` | Flat token map grouped by scheme variant. |
|
|
1128
|
+
| `palette.tasty(options?)` | [Tasty](https://tasty.style) style-to-state bindings. |
|
|
1129
|
+
| `palette.json(options?)` | Per-theme **resolved** color JSON (not restorable as authoring config). |
|
|
1130
|
+
| `palette.css(options?)` | CSS custom property declaration strings. |
|
|
1131
|
+
| `palette.dtcg(options?)` | Per-scheme W3C DTCG token trees. |
|
|
1132
|
+
| `palette.dtcgResolver(options?)` | One DTCG Resolver-Module document for every scheme. |
|
|
1133
|
+
| `palette.tailwind(options?)` | One Tailwind CSS v4 theme with scheme overrides. |
|
|
1134
|
+
|
|
1135
|
+
### `palette.export(override?)` / `glaze.paletteFrom()`
|
|
1136
|
+
|
|
1137
|
+
```ts
|
|
1138
|
+
const snapshot = palette.export();
|
|
1139
|
+
// → {
|
|
1140
|
+
// kind: 'palette',
|
|
1141
|
+
// version: 1,
|
|
1142
|
+
// primary: 'brand',
|
|
1143
|
+
// themes: { brand: { kind: 'theme', ... }, danger: { ... } },
|
|
1144
|
+
// }
|
|
1145
|
+
|
|
1146
|
+
const restored = glaze.paletteFrom(JSON.parse(JSON.stringify(snapshot)));
|
|
1147
|
+
const brand = restored.theme('brand')!;
|
|
1148
|
+
```
|
|
1149
|
+
|
|
1150
|
+
Optional `override` is forwarded to each nested `theme.export(override)`.
|
|
1151
|
+
Config snapshot vs resolved output: use `export()` / `paletteFrom()` to
|
|
1152
|
+
persist and restore the authoring graph (themes, color defs, relations). Use
|
|
1153
|
+
`json()` / `tokens()` / `css()` / … to emit resolved color strings for apps
|
|
1154
|
+
and design tools.
|
|
1001
1155
|
|
|
1002
1156
|
### `GlazePaletteExportOptions`
|
|
1003
1157
|
|
|
1004
1158
|
Shared by `tokens`, `tasty`, and `css`:
|
|
1005
1159
|
|
|
1006
|
-
| Option
|
|
1007
|
-
|
|
1008
|
-
| `prefix`
|
|
1009
|
-
| `primary` | inherits from palette creation | `string` to override, `false` to disable for this call.
|
|
1160
|
+
| Option | Default | Description |
|
|
1161
|
+
| --------- | ------------------------------ | -------------------------------------------------------------------------------------------- |
|
|
1162
|
+
| `prefix` | `true` (= `"<themeName>-"`) | `false` disables prefixing. Or pass a custom map: `{ primary: 'brand-', danger: 'error-' }`. |
|
|
1163
|
+
| `primary` | inherits from palette creation | `string` to override, `false` to disable for this call. |
|
|
1010
1164
|
|
|
1011
1165
|
Each export method also accepts its own format/options shape:
|
|
1012
1166
|
|
|
1013
|
-
| Method
|
|
1014
|
-
|
|
1015
|
-
| `palette.tokens(options?)`
|
|
1016
|
-
| `palette.tasty(options?)`
|
|
1017
|
-
| `palette.css(options?)`
|
|
1167
|
+
| Method | Additional options |
|
|
1168
|
+
| -------------------------------- | ----------------------------------------- |
|
|
1169
|
+
| `palette.tokens(options?)` | `format`, `modes` |
|
|
1170
|
+
| `palette.tasty(options?)` | `format`, `modes`, `states` |
|
|
1171
|
+
| `palette.css(options?)` | `format`, `suffix` |
|
|
1172
|
+
| `palette.dtcg(options?)` | `colorSpace`, `modes` |
|
|
1173
|
+
| `palette.dtcgResolver(options?)` | `colorSpace`, `modes`, resolver names |
|
|
1174
|
+
| `palette.tailwind(options?)` | `format`, `modes`, selectors, `namespace` |
|
|
1018
1175
|
|
|
1019
1176
|
`palette.css()` does not accept `modes`; it always returns all four CSS strings (`light`, `dark`, `lightContrast`, `darkContrast`).
|
|
1020
1177
|
|
|
@@ -1025,8 +1182,8 @@ By default all palette tokens are prefixed:
|
|
|
1025
1182
|
```ts
|
|
1026
1183
|
palette.tokens();
|
|
1027
1184
|
// → {
|
|
1028
|
-
// light: { 'primary-surface': '
|
|
1029
|
-
// dark: { 'primary-surface': '
|
|
1185
|
+
// light: { 'primary-surface': 'oklch(...)', 'danger-surface': 'oklch(...)' },
|
|
1186
|
+
// dark: { 'primary-surface': 'oklch(...)', 'danger-surface': 'oklch(...)' },
|
|
1030
1187
|
// }
|
|
1031
1188
|
```
|
|
1032
1189
|
|
|
@@ -1062,10 +1219,10 @@ const palette = glaze.palette(
|
|
|
1062
1219
|
palette.tokens();
|
|
1063
1220
|
// → {
|
|
1064
1221
|
// light: {
|
|
1065
|
-
// 'primary-surface': '
|
|
1066
|
-
// 'danger-surface': '
|
|
1067
|
-
// 'success-surface': '
|
|
1068
|
-
// 'surface': '
|
|
1222
|
+
// 'primary-surface': 'oklch(...)',
|
|
1223
|
+
// 'danger-surface': 'oklch(...)',
|
|
1224
|
+
// 'success-surface': 'oklch(...)',
|
|
1225
|
+
// 'surface': 'oklch(...)', // unprefixed alias
|
|
1069
1226
|
// },
|
|
1070
1227
|
// }
|
|
1071
1228
|
```
|
|
@@ -1091,8 +1248,8 @@ JSON export groups by theme name (no prefix needed):
|
|
|
1091
1248
|
```ts
|
|
1092
1249
|
palette.json();
|
|
1093
1250
|
// → {
|
|
1094
|
-
// primary: { surface: { light: '
|
|
1095
|
-
// danger: { surface: { light: '
|
|
1251
|
+
// primary: { surface: { light: 'oklch(...)', dark: 'oklch(...)' } },
|
|
1252
|
+
// danger: { surface: { light: 'oklch(...)', dark: 'oklch(...)' } },
|
|
1096
1253
|
// }
|
|
1097
1254
|
```
|
|
1098
1255
|
|
|
@@ -1116,7 +1273,7 @@ It does not accept `modes`; all four result fields are always returned.
|
|
|
1116
1273
|
DTCG export for a palette. Prefix defaults to `true` and the palette-level `primary` is honored (the primary theme's tokens are duplicated without prefix as aliases).
|
|
1117
1274
|
|
|
1118
1275
|
```ts
|
|
1119
|
-
palette.dtcg()
|
|
1276
|
+
palette.dtcg();
|
|
1120
1277
|
// → {
|
|
1121
1278
|
// light: {
|
|
1122
1279
|
// 'primary-surface': { $type: 'color', $value: { ... } },
|
|
@@ -1134,7 +1291,7 @@ Accepts `GlazeDtcgOptions` plus `GlazePaletteExportOptions`.
|
|
|
1134
1291
|
Resolver-Module export for a palette. Same as `theme.dtcgResolver()` but merges every theme (with prefix / `primary` aliasing) into the single `sets.base` source and each `scheme` context. Prefix defaults to `true`; the palette-level `primary` is honored.
|
|
1135
1292
|
|
|
1136
1293
|
```ts
|
|
1137
|
-
palette.dtcgResolver()
|
|
1294
|
+
palette.dtcgResolver();
|
|
1138
1295
|
// → {
|
|
1139
1296
|
// version: '2025.10',
|
|
1140
1297
|
// sets: { base: { sources: [ { 'primary-surface': {…}, 'surface': {…}, 'danger-surface': {…} } ] } },
|
|
@@ -1167,24 +1324,24 @@ Accepts `GlazeTailwindOptions` plus `GlazePaletteExportOptions`. The palette `pr
|
|
|
1167
1324
|
|
|
1168
1325
|
Control the color format with the `format` option on any export method:
|
|
1169
1326
|
|
|
1170
|
-
| Format
|
|
1171
|
-
|
|
1172
|
-
| `'
|
|
1173
|
-
| `'
|
|
1174
|
-
| `'
|
|
1175
|
-
| `'
|
|
1176
|
-
| `'
|
|
1327
|
+
| Format | Output (alpha = 1) | Output (alpha < 1) | Notes |
|
|
1328
|
+
| --------------------------------------------- | ------------------ | -------------------- | --------------------------------------------------------------------------------------------- |
|
|
1329
|
+
| `'oklch'` (default for CSS-string exports) | `oklch(L C H)` | `oklch(L C H / A)` | OKLab-based LCH. Native CSS. Required for `splitHue`. |
|
|
1330
|
+
| `'rgb'` | `rgb(R G B)` | `rgb(R G B / A)` | Rounded integers, modern space syntax. |
|
|
1331
|
+
| `'hsl'` | `hsl(H S% L%)` | `hsl(H S% L% / A)` | Modern space syntax. |
|
|
1332
|
+
| `'okhsl'` | `okhsl(H S% L%)` | `okhsl(H S% L% / A)` | Glaze's native format, not a CSS function. **[Tasty](https://tasty.style)-only** (`tasty()`, `token()`, `.tasty()`). |
|
|
1333
|
+
| `'okhst'` | `okhst(H S% T%)` | `okhst(H S% T% / A)` | OKHST tone axis. **[Tasty](https://tasty.style)-only** — same restriction as `okhsl`. |
|
|
1177
1334
|
|
|
1178
1335
|
```ts
|
|
1179
|
-
theme.tokens();
|
|
1180
|
-
theme.tokens({ format: 'rgb' });
|
|
1181
|
-
theme.tasty();
|
|
1182
|
-
theme.tasty({ format: 'okhst' });
|
|
1336
|
+
theme.tokens(); // 'oklch(0.965 0.0123 280)' (default)
|
|
1337
|
+
theme.tokens({ format: 'rgb' }); // 'rgb(244 240 250)'
|
|
1338
|
+
theme.tasty(); // 'oklch(0.965 0.0123 280)' (default)
|
|
1339
|
+
theme.tasty({ format: 'okhst' }); // 'okhst(280 60% 97%)'
|
|
1183
1340
|
```
|
|
1184
1341
|
|
|
1185
1342
|
All numeric output strips trailing zeros for cleaner CSS (e.g. `95` not `95.0`).
|
|
1186
1343
|
|
|
1187
|
-
The `format` option works on CSS-string exports: `theme.tokens()`, `theme.tasty()`, `theme.json()`, `theme.css()`, `theme.tailwind()`, the same on `palette`, and on `token.token()` / `.tasty()` / `.json()` / `.css()` / `.tailwind()`. **`okhsl` and `okhst` throw on non-Tasty exports** (`tokens`, `json`, `css`, `tailwind`) — they are not native CSS color spaces.
|
|
1344
|
+
The `format` option works on CSS-string exports: `theme.tokens()`, `theme.tasty()`, `theme.json()`, `theme.css()`, `theme.tailwind()`, the same on `palette`, and on `token.token()` / `.tasty()` / `.json()` / `.css()` / `.tailwind()`. **`okhsl` and `okhst` throw on non-[Tasty](https://tasty.style) exports** (`tokens`, `json`, `css`, `tailwind`) — they are not native CSS color spaces.
|
|
1188
1345
|
|
|
1189
1346
|
### Hue channel splitting (`splitHue`)
|
|
1190
1347
|
|
|
@@ -1198,7 +1355,7 @@ On `theme.css()`, `theme.tasty()`, `palette.css()`, `palette.tasty()`, and stand
|
|
|
1198
1355
|
--accent-color: oklch(0.62 0.03 var(--accent-hue));
|
|
1199
1356
|
```
|
|
1200
1357
|
|
|
1201
|
-
**Requirements:** every exported color must be pastel (`pastel: true`
|
|
1358
|
+
**Requirements:** every exported color must be pastel (`pastel: true` on the theme/token override or per-color). Pastel mode bounds chroma by the hue-independent safe chroma at each lightness, so emitted `C` stays in sRGB for any rotated hue. Non-pastel palettes throw rather than emit values that would clip under rotation.
|
|
1202
1359
|
|
|
1203
1360
|
**Limitations:** `oklch` only (native CSS `var()` in the hue slot). Shadow and mix colors stay inline (blended hue). Standalone `.token()` / `.tasty()` do not support `splitHue` (return shape cannot carry the `$name-hue` declaration).
|
|
1204
1361
|
|
|
@@ -1210,11 +1367,11 @@ On `theme.css()`, `theme.tasty()`, `palette.css()`, `palette.tasty()`, and stand
|
|
|
1210
1367
|
|
|
1211
1368
|
`mode` controls how a color adapts across schemes:
|
|
1212
1369
|
|
|
1213
|
-
| Mode
|
|
1214
|
-
|
|
1215
|
-
| `'auto'` (default) | Full adaptation. Dark
|
|
1216
|
-
| `'fixed'`
|
|
1217
|
-
| `'static'`
|
|
1370
|
+
| Mode | Behavior |
|
|
1371
|
+
| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
1372
|
+
| `'auto'` (default) | Full adaptation. Dark uses **dark tone inversion** (`100 − t`) and then remaps into the dark tone window. High-contrast uses the full range. |
|
|
1373
|
+
| `'fixed'` | Color stays recognizable. Tone is _mapped_ (not inverted) into the dark window. Use for brand buttons, CTAs, status banners. |
|
|
1374
|
+
| `'static'` | No adaptation. Same tone in every scheme. |
|
|
1218
1375
|
|
|
1219
1376
|
### How relative tone adapts
|
|
1220
1377
|
|
|
@@ -1241,11 +1398,17 @@ Offsets that would push past `[0, 100]` clamp to the boundary, or — with `auto
|
|
|
1241
1398
|
|
|
1242
1399
|
## Light / dark scheme mapping
|
|
1243
1400
|
|
|
1244
|
-
The mapping is
|
|
1401
|
+
The mapping is a single tone pipeline; there is no Möbius curve. See
|
|
1402
|
+
[Scheme adaptation](okhst.md#scheme-adaptation) for the product-level model and
|
|
1403
|
+
the [canonical OKHST specification](https://github.com/tenphi/okhst) for the
|
|
1404
|
+
transfer math.
|
|
1245
1405
|
|
|
1246
1406
|
### Light scheme
|
|
1247
1407
|
|
|
1248
|
-
An authored tone (0–100) is remapped into the `lightTone`
|
|
1408
|
+
An authored tone (0–100) is remapped into the `lightTone` **tone window**.
|
|
1409
|
+
The window's `lo`/`hi` are OKHSL-lightness boundaries (0–100); authored tone is
|
|
1410
|
+
positioned within the corresponding tone interval and converted to final OKHSL
|
|
1411
|
+
lightness. `static` mode and HC variants use the full range.
|
|
1249
1412
|
|
|
1250
1413
|
```
|
|
1251
1414
|
window = lightTone // default [10, 100]
|
|
@@ -1263,7 +1426,10 @@ inverted = 100 - authorTone
|
|
|
1263
1426
|
finalTone = remap(inverted, window)
|
|
1264
1427
|
```
|
|
1265
1428
|
|
|
1266
|
-
|
|
1429
|
+
The inversion preserves authored tone spacing without a fitted curve. This is
|
|
1430
|
+
exactly contrast-even for neutrals and approximate for chromatic colors. The
|
|
1431
|
+
ordinary light/dark asymmetry lives in the two windows' `(lo, hi, eps)` values
|
|
1432
|
+
(`eps` defaults to the reference `0.05`).
|
|
1267
1433
|
|
|
1268
1434
|
**`fixed`** — remap into the dark window without inversion:
|
|
1269
1435
|
|
|
@@ -1278,7 +1444,7 @@ In high-contrast variants both windows are bypassed (forced to the full `[0, 100
|
|
|
1278
1444
|
`darkDesaturation` reduces saturation for all colors in dark scheme:
|
|
1279
1445
|
|
|
1280
1446
|
```ts
|
|
1281
|
-
S_dark = S_light * (1 - darkDesaturation) // default: 0.1
|
|
1447
|
+
S_dark = S_light * (1 - darkDesaturation); // default: 0.1
|
|
1282
1448
|
```
|
|
1283
1449
|
|
|
1284
1450
|
`static` mode skips desaturation.
|
|
@@ -1290,7 +1456,7 @@ S_dark = S_light * (1 - darkDesaturation) // default: 0.1
|
|
|
1290
1456
|
```ts
|
|
1291
1457
|
glaze.configure({
|
|
1292
1458
|
lightTone: [10, 100], // [lo, hi]; or { lo, hi, eps } / false to disable clamping
|
|
1293
|
-
darkTone: [15, 95],
|
|
1459
|
+
darkTone: [15, 95], // [lo, hi]; or { lo, hi, eps } / false to disable clamping
|
|
1294
1460
|
darkDesaturation: 0.1,
|
|
1295
1461
|
states: {
|
|
1296
1462
|
dark: '@media(prefers-color-scheme: dark)',
|
|
@@ -1307,31 +1473,33 @@ glaze.configure({
|
|
|
1307
1473
|
});
|
|
1308
1474
|
```
|
|
1309
1475
|
|
|
1310
|
-
A `ToneWindow` is `[lo, hi]` (OKHSL-lightness
|
|
1476
|
+
A `ToneWindow` is `[lo, hi]` (OKHSL-lightness boundaries, reference eps — the
|
|
1477
|
+
common form), `{ lo, hi, eps }` (advanced: explicit per-scheme render eps), or
|
|
1478
|
+
`false` for the full range `[0, 100]` at the reference eps. `false` removes the
|
|
1479
|
+
boundaries, not the tone transfer.
|
|
1311
1480
|
|
|
1312
1481
|
`GlazeConfig`:
|
|
1313
1482
|
|
|
1314
|
-
| Field
|
|
1315
|
-
|
|
1316
|
-
| `lightTone`
|
|
1317
|
-
| `darkTone`
|
|
1318
|
-
| `darkDesaturation`
|
|
1319
|
-
| `states.dark`
|
|
1320
|
-
| `states.highContrast` | `'@media(prefers-contrast: more)'`
|
|
1321
|
-
| `modes.dark`
|
|
1322
|
-
| `modes.highContrast`
|
|
1323
|
-
| `shadowTuning`
|
|
1324
|
-
| `autoFlip`
|
|
1325
|
-
| `
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
|
1329
|
-
|---|---|
|
|
1483
|
+
| Field | Default | Description |
|
|
1484
|
+
| --------------------- | -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
1485
|
+
| `lightTone` | `[10, 100]` | Light scheme tone window: `[lo, hi]`, `{ lo, hi, eps }`, or `false` to disable clamping. Bypassed in HC. |
|
|
1486
|
+
| `darkTone` | `[15, 95]` | Dark scheme tone window: `[lo, hi]`, `{ lo, hi, eps }`, or `false` to disable clamping. Bypassed in HC. |
|
|
1487
|
+
| `darkDesaturation` | `0.1` | Saturation reduction in dark scheme (0–1). |
|
|
1488
|
+
| `states.dark` | `'@media(prefers-color-scheme: dark)'` | State alias for dark mode tokens ([Tasty](https://tasty.style) export). Defaults to a media query so tokens react to the OS preference without registering custom states. |
|
|
1489
|
+
| `states.highContrast` | `'@media(prefers-contrast: more)'` | State alias for HC tokens ([Tasty](https://tasty.style) export). |
|
|
1490
|
+
| `modes.dark` | `true` | Include dark variants in exports. |
|
|
1491
|
+
| `modes.highContrast` | `false` | Include HC variants. |
|
|
1492
|
+
| `shadowTuning` | `undefined` | Default tuning for all shadow colors. Per-color tuning merges field-by-field. |
|
|
1493
|
+
| `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). |
|
|
1494
|
+
| `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). |
|
|
1495
|
+
|
|
1496
|
+
| Method | Description |
|
|
1497
|
+
| ------------------------- | ----------------------------------------------------------------------------------- |
|
|
1330
1498
|
| `glaze.configure(config)` | Merge into the global config. Bumps a config version that invalidates theme caches. |
|
|
1331
|
-
| `glaze.getConfig()`
|
|
1332
|
-
| `glaze.resetConfig()`
|
|
1499
|
+
| `glaze.getConfig()` | Snapshot the current resolved config (shallow copy). |
|
|
1500
|
+
| `glaze.resetConfig()` | Reset to defaults (also bumps the version counter). |
|
|
1333
1501
|
|
|
1334
|
-
|
|
1502
|
+
Themes and standalone color tokens keep a sparse local `GlazeConfigOverride` and merge the live global at resolve time for omitted fields. Authoring `.export(override?)` freezes the effective merge at call time; restored instances pin that freeze. `pastel` is instance-only (theme/token override or per-color) — not set via `configure()`.
|
|
1335
1503
|
|
|
1336
1504
|
---
|
|
1337
1505
|
|
|
@@ -1361,32 +1529,38 @@ Resolution priority (highest first):
|
|
|
1361
1529
|
|
|
1362
1530
|
## Validation
|
|
1363
1531
|
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
|
1371
|
-
|
|
|
1372
|
-
| `
|
|
1373
|
-
| `
|
|
1374
|
-
|
|
|
1375
|
-
| `
|
|
1376
|
-
|
|
|
1377
|
-
|
|
|
1378
|
-
|
|
|
1379
|
-
|
|
|
1380
|
-
|
|
|
1381
|
-
|
|
|
1382
|
-
| `
|
|
1383
|
-
|
|
|
1384
|
-
|
|
|
1385
|
-
|
|
|
1386
|
-
|
|
|
1387
|
-
|
|
|
1388
|
-
|
|
|
1389
|
-
|
|
|
1532
|
+
Invalid definitions throw before resolution when Glaze cannot produce a
|
|
1533
|
+
well-defined dependency graph. Recoverable numeric bounds are clamped. A
|
|
1534
|
+
physically unreachable contrast floor or a potentially misleading
|
|
1535
|
+
contrast/opacity combination emits `console.warn` and returns the closest
|
|
1536
|
+
available result.
|
|
1537
|
+
|
|
1538
|
+
| Condition | Behavior |
|
|
1539
|
+
| --------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
|
|
1540
|
+
| `contrast` without `base` in a **theme** color | Validation error |
|
|
1541
|
+
| Relative `tone` without `base` in a **theme** color | Validation error |
|
|
1542
|
+
| `contrast` without `base` in `glaze.color()` | Anchors against the literal seed (no error) |
|
|
1543
|
+
| Relative `tone` without `base` in `glaze.color()` | Anchors against the literal seed (no error) |
|
|
1544
|
+
| Relative `tone` overshoots `[0, 100]` | Mirror to the other side of the base (`autoFlip` on, default), or clamp to the boundary (`autoFlip` off) |
|
|
1545
|
+
| `tone` resolves outside 0–100 | Clamp silently |
|
|
1546
|
+
| `'max'` / `'min'` without `base` | Allowed — resolves to the scheme's tone extreme (root color) |
|
|
1547
|
+
| `saturation` outside 0–1 | Clamp silently |
|
|
1548
|
+
| Circular `base` references | Validation error |
|
|
1549
|
+
| `base` references non-existent name | Validation error |
|
|
1550
|
+
| Shadow `bg` references non-existent color | Validation error |
|
|
1551
|
+
| Shadow `fg` references non-existent color | Validation error |
|
|
1552
|
+
| Shadow `bg` references another shadow color | Validation error |
|
|
1553
|
+
| Shadow `fg` references another shadow color | Validation error |
|
|
1554
|
+
| Regular color `base` references a shadow color | Validation error |
|
|
1555
|
+
| Shadow `intensity` outside 0–100 | Clamp silently |
|
|
1556
|
+
| `contrast` + `opacity` combined | `console.warn` |
|
|
1557
|
+
| Mix `base` references non-existent color | Validation error |
|
|
1558
|
+
| Mix `target` references non-existent color | Validation error |
|
|
1559
|
+
| Mix `base` references a shadow color | Validation error |
|
|
1560
|
+
| Mix `target` references a shadow color | Validation error |
|
|
1561
|
+
| Mix `value` outside 0–100 | Clamp silently |
|
|
1562
|
+
| Circular references involving mix colors | Validation error |
|
|
1563
|
+
| Contrast target physically unreachable | `console.warn` (deduped per `(name, scheme, target)`); closest passing variant returned |
|
|
1390
1564
|
|
|
1391
1565
|
---
|
|
1392
1566
|
|
|
@@ -1412,19 +1586,19 @@ import {
|
|
|
1412
1586
|
} from '@tenphi/glaze';
|
|
1413
1587
|
```
|
|
1414
1588
|
|
|
1415
|
-
| Function
|
|
1416
|
-
|
|
1417
|
-
| `okhslToLinearSrgb(h, s, l)`
|
|
1418
|
-
| `okhslToSrgb(h, s, l)`
|
|
1419
|
-
| `okhslToOklab([h, s, l])`
|
|
1420
|
-
| `oklabToOkhsl([L, a, b])`
|
|
1421
|
-
| `srgbToOkhsl([r, g, b])`
|
|
1422
|
-
| `hslToSrgb(h, s, l)`
|
|
1423
|
-
| `parseHex(hex)`
|
|
1424
|
-
| `parseHexAlpha(hex)`
|
|
1425
|
-
| `relativeLuminanceFromLinearRgb(rgb)` | WCAG relative luminance from linear sRGB.
|
|
1426
|
-
| `contrastRatioFromLuminance(yA, yB)`
|
|
1427
|
-
| `gamutClampedLuminance(linearRgb)`
|
|
1589
|
+
| Function | Description |
|
|
1590
|
+
| ------------------------------------- | ------------------------------------------------------------------------ |
|
|
1591
|
+
| `okhslToLinearSrgb(h, s, l)` | OKHSL (h: 0–360, s/l: 0–1) → linear sRGB tuple. |
|
|
1592
|
+
| `okhslToSrgb(h, s, l)` | OKHSL → gamma-encoded sRGB tuple (0–1 per channel). |
|
|
1593
|
+
| `okhslToOklab([h, s, l])` | OKHSL → OKLab `[L, a, b]`. |
|
|
1594
|
+
| `oklabToOkhsl([L, a, b])` | OKLab → OKHSL. |
|
|
1595
|
+
| `srgbToOkhsl([r, g, b])` | Gamma sRGB (0–1) → OKHSL. |
|
|
1596
|
+
| `hslToSrgb(h, s, l)` | CSS HSL → sRGB tuple. |
|
|
1597
|
+
| `parseHex(hex)` | Parse `#rgb` / `#rrggbb` to sRGB tuple. Returns `null` on invalid input. |
|
|
1598
|
+
| `parseHexAlpha(hex)` | Parse `#rgb` / `#rrggbb` / `#rrggbbaa`; returns `[r, g, b, a?]`. |
|
|
1599
|
+
| `relativeLuminanceFromLinearRgb(rgb)` | WCAG relative luminance from linear sRGB. |
|
|
1600
|
+
| `contrastRatioFromLuminance(yA, yB)` | WCAG contrast ratio from two luminances. |
|
|
1601
|
+
| `gamutClampedLuminance(linearRgb)` | Relative luminance with channel clamping for out-of-gamut colors. |
|
|
1428
1602
|
|
|
1429
1603
|
### Format writers
|
|
1430
1604
|
|
|
@@ -1432,8 +1606,8 @@ import {
|
|
|
1432
1606
|
import { formatOkhsl, formatRgb, formatHsl, formatOklch } from '@tenphi/glaze';
|
|
1433
1607
|
|
|
1434
1608
|
formatOkhsl(280, 60, 95); // 'okhsl(280 60% 95%)'
|
|
1435
|
-
formatRgb(280, 60, 95);
|
|
1436
|
-
formatHsl(280, 60, 95);
|
|
1609
|
+
formatRgb(280, 60, 95); // 'rgb(244 240 250)'
|
|
1610
|
+
formatHsl(280, 60, 95); // 'hsl(280 60% 95%)'
|
|
1437
1611
|
formatOklch(280, 60, 95); // 'oklch(0.95 ... 280)'
|
|
1438
1612
|
```
|
|
1439
1613
|
|
|
@@ -1454,17 +1628,19 @@ import {
|
|
|
1454
1628
|
} from '@tenphi/glaze';
|
|
1455
1629
|
```
|
|
1456
1630
|
|
|
1457
|
-
| Function
|
|
1458
|
-
|
|
1459
|
-
| `toTone(l, eps?)`
|
|
1460
|
-
| `fromTone(t, eps?)`
|
|
1461
|
-
| `toneFromY(y, eps?)` / `yFromTone(t, eps?)` | Same transfer in luminance space (0–1).
|
|
1462
|
-
| `okhstToOkhsl({ h, s, t })`
|
|
1463
|
-
| `okhslToOkhst({ h, s, l })`
|
|
1464
|
-
| `variantToOkhsl(variant)`
|
|
1465
|
-
| `REF_EPS`
|
|
1631
|
+
| Function | Description |
|
|
1632
|
+
| ------------------------------------------- | ------------------------------------------------------------------------- |
|
|
1633
|
+
| `toTone(l, eps?)` | OKHSL lightness (0–1) → tone (0–100). Defaults to `REF_EPS`. |
|
|
1634
|
+
| `fromTone(t, eps?)` | Tone (0–100) → OKHSL lightness (0–1). Inverse of `toTone`. |
|
|
1635
|
+
| `toneFromY(y, eps?)` / `yFromTone(t, eps?)` | Same transfer in luminance space (0–1). |
|
|
1636
|
+
| `okhstToOkhsl({ h, s, t })` | OKHST → OKHSL (`{ h, s, l }`). |
|
|
1637
|
+
| `okhslToOkhst({ h, s, l })` | OKHSL → OKHST (`{ h, s, t }`). |
|
|
1638
|
+
| `variantToOkhsl(variant)` | `ResolvedColorVariant` (stores `t`) → `{ h, s, l, alpha }` for rendering. |
|
|
1639
|
+
| `REF_EPS` | Reference epsilon (`0.05`) for the canonical tone axis. |
|
|
1466
1640
|
|
|
1467
|
-
`ResolvedColorVariant`
|
|
1641
|
+
`ResolvedColorVariant` stores `{ h, s, t, alpha }` (tone, not lightness). Use
|
|
1642
|
+
`variantToOkhsl(variant).l` to recover OKHSL lightness. See
|
|
1643
|
+
[OKHST in Glaze](okhst.md) for the model.
|
|
1468
1644
|
|
|
1469
1645
|
### Contrast solver
|
|
1470
1646
|
|
|
@@ -1478,29 +1654,29 @@ import {
|
|
|
1478
1654
|
} from '@tenphi/glaze';
|
|
1479
1655
|
```
|
|
1480
1656
|
|
|
1481
|
-
| Function
|
|
1482
|
-
|
|
1483
|
-
| `findToneForContrast(opts)`
|
|
1484
|
-
| `findValueForMixContrast(opts)`
|
|
1657
|
+
| Function | Description |
|
|
1658
|
+
| ----------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
1659
|
+
| `findToneForContrast(opts)` | Binary-search for the tone (0–1) that meets a contrast floor (WCAG or APCA) against a base color. Returns `{ tone, contrast, met, branch, flipped? }`. |
|
|
1660
|
+
| `findValueForMixContrast(opts)` | Same, but searches for a mix `value` (0–1) that meets a contrast floor between a base and a target. |
|
|
1485
1661
|
| `resolveContrastForMode(spec, isHC, polarity?, outerExplicitHC?)` | Resolves a `ContrastSpec` to `{ metric: 'wcag' \| 'apca', target }` for the requested mode (picks the normal or HC entry of any pair). In HC, applies the metric's auto-enhancement unless `outerExplicitHC` is set or the inner metric pair carries an explicit HC value: APCA +15 Lc (clamped to 106); WCAG AA → AAA / AA-large → AAA-large (AAA-family and bare numbers unchanged). |
|
|
1486
|
-
| `resolveMinContrast(value)`
|
|
1487
|
-
| `apcaContrast(yText, yBg)`
|
|
1662
|
+
| `resolveMinContrast(value)` | Resolves a `MinContrast` (WCAG preset or number) to a numeric ratio. |
|
|
1663
|
+
| `apcaContrast(yText, yBg)` | APCA Lc magnitude (0–106) for two relative luminances. |
|
|
1488
1664
|
|
|
1489
1665
|
Exported constants: `APCA_PRESETS`, `APCA_HC_ENHANCEMENT` (`15`, the Enhanced Level delta), `APCA_MAX_LC` (`106`).
|
|
1490
1666
|
|
|
1491
1667
|
`findToneForContrast` options:
|
|
1492
1668
|
|
|
1493
|
-
| Option
|
|
1494
|
-
|
|
1495
|
-
| `hue`
|
|
1496
|
-
| `saturation`
|
|
1497
|
-
| `preferredTone`
|
|
1498
|
-
| `baseLinearRgb`
|
|
1499
|
-
| `contrast`
|
|
1500
|
-
| `toneRange`
|
|
1501
|
-
| `epsilon`
|
|
1502
|
-
| `maxIterations`
|
|
1503
|
-
| `initialDirection` | higher-contrast side | Direction to search first (`'lighter'` or `'darker'`).
|
|
1504
|
-
| `flip`
|
|
1669
|
+
| Option | Default | Description |
|
|
1670
|
+
| ------------------ | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
1671
|
+
| `hue` | — | Candidate hue (0–360). |
|
|
1672
|
+
| `saturation` | — | Candidate saturation (0–1). |
|
|
1673
|
+
| `preferredTone` | — | Preferred candidate tone (0–1). Kept if it already meets the target. |
|
|
1674
|
+
| `baseLinearRgb` | — | Base color as linear sRGB tuple. |
|
|
1675
|
+
| `contrast` | — | `ResolvedContrast` (`{ metric, target }`). |
|
|
1676
|
+
| `toneRange` | `[0, 1]` | Search bounds in tone. |
|
|
1677
|
+
| `epsilon` | `1e-4` | Convergence threshold. |
|
|
1678
|
+
| `maxIterations` | `18` | Max binary-search iterations per branch. |
|
|
1679
|
+
| `initialDirection` | higher-contrast side | Direction to search first (`'lighter'` or `'darker'`). |
|
|
1680
|
+
| `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. |
|
|
1505
1681
|
|
|
1506
1682
|
Result: `{ tone, contrast, met, branch: 'lighter' | 'darker' | 'preferred', flipped? }`. `flipped: true` indicates the initial direction failed and the opposite direction satisfied the target.
|