@tenphi/glaze 0.15.0 → 0.16.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 +3 -0
- package/dist/index.cjs +884 -113
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +496 -7
- package/dist/index.d.mts +496 -7
- package/dist/index.mjs +875 -114
- package/dist/index.mjs.map +1 -1
- package/docs/api.md +294 -14
- package/docs/methodology.md +437 -222
- package/docs/migration.md +70 -5
- package/docs/okhst.md +45 -2
- package/package.json +2 -1
package/docs/api.md
CHANGED
|
@@ -115,14 +115,14 @@ Flat token map grouped by scheme variant.
|
|
|
115
115
|
|
|
116
116
|
```ts
|
|
117
117
|
theme.tokens()
|
|
118
|
-
// → { light: { surface: '
|
|
118
|
+
// → { light: { surface: 'oklch(...)' }, dark: { surface: 'oklch(...)' } }
|
|
119
119
|
```
|
|
120
120
|
|
|
121
121
|
`GlazeJsonOptions`:
|
|
122
122
|
|
|
123
123
|
| Option | Default | Description |
|
|
124
124
|
|---|---|---|
|
|
125
|
-
| `format` | `'
|
|
125
|
+
| `format` | `'oklch'` | Output color format. One of `'rgb' \| 'hsl' \| 'oklch'`. `'okhsl'` and `'okhst'` throw — use `tasty()` for those. |
|
|
126
126
|
| `modes` | `{ dark: true, highContrast: false }` (or global config) | Which scheme variants to include. |
|
|
127
127
|
|
|
128
128
|
### `theme.tasty(options?)`
|
|
@@ -141,10 +141,12 @@ theme.tasty()
|
|
|
141
141
|
|
|
142
142
|
| Option | Default | Description |
|
|
143
143
|
|---|---|---|
|
|
144
|
-
| `format` | `'okhsl'` | Output color format. |
|
|
144
|
+
| `format` | `'okhsl'` | Output color format. `'okhsl'` and `'okhst'` are supported here (Tasty-only spaces). |
|
|
145
145
|
| `modes` | global config | Which scheme variants to include. |
|
|
146
146
|
| `states.dark` | `'@dark'` (or global config) | State alias for dark mode tokens. |
|
|
147
147
|
| `states.highContrast` | `'@high-contrast'` (or global config) | State alias for high-contrast tokens. |
|
|
148
|
+
| `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. |
|
|
149
|
+
| `name` | `'theme'` | Base name for the theme-level hue var (`#theme-hue` / `--theme-hue`). Palette export auto-derives this from the theme name. |
|
|
148
150
|
| `prefix` | (palette only) | See [Palette](#palette). |
|
|
149
151
|
|
|
150
152
|
When both `dark` and `highContrast` modes are enabled, dark high-contrast variants are emitted under the combined key `<dark> & <highContrast>` (e.g. `'@dark & @high-contrast'`).
|
|
@@ -156,8 +158,8 @@ Per-color JSON map.
|
|
|
156
158
|
```ts
|
|
157
159
|
theme.json()
|
|
158
160
|
// → {
|
|
159
|
-
// surface: { light: '
|
|
160
|
-
// text: { light: '
|
|
161
|
+
// surface: { light: 'oklch(...)', dark: 'oklch(...)' },
|
|
162
|
+
// text: { light: 'oklch(...)', dark: 'oklch(...)' },
|
|
161
163
|
// }
|
|
162
164
|
```
|
|
163
165
|
|
|
@@ -181,11 +183,137 @@ theme.css();
|
|
|
181
183
|
|
|
182
184
|
| Option | Default | Description |
|
|
183
185
|
|---|---|---|
|
|
184
|
-
| `format` | `'rgb'` | Output color format. |
|
|
186
|
+
| `format` | `'rgb'` | Output color format. `'okhsl'` and `'okhst'` throw — use `tasty()` for those. |
|
|
185
187
|
| `suffix` | `'-color'` | Suffix appended to each CSS property name. Pass `''` for bare property names. |
|
|
188
|
+
| `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). |
|
|
189
|
+
| `name` | `'theme'` | Base name for the theme-level hue var (`--theme-hue`). Palette export auto-derives this from the theme name. |
|
|
186
190
|
|
|
187
191
|
`GlazeCssResult` always contains all four keys (`light`, `dark`, `lightContrast`, `darkContrast`); empty if no colors are defined for that variant.
|
|
188
192
|
|
|
193
|
+
### `theme.dtcg(options?)`
|
|
194
|
+
|
|
195
|
+
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
|
+
|
|
197
|
+
```ts
|
|
198
|
+
theme.dtcg()
|
|
199
|
+
// → {
|
|
200
|
+
// light: {
|
|
201
|
+
// surface: {
|
|
202
|
+
// $type: 'color',
|
|
203
|
+
// $value: { colorSpace: 'srgb', components: [0.96, 0.94, 0.98], hex: '#f5f0fa' },
|
|
204
|
+
// },
|
|
205
|
+
// },
|
|
206
|
+
// dark: {
|
|
207
|
+
// surface: {
|
|
208
|
+
// $type: 'color',
|
|
209
|
+
// $value: { colorSpace: 'srgb', components: [0.16, 0.14, 0.2], hex: '#292333' },
|
|
210
|
+
// },
|
|
211
|
+
// },
|
|
212
|
+
// }
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
Write each document to its own `.tokens.json` file — one file per scheme is the most tool-compatible convention (one per Style Dictionary theme / Tokens Studio set / Figma variable mode).
|
|
216
|
+
|
|
217
|
+
`GlazeDtcgOptions`:
|
|
218
|
+
|
|
219
|
+
| Option | Default | Description |
|
|
220
|
+
|---|---|---|
|
|
221
|
+
| `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. |
|
|
222
|
+
| `modes` | global config | Which scheme variants to include. `light` is always present. |
|
|
223
|
+
|
|
224
|
+
`alpha` is included on `$value` only when the color's opacity is below 1. `$type` is always `'color'`.
|
|
225
|
+
|
|
226
|
+
### `theme.dtcgResolver(options?)`
|
|
227
|
+
|
|
228
|
+
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
|
+
|
|
230
|
+
```ts
|
|
231
|
+
theme.dtcgResolver({ modes: { highContrast: true } })
|
|
232
|
+
// → {
|
|
233
|
+
// version: '2025.10',
|
|
234
|
+
// sets: {
|
|
235
|
+
// base: {
|
|
236
|
+
// sources: [
|
|
237
|
+
// {
|
|
238
|
+
// surface: {
|
|
239
|
+
// $type: 'color',
|
|
240
|
+
// $value: { colorSpace: 'srgb', components: [0.96, 0.94, 0.98], hex: '#f5f0fa' },
|
|
241
|
+
// },
|
|
242
|
+
// },
|
|
243
|
+
// ],
|
|
244
|
+
// },
|
|
245
|
+
// },
|
|
246
|
+
// modifiers: {
|
|
247
|
+
// scheme: {
|
|
248
|
+
// default: 'light',
|
|
249
|
+
// contexts: {
|
|
250
|
+
// light: [],
|
|
251
|
+
// dark: [
|
|
252
|
+
// {
|
|
253
|
+
// surface: {
|
|
254
|
+
// $type: 'color',
|
|
255
|
+
// $value: { colorSpace: 'srgb', components: [0.16, 0.14, 0.2], hex: '#292333' },
|
|
256
|
+
// },
|
|
257
|
+
// },
|
|
258
|
+
// ],
|
|
259
|
+
// lightContrast: [ /* … */ ],
|
|
260
|
+
// darkContrast: [ /* … */ ],
|
|
261
|
+
// },
|
|
262
|
+
// },
|
|
263
|
+
// },
|
|
264
|
+
// resolutionOrder: [
|
|
265
|
+
// { $ref: '#/sets/base' },
|
|
266
|
+
// { $ref: '#/modifiers/scheme' },
|
|
267
|
+
// ],
|
|
268
|
+
// }
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
**Why one modifier with four contexts.** Glaze resolves `darkContrast` independently — it is not `dark` + `lightContrast` layered. The resolver model composes modifiers additively (last in `resolutionOrder` wins on conflict), so two independent modifiers (`scheme` × `contrast`) would produce wrong values for the dark + high-contrast permutation. One `scheme` modifier with a context per variant keeps every resolved value exact. Choose `dtcgResolver()` when you want single-file theming and feed it to a resolver tool; choose `dtcg()` for maximum per-file tool compatibility.
|
|
272
|
+
|
|
273
|
+
`GlazeDtcgResolverOptions` (extends `GlazeDtcgOptions`, so `modes` and `colorSpace` pass through):
|
|
274
|
+
|
|
275
|
+
| Option | Default | Description |
|
|
276
|
+
|---|---|---|
|
|
277
|
+
| `colorSpace` | `'srgb'` | Same as `dtcg()` — flows through to every source and context. |
|
|
278
|
+
| `modes` | global config | Which scheme variants to emit as contexts. `light` is always present (the default); absent variants are omitted. |
|
|
279
|
+
| `setName` | `'base'` | Name of the single set holding the default (light) token tree. |
|
|
280
|
+
| `modifierName` | `'scheme'` | Name of the modifier describing the scheme axis. |
|
|
281
|
+
| `contextNames` | identity | Override the four context names (`light` / `dark` / `lightContrast` / `darkContrast`) — e.g. `{ dark: 'night' }`. |
|
|
282
|
+
| `version` | `'2025.10'` | Resolver document version. |
|
|
283
|
+
|
|
284
|
+
### `theme.tailwind(options?)`
|
|
285
|
+
|
|
286
|
+
A Tailwind CSS v4 `@theme` block (light baseline) plus dark / high-contrast overrides under configurable selectors. Returns a single ready-to-paste CSS string. The `--color-*` namespace auto-generates `bg-*` / `text-*` / `border-*` utilities.
|
|
287
|
+
|
|
288
|
+
```css
|
|
289
|
+
@theme {
|
|
290
|
+
--color-surface: oklch(0.96 0.01 280);
|
|
291
|
+
--color-text: oklch(0.3 0.05 280);
|
|
292
|
+
}
|
|
293
|
+
.dark {
|
|
294
|
+
--color-surface: oklch(0.16 0.01 280);
|
|
295
|
+
--color-text: oklch(0.85 0.05 280);
|
|
296
|
+
}
|
|
297
|
+
.high-contrast {
|
|
298
|
+
--color-surface: oklch(0.98 0.01 280);
|
|
299
|
+
--color-text: oklch(0.1 0.05 280);
|
|
300
|
+
}
|
|
301
|
+
.dark.high-contrast {
|
|
302
|
+
--color-surface: oklch(0.05 0.01 280);
|
|
303
|
+
--color-text: oklch(0.95 0.05 280);
|
|
304
|
+
}
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
`GlazeTailwindOptions`:
|
|
308
|
+
|
|
309
|
+
| Option | Default | Description |
|
|
310
|
+
|---|---|---|
|
|
311
|
+
| `format` | `'oklch'` | Output color format for the values. |
|
|
312
|
+
| `namespace` | `'color-'` | CSS custom property namespace, forming `--<namespace><name>` (e.g. `--color-surface`). Named `namespace` to avoid clashing with the palette theme-prefix option. |
|
|
313
|
+
| `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). |
|
|
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` | global config | Which scheme variants to include. The `@theme` block (light) is always emitted when colors exist. |
|
|
316
|
+
|
|
189
317
|
### `theme.export()`
|
|
190
318
|
|
|
191
319
|
```ts
|
|
@@ -219,6 +347,8 @@ type ColorDef = RegularColorDef | ShadowColorDef | MixColorDef;
|
|
|
219
347
|
| `mode` | `'auto' \| 'fixed' \| 'static'` | Adaptation mode. Default: `'auto'`. See [Adaptation modes](#adaptation-modes). |
|
|
220
348
|
| `flip` | `boolean` | Flip out-of-bounds results (relative `tone` overshoot / unmet `contrast`) to the opposite side instead of clamping. Default: the global `autoFlip` (`true`). See [`flip`](#flip). |
|
|
221
349
|
| `opacity` | `number` | Fixed alpha 0–1. Output includes alpha in the CSS value. Combining with `contrast` is not recommended (a `console.warn` is emitted). |
|
|
350
|
+
| `pastel` | `boolean` | Per-color override for the hue-independent "safe" chroma limit used in OKHSL↔sRGB conversions (luminance, contrast solving, output formatting). Falls through to the global / per-theme `pastel` config when omitted. Default: unset. See [Per-color `pastel`](#per-color-pastel). |
|
|
351
|
+
| `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). |
|
|
222
352
|
| `inherit` | `boolean` | Whether this color is inherited by child themes via `extend()`. Default: `true`. Set to `false` to make the color local to the current theme. |
|
|
223
353
|
|
|
224
354
|
#### Tone values
|
|
@@ -276,8 +406,15 @@ contrast: { wcag: 6 } // WCAG 6
|
|
|
276
406
|
contrast: { wcag: [4.5, 7] } // WCAG 4.5 normal / 7 high-contrast
|
|
277
407
|
contrast: { apca: 60 } // APCA Lc 60
|
|
278
408
|
contrast: { apca: [45, 60] } // APCA Lc 45 normal / 60 high-contrast
|
|
409
|
+
contrast: { apca: 'content' } // APCA preset -> Lc 60
|
|
410
|
+
contrast: { apca: ['content', 'body'] } // Lc 60 normal / 75 high-contrast
|
|
279
411
|
```
|
|
280
412
|
|
|
413
|
+
APCA preset keywords (Bronze Simple Mode conformance levels, role-independent):
|
|
414
|
+
`'preferred'` (Lc 90), `'body'` (75), `'content'` (60, ~AA), `'large'` (45, ~3:1),
|
|
415
|
+
`'non-text'` (30), `'min'` (15, point of invisibility). See
|
|
416
|
+
[`docs/okhst.md`](okhst.md) §APCA.
|
|
417
|
+
|
|
281
418
|
The floor is applied independently per scheme — if the `tone` already satisfies it the tone is kept, otherwise the solver searches in tone (contrast-uniform → a closed-form WCAG seed and fast convergence) until the target is met.
|
|
282
419
|
|
|
283
420
|
By default, the solver crosses to the opposite side of the base color when the requested tone direction cannot satisfy the floor. This is controlled per-color by [`flip`](#flip) (which defaults to the global `autoFlip`). Set `glaze.configure({ autoFlip: false })` — or `flip: false` on a single color — to keep strict directionality: unmet colors pin to that direction's 0 or 100 tone extreme instead of falling back to the original requested value.
|
|
@@ -299,6 +436,68 @@ theme.colors({
|
|
|
299
436
|
|
|
300
437
|
Relative hue is always relative to the **theme seed hue**, not to a base color.
|
|
301
438
|
|
|
439
|
+
#### Per-color `pastel`
|
|
440
|
+
|
|
441
|
+
`pastel: true` on a single color def overrides the global / per-theme `pastel` config for that color only. It toggles the hue-independent "safe" chroma limit used in every OKHSL↔sRGB conversion that touches this color: luminance calculations during contrast solving, gamut clamping during sRGB blend / mix edges, and output formatting. The effective flag is carried on the resolved variant (`ResolvedColorVariant.pastel`) so formatting matches the gamut mapping applied during resolution.
|
|
442
|
+
|
|
443
|
+
```ts
|
|
444
|
+
const theme = glaze(280, 80);
|
|
445
|
+
theme.colors({
|
|
446
|
+
plain: { tone: 50, saturation: 1 },
|
|
447
|
+
soft: { tone: 50, saturation: 1, pastel: true },
|
|
448
|
+
});
|
|
449
|
+
// theme.resolve().get('soft')!.light.pastel === true
|
|
450
|
+
// theme.css().light contains different rgb() triples for `--plain` and `--soft`
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
Omit the field to inherit the global / per-theme `pastel` config — useful for keeping the default behavior while opting a single accent into the pastel gamut.
|
|
454
|
+
|
|
455
|
+
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:
|
|
456
|
+
|
|
457
|
+
```ts
|
|
458
|
+
const parent = glaze(280, 80);
|
|
459
|
+
parent.colors({ soft: { tone: 50, saturation: 1, pastel: true } });
|
|
460
|
+
|
|
461
|
+
const child = parent.extend({
|
|
462
|
+
colors: { soft: { tone: 50, saturation: 1, pastel: false } },
|
|
463
|
+
});
|
|
464
|
+
// child.resolve().get('soft')!.light.pastel === false
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
> **Note:** Per-color `pastel` is also supported on `ShadowColorDef` and `MixColorDef` (see the tables above). For shadows the math itself happens in OKHSL space, so the flag mainly controls the gamut-mapped output formatting and any luminance verification for that variant.
|
|
468
|
+
>
|
|
469
|
+
> Standalone `glaze.color()` tokens accept the same `pastel` field on both the structured (`GlazeColorInput`) and value-shorthand (`GlazeColorOverrides`) forms, and it survives the `export()` / `glaze.colorFrom()` round-trip.
|
|
470
|
+
|
|
471
|
+
#### Roles
|
|
472
|
+
|
|
473
|
+
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.
|
|
474
|
+
|
|
475
|
+
| Role | Polarity | Use | Aliases (name inference) |
|
|
476
|
+
|---|---|---|---|
|
|
477
|
+
| `'text'` | fg | Text / icons / foreground content | `text`, `fg`, `foreground`, `content`, `ink`, `label`, `stroke` |
|
|
478
|
+
| `'border'` | fg | Non-text spot elements (borders, dividers, outlines) | `border`, `divider`, `outline`, `separator`, `hairline`, `rule` |
|
|
479
|
+
| `'surface'` | bg | Backgrounds / fills | `surface`, `bg`, `background`, `fill`, `canvas`, `paper`, `layer` |
|
|
480
|
+
|
|
481
|
+
Resolution chain (per color):
|
|
482
|
+
|
|
483
|
+
1. Explicit `role` (normalized from an alias) wins.
|
|
484
|
+
2. Else, when `inferRole` is enabled (default), infer from the color name — the **last** recognized token wins (`button-text` → `text`, `input-bg` → `surface`, `card-outline` → `border`).
|
|
485
|
+
3. Else, the opposite of the base's role (a `surface` base ⇒ this is `text`).
|
|
486
|
+
4. Else, `'text'` (foreground) — i.e. the base is treated as the background.
|
|
487
|
+
|
|
488
|
+
```ts
|
|
489
|
+
const theme = glaze(280, 60);
|
|
490
|
+
theme.colors({
|
|
491
|
+
surface: { tone: 90 },
|
|
492
|
+
text: { base: 'surface', contrast: { apca: 'content' } }, // inferred text
|
|
493
|
+
border: { base: 'surface', tone: '-10' }, // inferred border
|
|
494
|
+
});
|
|
495
|
+
// role fixes APCA polarity; set `pastel: true` explicitly if a border
|
|
496
|
+
// needs the hue-independent safe chroma limit.
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
Disable name inference with `glaze.configure({ inferRole: false })` (the base-opposite and foreground-default fallbacks still apply).
|
|
500
|
+
|
|
302
501
|
### `ShadowColorDef`
|
|
303
502
|
|
|
304
503
|
| Field | Type | Description |
|
|
@@ -308,6 +507,7 @@ Relative hue is always relative to the **theme seed hue**, not to a base color.
|
|
|
308
507
|
| `fg` | `string` | Optional foreground color name for tinting and intensity modulation. Must reference a non-shadow color. Omit for an achromatic shadow at full user-specified intensity. |
|
|
309
508
|
| `intensity` | `HCPair<number>` | Shadow intensity, 0–100. Supports HC pairs. |
|
|
310
509
|
| `tuning` | `ShadowTuning` | Per-color tuning overrides. Merged field-by-field with the global `shadowTuning`. |
|
|
510
|
+
| `pastel` | `boolean` | Per-color `pastel` override. See [Per-color `pastel`](#per-color-pastel). |
|
|
311
511
|
| `inherit` | `boolean` | Inheritance flag, default `true`. |
|
|
312
512
|
|
|
313
513
|
See [Shadows](#shadows) below for the algorithm and tuning details.
|
|
@@ -323,6 +523,8 @@ See [Shadows](#shadows) below for the algorithm and tuning details.
|
|
|
323
523
|
| `blend` | `'opaque' \| 'transparent'` | Default `'opaque'`. |
|
|
324
524
|
| `space` | `'okhsl' \| 'srgb'` | Interpolation space for opaque blending. Default `'okhsl'`. Ignored for `'transparent'` (always composites in linear sRGB). |
|
|
325
525
|
| `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). |
|
|
526
|
+
| `pastel` | `boolean` | Per-color `pastel` override. See [Per-color `pastel`](#per-color-pastel). |
|
|
527
|
+
| `role` | `RoleInput` | Semantic role of the mixed result against `base`. Same semantics as `RegularColorDef.role` (see [Roles](#roles)). |
|
|
326
528
|
| `inherit` | `boolean` | Inheritance flag, default `true`. |
|
|
327
529
|
|
|
328
530
|
See [Mix colors](#mix-colors) below.
|
|
@@ -378,6 +580,8 @@ glaze.color(color: GlazeFromInput | GlazeColorInput | GlazeColorValue, config?:
|
|
|
378
580
|
| `opacity` | `number` | Fixed alpha 0–1. |
|
|
379
581
|
| `base` | `GlazeColorToken \| GlazeColorValue` | Optional dependency. See [Pairing colors](#pairing-colors). |
|
|
380
582
|
| `contrast` | `HCPair<ContrastSpec>` | Contrast floor against `base` (WCAG or APCA). Without `base`, anchored to the literal seed. |
|
|
583
|
+
| `pastel` | `boolean` | Per-color `pastel` override. Falls through to the global / per-theme `pastel` config when omitted. See [Per-color `pastel`](#per-color-pastel). |
|
|
584
|
+
| `role` | `RoleInput` | Semantic role against `base` / the seed (see [Roles](#roles)). Fixes APCA polarity. |
|
|
381
585
|
| `name` | `string` | Debug label for warnings; doesn't change output keys. Reserved names (`'value'`, `'seed'`, `'externalBase'`) are rejected. |
|
|
382
586
|
|
|
383
587
|
`GlazeFromInput` (from form) is `{ from: GlazeColorValue, ...colorOverrides }`:
|
|
@@ -394,6 +598,8 @@ glaze.color(color: GlazeFromInput | GlazeColorInput | GlazeColorValue, config?:
|
|
|
394
598
|
| `contrast` | Contrast floor (WCAG or APCA). Without `base`, anchored to the literal seed; with `base`, solved per scheme. |
|
|
395
599
|
| `base` | `GlazeColorToken` or raw `GlazeColorValue`. See [Pairing colors](#pairing-colors). |
|
|
396
600
|
| `opacity` | Fixed alpha 0–1. Combining with `contrast` is not recommended — `console.warn` is emitted. |
|
|
601
|
+
| `pastel` | Per-color `pastel` override. Falls through to the global / per-theme `pastel` config when omitted. See [Per-color `pastel`](#per-color-pastel). |
|
|
602
|
+
| `role` | Semantic role against `base` / the seed (see [Roles](#roles)). Fixes APCA polarity. |
|
|
397
603
|
| `name` | Debug label only — surfaces in warnings/errors. Does not change output keys. |
|
|
398
604
|
|
|
399
605
|
Named CSS colors (`'red'`, `'blueviolet'`) are not supported.
|
|
@@ -434,9 +640,12 @@ A `GlazeColorToken` exposes:
|
|
|
434
640
|
|---|---|
|
|
435
641
|
| `token.resolve()` | Resolve as a `ResolvedColor` (light/dark/lightContrast/darkContrast variants). |
|
|
436
642
|
| `token.token(options?)` | Flat token map (no color-name key). Options: `format`, `modes`, `states`. |
|
|
437
|
-
| `token.tasty(options?)` | Tasty state map (no color-name key). Same options as `token.token`. |
|
|
643
|
+
| `token.tasty(options?)` | [Tasty](https://tasty.style) state map (no color-name key). Same options as `token.token`. |
|
|
438
644
|
| `token.json(options?)` | JSON map (no color-name key). Options: `format`, `modes`. |
|
|
439
645
|
| `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`). |
|
|
646
|
+
| `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`. |
|
|
647
|
+
| `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`. |
|
|
648
|
+
| `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`. |
|
|
440
649
|
| `token.export()` | JSON-safe snapshot — pass to `glaze.colorFrom(...)` to rehydrate. |
|
|
441
650
|
|
|
442
651
|
### Per-instance config override
|
|
@@ -885,6 +1094,56 @@ const stylesheet = `
|
|
|
885
1094
|
`palette.css()` accepts the same `GlazeCssOptions` as `theme.css()` plus `GlazePaletteExportOptions`.
|
|
886
1095
|
It does not accept `modes`; all four result fields are always returned.
|
|
887
1096
|
|
|
1097
|
+
### `palette.dtcg()`
|
|
1098
|
+
|
|
1099
|
+
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).
|
|
1100
|
+
|
|
1101
|
+
```ts
|
|
1102
|
+
palette.dtcg()
|
|
1103
|
+
// → {
|
|
1104
|
+
// light: {
|
|
1105
|
+
// 'primary-surface': { $type: 'color', $value: { ... } },
|
|
1106
|
+
// 'surface': { $type: 'color', $value: { ... } }, // unprefixed alias
|
|
1107
|
+
// 'danger-surface': { $type: 'color', $value: { ... } },
|
|
1108
|
+
// },
|
|
1109
|
+
// dark: { ... },
|
|
1110
|
+
// }
|
|
1111
|
+
```
|
|
1112
|
+
|
|
1113
|
+
Accepts `GlazeDtcgOptions` plus `GlazePaletteExportOptions`.
|
|
1114
|
+
|
|
1115
|
+
### `palette.dtcgResolver()`
|
|
1116
|
+
|
|
1117
|
+
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.
|
|
1118
|
+
|
|
1119
|
+
```ts
|
|
1120
|
+
palette.dtcgResolver()
|
|
1121
|
+
// → {
|
|
1122
|
+
// version: '2025.10',
|
|
1123
|
+
// sets: { base: { sources: [ { 'primary-surface': {…}, 'surface': {…}, 'danger-surface': {…} } ] } },
|
|
1124
|
+
// modifiers: { scheme: { default: 'light', contexts: { light: [], dark: [ {…} ] } } },
|
|
1125
|
+
// resolutionOrder: [ { $ref: '#/sets/base' }, { $ref: '#/modifiers/scheme' } ],
|
|
1126
|
+
// }
|
|
1127
|
+
```
|
|
1128
|
+
|
|
1129
|
+
Accepts `GlazeDtcgResolverOptions` plus `GlazePaletteExportOptions`.
|
|
1130
|
+
|
|
1131
|
+
### `palette.tailwind()`
|
|
1132
|
+
|
|
1133
|
+
Tailwind export for a palette. All themes are merged into a single `@theme` block (plus dark / high-contrast overrides), so each color is reachable as a Tailwind utility. Prefix defaults to `true`.
|
|
1134
|
+
|
|
1135
|
+
```ts
|
|
1136
|
+
const css = palette.tailwind();
|
|
1137
|
+
// @theme {
|
|
1138
|
+
// --color-primary-surface: oklch(...);
|
|
1139
|
+
// --color-surface: oklch(...); /* unprefixed alias */
|
|
1140
|
+
// --color-danger-surface: oklch(...);
|
|
1141
|
+
// }
|
|
1142
|
+
// .dark { ... }
|
|
1143
|
+
```
|
|
1144
|
+
|
|
1145
|
+
Accepts `GlazeTailwindOptions` plus `GlazePaletteExportOptions`. The palette `prefix` option (theme prefixing) is separate from `GlazeTailwindOptions.namespace` (the `--color-*` CSS namespace).
|
|
1146
|
+
|
|
888
1147
|
---
|
|
889
1148
|
|
|
890
1149
|
## Output formats
|
|
@@ -893,21 +1152,40 @@ Control the color format with the `format` option on any export method:
|
|
|
893
1152
|
|
|
894
1153
|
| Format | Output (alpha = 1) | Output (alpha < 1) | Notes |
|
|
895
1154
|
|---|---|---|---|
|
|
896
|
-
| `'okhsl'` (default for
|
|
897
|
-
| `'
|
|
1155
|
+
| `'okhsl'` (default for `tasty()`) | `okhsl(H S% L%)` | `okhsl(H S% L% / A)` | Glaze's native format, not a CSS function. **Tasty-only** (`tasty()`, `token()`, `.tasty()`). |
|
|
1156
|
+
| `'okhst'` | `okhst(H S% T%)` | `okhst(H S% T% / A)` | OKHST tone axis. **Tasty-only** — same restriction as `okhsl`. |
|
|
1157
|
+
| `'oklch'` (default for `tokens()` / `json()`) | `oklch(L C H)` | `oklch(L C H / A)` | OKLab-based LCH. Native CSS. Required for `splitHue`. |
|
|
1158
|
+
| `'rgb'` (default for `css()`) | `rgb(R G B)` | `rgb(R G B / A)` | Rounded integers, modern space syntax. |
|
|
898
1159
|
| `'hsl'` | `hsl(H S% L%)` | `hsl(H S% L% / A)` | Modern space syntax. |
|
|
899
|
-
| `'oklch'` | `oklch(L C H)` | `oklch(L C H / A)` | OKLab-based LCH. |
|
|
900
1160
|
|
|
901
1161
|
```ts
|
|
902
|
-
theme.tokens(); // '
|
|
1162
|
+
theme.tokens(); // 'oklch(0.965 0.0123 280)' (default)
|
|
903
1163
|
theme.tokens({ format: 'rgb' }); // 'rgb(244 240 250)'
|
|
904
|
-
theme.
|
|
905
|
-
theme.
|
|
1164
|
+
theme.tasty(); // 'okhsl(280 60% 97%)' (default)
|
|
1165
|
+
theme.tasty({ format: 'okhst' }); // 'okhst(280 60% 97%)'
|
|
906
1166
|
```
|
|
907
1167
|
|
|
908
1168
|
All numeric output strips trailing zeros for cleaner CSS (e.g. `95` not `95.0`).
|
|
909
1169
|
|
|
910
|
-
The `format` option works on
|
|
1170
|
+
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.
|
|
1171
|
+
|
|
1172
|
+
### Hue channel splitting (`splitHue`)
|
|
1173
|
+
|
|
1174
|
+
On `theme.css()`, `theme.tasty()`, `palette.css()`, `palette.tasty()`, and standalone `color.css()` with `format: 'oklch'`, set `splitHue: true` to emit hue as its own custom property so consumers can re-skin at runtime:
|
|
1175
|
+
|
|
1176
|
+
```css
|
|
1177
|
+
/* theme.css({ format: 'oklch', splitHue: true, name: 'brand' }) */
|
|
1178
|
+
--brand-hue: 240;
|
|
1179
|
+
--accent-hue: calc(var(--brand-hue) + 20);
|
|
1180
|
+
--surface-color: oklch(0.52 0.06 var(--brand-hue));
|
|
1181
|
+
--accent-color: oklch(0.62 0.03 var(--accent-hue));
|
|
1182
|
+
```
|
|
1183
|
+
|
|
1184
|
+
**Requirements:** every exported color must be pastel (`pastel: true` globally 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.
|
|
1185
|
+
|
|
1186
|
+
**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).
|
|
1187
|
+
|
|
1188
|
+
`theme.dtcg()` / `theme.dtcgResolver()` / `palette.dtcg()` / `palette.dtcgResolver()` ignore `format` — DTCG emits structured `$value` objects, not CSS strings. Use the `colorSpace` option (`'srgb'` or `'oklch'`) to pick the color representation instead.
|
|
911
1189
|
|
|
912
1190
|
---
|
|
913
1191
|
|
|
@@ -1027,6 +1305,8 @@ A `ToneWindow` is `[lo, hi]` (OKHSL-lightness endpoints, reference eps — the c
|
|
|
1027
1305
|
| `modes.highContrast` | `false` | Include HC variants. |
|
|
1028
1306
|
| `shadowTuning` | `undefined` | Default tuning for all shadow colors. Per-color tuning merges field-by-field. |
|
|
1029
1307
|
| `autoFlip` | `true` | Default for each color's `flip`. When solving `contrast` (or applying a relative `tone` that overshoots `[0, 100]`), allow crossing to the opposite side instead of clamping. With `false`, only the requested direction is considered; unmet contrasts pin the tone to that direction's extreme (and emit a warning) and overshooting offsets clamp to the boundary. Override per color via [`flip`](#flip). |
|
|
1308
|
+
| `pastel` | `false` | Hue-independent "safe" chroma limit across all colors so scaling saturation never exceeds the sRGB boundary at any hue for the given lightness. Override per color via [`pastel`](#per-color-pastel). |
|
|
1309
|
+
| `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). |
|
|
1030
1310
|
|
|
1031
1311
|
| Method | Description |
|
|
1032
1312
|
|---|---|
|