@tenphi/glaze 0.0.0-snapshot.d38eee5 → 0.0.0-snapshot.d79cac8

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/dist/index.d.cts CHANGED
@@ -16,6 +16,16 @@ type MinContrast$1 = number | ContrastPreset;
16
16
  * | `'min'` | 15 | Dividers/decorative; APCA "point of invisibility" |
17
17
  */
18
18
  type ApcaPreset = 'preferred' | 'body' | 'content' | 'large' | 'non-text' | 'min';
19
+ declare const APCA_PRESETS: Record<ApcaPreset, number>;
20
+ /**
21
+ * APCA-W3 "Enhanced Level" delta added to a bare APCA target in high-contrast
22
+ * mode when no explicit HC value is provided (analogous to WCAG AAA over AA).
23
+ * Only applied when neither the outer `contrast` pair nor the inner `apca`
24
+ * pair carries an explicit HC entry.
25
+ */
26
+ declare const APCA_HC_ENHANCEMENT = 15;
27
+ /** Upper bound for an APCA Lc target after HC enhancement. */
28
+ declare const APCA_MAX_LC = 106;
19
29
  /**
20
30
  * Resolve an APCA target — a raw Lc number (kept as-is) or an `ApcaPreset`
21
31
  * keyword mapped to its Lc value. The magnitude is forced non-negative.
@@ -40,8 +50,36 @@ declare function resolveMinContrast(value: MinContrast$1): number;
40
50
  * given mode into `{ metric, target }`. Handles the inner metric HC pair and
41
51
  * preset resolution. `polarity` is passed through to the result for the APCA
42
52
  * branch (it controls argument order in the solver); WCAG ignores it.
53
+ *
54
+ * `outerExplicitHC` indicates whether the caller selected this `spec` from an
55
+ * explicit high-contrast entry of the outer `contrast` pair. Together with the
56
+ * inner metric pair, it decides whether the HC auto-enhancement fires:
57
+ * - APCA: +15 Lc "Enhanced Level" boost when neither level is explicit.
58
+ * - WCAG: AA → AAA / AA-large → AAA-large promotion (SC 1.4.3 → 1.4.6) when
59
+ * neither level is explicit. AAA-family presets and bare numbers are left
60
+ * unchanged (AAA is the top WCAG tier).
61
+ * Defaults to `false` (correct for direct callers, which pass a single
62
+ * selected spec rather than an outer pair).
43
63
  */
44
- declare function resolveContrastForMode(spec: ContrastSpec, isHighContrast: boolean, polarity?: 'fg' | 'bg'): ResolvedContrast;
64
+ declare function resolveContrastForMode(spec: ContrastSpec, isHighContrast: boolean, polarity?: 'fg' | 'bg', outerExplicitHC?: boolean): ResolvedContrast;
65
+ /**
66
+ * Resolve a `contrast` spec — including its outer `[normal, highContrast]`
67
+ * pair — at a manual contrast `level` (0–100), by resolving both ends and
68
+ * interpolating the numeric target.
69
+ *
70
+ * Level 0 / 100 delegate verbatim to {@link resolveContrastForMode}, so the
71
+ * endpoints are bit-identical to classic normal / high-contrast resolution.
72
+ * Resolving each end fully first also means every rule that governs the
73
+ * escalation still applies at both ends for free: preset mapping, the
74
+ * `APCA_MAX_LC` clamp, and the explicit-HC suppression that cancels the
75
+ * `AA → AAA` promotion and the `+15 Lc` enhancement.
76
+ *
77
+ * Throws when the two ends measure in different metrics — a WCAG ratio and an
78
+ * APCA Lc live on different scales, so there is no target between them. Such a
79
+ * pair is rejected at authoring time by `validateColorDefs`; this is the guard
80
+ * for direct callers.
81
+ */
82
+ declare function resolveContrastForLevel(spec: HCPair<ContrastSpec>, level: number, polarity?: 'fg' | 'bg'): ResolvedContrast;
45
83
  /**
46
84
  * APCA lightness contrast (Lc), signed: positive for dark text on light bg,
47
85
  * negative for light text on dark bg. Inputs are screen luminances (0–1).
@@ -64,10 +102,31 @@ interface FindToneForContrastOptions {
64
102
  epsilon?: number;
65
103
  /** Maximum binary-search iterations per branch. Default: 18. */
66
104
  maxIterations?: number;
67
- /** Preferred search direction before auto-flip is considered. */
105
+ /**
106
+ * Preferred search direction before auto-flip is considered. Default: the
107
+ * side whose bound scores higher (the only open side when `preferredTone`
108
+ * sits on a bound); a tie within float noise starts `'darker'`.
109
+ */
68
110
  initialDirection?: 'lighter' | 'darker';
69
- /** Auto-flip tone direction when contrast can't be met. Default: false. */
111
+ /**
112
+ * Also search the opposite direction. Default: false. When both directions
113
+ * meet the target, the result nearer `preferredTone` wins; a gap within
114
+ * twice the search resolution is a tie and keeps `initialDirection`.
115
+ */
70
116
  flip?: boolean;
117
+ /**
118
+ * When `flip` is on and *both* directions meet the target, keep
119
+ * `initialDirection` instead of taking whichever result lands nearer the
120
+ * preferred tone. Default: false (nearest wins).
121
+ *
122
+ * The nearest-wins tie-break is unstable along a ramp of targets: which side
123
+ * is nearer shifts as the target grows, so a color can swap sides between two
124
+ * adjacent targets. Set this when a caller needs the side to be a property of
125
+ * the color rather than of the target — the manual contrast level does. The
126
+ * flip itself is unaffected: an initial direction that cannot reach the
127
+ * target still falls back to the opposite side.
128
+ */
129
+ preferInitial?: boolean;
71
130
  /** Use the hue-independent "safe" chroma boundary. Default: false. */
72
131
  pastel?: boolean;
73
132
  }
@@ -78,26 +137,42 @@ interface FindToneForContrastResult {
78
137
  contrast: number;
79
138
  /** Whether the target was reached. */
80
139
  met: boolean;
81
- /** Which branch was selected. */
140
+ /**
141
+ * The side the result was found on. The two sides meet at `preferredTone`,
142
+ * so a met result sits on that side of the base too. `'preferred'` whenever
143
+ * a met result is `preferredTone` itself: it already cleared the
144
+ * overshooting search target, or it clears the bare target and no search
145
+ * could improve on it — no searched side met the floor, or the starting
146
+ * side's only position was `preferredTone` (it sits on that side's bound).
147
+ * Also `'preferred'` when `toneRange` leaves no room to search, met or not.
148
+ * An unmet result otherwise reports the side whose extreme it pinned to.
149
+ */
82
150
  branch: 'lighter' | 'darker' | 'preferred';
83
151
  /** Whether the result auto-flipped to the opposite direction. */
84
152
  flipped?: boolean;
85
153
  }
86
154
  /**
87
155
  * Find the tone that satisfies a contrast floor against a base color,
88
- * staying as close to `preferredTone` as possible.
156
+ * staying as close to `preferredTone` as possible. A searched result keeps the
157
+ * overshoot (1% for WCAG, 0.5 Lc for APCA). When no searched side reaches the
158
+ * floor, the result pins to the initial direction's extreme, unmet — unless
159
+ * `preferredTone` itself clears the bare target, which is then kept as met
160
+ * (`branch: 'preferred'`, the same label a met result gets when the starting
161
+ * side's only position is `preferredTone`).
89
162
  */
90
163
  declare function findToneForContrast(options: FindToneForContrastOptions): FindToneForContrastResult;
91
164
  interface FindValueForMixContrastOptions {
92
165
  /** Preferred mix parameter (0–1). */
93
166
  preferredValue: number;
94
- /** Base color as linear sRGB. */
167
+ /** Base color as linear sRGB — the color the mix is measured against. */
95
168
  baseLinearRgb: LinearRgb;
96
- /** Target color as linear sRGB. */
97
- targetLinearRgb: LinearRgb;
98
169
  /** Resolved contrast floor (metric + target). */
99
170
  contrast: ResolvedContrast;
100
- /** Compute the luminance of the mixed color at parameter t. */
171
+ /**
172
+ * Luminance of the mixed color at parameter t, in the metric's basis
173
+ * ({@link metricLuminance}). This closure is the only view the solver has of
174
+ * the mix's target color, blend, and color space.
175
+ */
101
176
  luminanceAtValue: (t: number) => number;
102
177
  /** Convergence threshold. Default: 1e-4. */
103
178
  epsilon?: number;
@@ -115,6 +190,9 @@ interface FindValueForMixContrastResult {
115
190
  /**
116
191
  * Find the mix parameter (ratio or opacity) that satisfies a contrast floor
117
192
  * against a base color, staying as close to `preferredValue` as possible.
193
+ * Misses pin to the initial direction's end like the tone solver, and a
194
+ * `preferredValue` that clears the bare target is kept as met when no searched
195
+ * side reaches the floor (a value of 1 has no room toward the target).
118
196
  */
119
197
  declare function findValueForMixContrast(options: FindValueForMixContrastOptions): FindValueForMixContrastResult;
120
198
  //#endregion
@@ -172,7 +250,14 @@ type ExtremeValue = 'max' | 'min';
172
250
  */
173
251
  type ToneValue = number | RelativeValue | ExtremeValue;
174
252
  /** Color format for output. */
175
- type GlazeColorFormat = 'okhsl' | 'rgb' | 'hsl' | 'oklch';
253
+ type GlazeColorFormat = 'okhsl' | 'okhst' | 'rgb' | 'hsl' | 'oklch';
254
+ /**
255
+ * Output format for an HDR glow value (`glaze.formatHdr()`). Only
256
+ * `'srgb-linear'` — `color(srgb-linear R G B)` with channels that may exceed
257
+ * 1 — since OKLCH lightness clamps at parse time and `rgb()` / `hsl()` cannot
258
+ * go past white.
259
+ */
260
+ type GlazeHdrFormat = 'srgb-linear';
176
261
  /**
177
262
  * Controls which scheme variants are generated in the export.
178
263
  * Light is always included (it's the default).
@@ -180,8 +265,29 @@ type GlazeColorFormat = 'okhsl' | 'rgb' | 'hsl' | 'oklch';
180
265
  interface GlazeOutputModes {
181
266
  /** Include dark scheme variants. Default: true. */
182
267
  dark?: boolean;
183
- /** Include high-contrast variants (both light-HC and dark-HC). Default: false. */
268
+ /**
269
+ * Include high-contrast variants (both light-HC and dark-HC). Default: false.
270
+ *
271
+ * Independent of `contrastLevel` — the level positions the normal variants
272
+ * while these stay the true high-contrast resolution, so the two compose. The
273
+ * exception is a global level of 100, where the normal variants already *are*
274
+ * the high-contrast ones: the tier would duplicate them and is dropped.
275
+ * @see GlazeConfig.contrastLevel
276
+ */
184
277
  highContrast?: boolean;
278
+ /**
279
+ * Include the HDR tier of glows past SDR white (`variant.hdr`, set by
280
+ * `GlowTuning.headroom`). Default: false — every exporter's output stays
281
+ * byte-identical to SDR even when a headroom is set.
282
+ *
283
+ * When on, each exporter adds the HDR values next to the SDR ones it
284
+ * already emits (only for the scheme variants the other modes include),
285
+ * always written as `color(srgb-linear R G B)`: Tasty `hdr` state keys,
286
+ * `json()` `lightHdr` / `darkHdr` / … keys, `tokens()` `lightHdr` / … maps,
287
+ * Tailwind blocks under `hdrSelector`, DTCG `$extensions`. `css()` reads
288
+ * its own `hdr` option, which falls back to this.
289
+ */
290
+ hdr?: boolean;
185
291
  }
186
292
  /** Hex color string for DX hints. Runtime validation in `parseHex()`. */
187
293
  type HexColor = `#${string}`;
@@ -214,13 +320,43 @@ interface OklchColor {
214
320
  }
215
321
  interface RegularColorDef {
216
322
  /**
217
- * Tone value (0–100, contrast-uniform — see `docs/okhst.md`).
323
+ * Seed this color from a literal color instead of from the theme.
324
+ *
325
+ * Accepts the same values as `glaze.color()` — hex, `rgb()`, `hsl()`,
326
+ * `okhsl()`, `okhst()`, `oklch()`, or a value object. It supplies three
327
+ * things at once: `hue`, `tone`, and — unlike every other color in a theme —
328
+ * an **absolute saturation** rather than a factor of the seed. That last
329
+ * part is the point: a color can now be more saturated than its theme, so
330
+ * honoring a brand color no longer means re-seeding the whole theme to reach
331
+ * it.
332
+ *
333
+ * The **light, normal-contrast** variant reproduces the value exactly
334
+ * (a local `lightTone: false`, matching the value-shorthand form of
335
+ * `glaze.color()`). Dark and high-contrast variants adapt as usual — they
336
+ * are where readability outranks fidelity, and a color pinned across every
337
+ * scheme would defeat both. A `contrast` floor still applies everywhere and
338
+ * still only moves the color when the authored value misses it.
339
+ *
340
+ * Sibling fields override what the value supplied, so
341
+ * `{ from: '#2F5BFF', hue: 300 }` keeps its saturation and tone but rotates
342
+ * the hue.
343
+ */
344
+ from?: GlazeColorValue;
345
+ /**
346
+ * Tone value (0–100, contrast-uniform — see `docs/okhst.mdx`).
218
347
  * - Number: absolute tone.
219
348
  * - String ('+N' / '-N'): relative to base color's tone (requires `base`).
220
349
  * - `'max'` / `'min'`: force to the scheme's tone extreme (no base needed).
350
+ *
351
+ * Defaults to the tone of `from` when that is set.
221
352
  */
222
353
  tone?: HCPair<ToneValue>;
223
- /** Saturation factor applied to the seed saturation (0–1, default: 1). */
354
+ /**
355
+ * Saturation factor applied to the seed saturation (0–1, default: 1).
356
+ *
357
+ * With `from`, an explicit value here is still a factor of the seed and
358
+ * overrides the absolute saturation the color carried.
359
+ */
224
360
  saturation?: number;
225
361
  /**
226
362
  * Hue override for this color.
@@ -228,6 +364,26 @@ interface RegularColorDef {
228
364
  * - String ('+N' / '-N'): relative to the theme seed hue.
229
365
  */
230
366
  hue?: number | RelativeValue;
367
+ /**
368
+ * Dark-scheme hue override. Applies to the `dark` and `darkContrast`
369
+ * variants; falls back to `hue` when omitted.
370
+ * - Number: absolute hue (0–360).
371
+ * - String ('+N' / '-N'): relative to the theme's **dark** seed hue
372
+ * (`GlazeTheme.darkHue`, itself defaulting to the light seed hue).
373
+ *
374
+ * Ignored under `mode: 'static'`, which pins one value across all schemes.
375
+ */
376
+ darkHue?: number | RelativeValue;
377
+ /**
378
+ * Dark-scheme saturation factor (0–1) applied to the dark seed
379
+ * saturation. Applies to the `dark` and `darkContrast` variants; falls
380
+ * back to `saturation` when omitted.
381
+ *
382
+ * When set, the global `darkDesaturation` reduction is **not** applied on
383
+ * top — the resulting saturation is taken literally. Ignored under
384
+ * `mode: 'static'`.
385
+ */
386
+ darkSaturation?: number;
231
387
  /** Name of another color in the same theme (dependent color). */
232
388
  base?: string;
233
389
  /**
@@ -241,14 +397,15 @@ interface RegularColorDef {
241
397
  * Whether to flip out-of-bounds results to the opposite side instead of
242
398
  * clamping to the extreme. Affects both:
243
399
  * - relative `tone`: when `base ± delta` exceeds `[0, 100]`, mirror the
244
- * delta to the other side of the base.
400
+ * delta to the other side of the base. If the mirrored target is also
401
+ * out of range, keep the original delta and clamp on the authored side.
245
402
  * - `contrast`: when the requested direction can't meet the floor, try the
246
403
  * opposite side (same as the global `autoFlip`).
247
404
  *
248
405
  * Defaults to the global `autoFlip` config (default `true`). Set `false`
249
406
  * to clamp instead.
250
407
  */
251
- flip?: boolean;
408
+ autoFlip?: boolean;
252
409
  /**
253
410
  * Fixed opacity (0–1).
254
411
  * Output includes alpha in the CSS value.
@@ -260,8 +417,8 @@ interface RegularColorDef {
260
417
  /**
261
418
  * Per-color override for the hue-independent "safe" chroma limit used in
262
419
  * OKHSL↔sRGB conversions (luminance, contrast solving, output formatting).
263
- * Falls through to the global / per-theme `pastel` config when omitted.
264
- * @see GlazeConfig.pastel
420
+ * Falls through to the per-theme / per-token `pastel` override when omitted.
421
+ * @see GlazeConfigOverride.pastel
265
422
  */
266
423
  pastel?: boolean;
267
424
  /**
@@ -280,44 +437,96 @@ interface RegularColorDef {
280
437
  */
281
438
  inherit?: boolean;
282
439
  }
283
- /** Shadow tuning knobs. All values use the 0–1 scale (OKHSL). */
284
- interface ShadowTuning {
285
- /** Fraction of fg saturation kept in pigment (0-1). Default: 0.18. */
286
- saturationFactor?: number;
287
- /** Upper clamp on pigment saturation (0-1). Default: 0.25. */
288
- maxSaturation?: number;
289
- /** Multiplier for bg lightness → pigment lightness. Default: 0.25. */
290
- lightnessFactor?: number;
291
- /** [min, max] clamp for pigment lightness (0-1). Default: [0.05, 0.20]. */
292
- lightnessBounds?: [number, number];
440
+ /**
441
+ * Tuning shared by `shadow` and `glow`. Global defaults come from
442
+ * `shadowTuning` / `glowTuning`; a per-color `tuning` merges over them field
443
+ * by field.
444
+ */
445
+ interface LightingTuning {
293
446
  /**
294
- * Target minimum gap between pigment lightness and bg lightness (0-1).
295
- * Default: 0.05.
447
+ * β — how strongly the object (`fg`) scales the step. The step is
448
+ * `intensity × k` with `k = max(0, 1 + β · d · (t_bg − t_fg) / 100)`,
449
+ * `d = +1` for a shadow and `−1` for a glow: a darker object deepens its
450
+ * shadow, a lighter one strengthens its glow. An object at the surface's
451
+ * own tone (or no `fg`) gives exactly `intensity`. `0` ignores the object.
452
+ * Default: 0.5.
296
453
  */
297
- minGapTarget?: number;
298
- /** Max alpha (0-1). Reached at intensity=100 with max contrast. Default: 1.0. */
299
- alphaMax?: number;
454
+ objectInfluence?: number;
300
455
  /**
301
- * Blend weight (0-1) pulling pigment hue toward bg hue.
302
- * 0 = pure fg hue, 1 = pure bg hue. Default: 0.2.
456
+ * Pigment saturation (0–1) in the surface's hue. Default: 0 (neutral black /
457
+ * white). Shows only with `reach` below 1 — pure black and white have no hue.
458
+ * A neutral surface has no hue either: there the tint takes the object's
459
+ * hue with `hueBleed` above 0, and the pigment stays neutral otherwise.
303
460
  */
304
- bgHueBlend?: number;
461
+ tint?: number;
462
+ /**
463
+ * Where the pigment sits (0–1): the fraction of the way from the surface's
464
+ * tone to black (shadow) / white (glow). Default: 1 (pure black / white),
465
+ * the most reach. Lower values let a `tint` show at the cost of reach.
466
+ */
467
+ reach?: number;
468
+ /**
469
+ * Pull (0–1) of the pigment hue from the surface's hue toward the object's
470
+ * (`fg`) hue. Default: 0. Needs `fg`, a `tint`, and `reach` below 1 to show.
471
+ */
472
+ hueBleed?: number;
305
473
  }
474
+ /**
475
+ * Tuning for `shadow` colors: the shared {@link LightingTuning}. It has no
476
+ * `headroom` — a shadow darkens toward black, and nothing goes below black —
477
+ * so a `headroom` set on a shadow's tuning throws.
478
+ */
479
+ type ShadowTuning = LightingTuning;
480
+ /** Tuning for `glow` colors: the shared {@link LightingTuning} plus HDR `headroom`. */
481
+ interface GlowTuning extends LightingTuning {
482
+ /**
483
+ * Stops above SDR white a glow may use when its step crosses white. Default:
484
+ * 0 (SDR only). Counted in stops — doublings of SDR white's luminance — not
485
+ * as a ratio: `1` allows up to 2× white, `2` up to 4×. Must be a finite
486
+ * number ≥ 0; values above 2 clamp to 2 with a one-time warning.
487
+ *
488
+ * Only a glow whose target tone (surface tone + `intensity × k`) passes 100
489
+ * gets an HDR value: the variant's `hdr`, an opaque exposure of what SDR
490
+ * paints at its brightest — the pigment at full opacity, or a white surface
491
+ * no pigment can lift — plus a gain in stops. A pigment that cannot lift a
492
+ * non-white surface at all (`reach: 0` on a gray surface, say) paints
493
+ * nothing in SDR, so it gets no HDR value either and keeps the SDR "cannot
494
+ * reach" warning. On a chromatic surface a `reach: 0` gray can measure
495
+ * slightly brighter than the surface and pins at full opacity instead.
496
+ * Nothing about the SDR variant changes. The gain lands the exact step when
497
+ * the headroom allows it; the brightest channel caps at `2^headroom`.
498
+ * High-contrast schemes use the same headroom.
499
+ *
500
+ * The HDR value lands the whole step, not just the part past white. A
501
+ * full-reach glow (the default `reach: 1`) is pinned at white, so its HDR
502
+ * value starts at white (`exposure` → 0) as the target passes 100. A glow
503
+ * with `reach` below 1 is pinned at its pigment, below white: just under 100
504
+ * it stays short there (and warns), and just past 100 its HDR value jumps to
505
+ * the exact step.
506
+ */
507
+ headroom?: number;
508
+ }
509
+ /**
510
+ * A translucent dark overlay that lands the surface (`bg`) exactly
511
+ * `intensity` tones darker in every scheme — a shade for state layers and
512
+ * scrims, or, with `fg`, an object-aware drop shadow.
513
+ */
306
514
  interface ShadowColorDef {
307
515
  type: 'shadow';
308
516
  /**
309
- * Background color name — the surface the shadow sits on.
310
- * Must reference a non-shadow color in the same theme.
517
+ * The surface the shadow falls on. Must reference a color that is not a
518
+ * shadow or glow.
311
519
  */
312
520
  bg: string;
313
521
  /**
314
- * Foreground color name for tinting and intensity modulation.
315
- * Must reference a non-shadow color in the same theme.
316
- * Omit for achromatic shadow at full user-specified intensity.
522
+ * The object casting the shadow (e.g. the card). A darker object casts a
523
+ * deeper shadow, a lighter one a softer shadow — see
524
+ * `LightingTuning.objectInfluence`. Omit for an exact shade of `intensity`
525
+ * tones. Must reference a color that is not a shadow or glow.
317
526
  */
318
527
  fg?: string;
319
528
  /**
320
- * Shadow intensity, 0-100.
529
+ * Tone step, 0–100: how many tones darker than `bg` the composite lands.
321
530
  * Supports [normal, highContrast] pair.
322
531
  */
323
532
  intensity: HCPair<number>;
@@ -326,8 +535,46 @@ interface ShadowColorDef {
326
535
  /**
327
536
  * Per-color override for the hue-independent "safe" chroma limit used in
328
537
  * OKHSL↔sRGB conversions (luminance, contrast solving, output formatting).
329
- * Falls through to the global / per-theme `pastel` config when omitted.
330
- * @see GlazeConfig.pastel
538
+ * Falls through to the per-theme / per-token `pastel` override when omitted.
539
+ * @see GlazeConfigOverride.pastel
540
+ */
541
+ pastel?: boolean;
542
+ /**
543
+ * Whether this color is inherited by child themes created via `extend()`.
544
+ * Default: true. Set to false to make this color local to the current theme.
545
+ */
546
+ inherit?: boolean;
547
+ }
548
+ /**
549
+ * The mirror of {@link ShadowColorDef}: a translucent light overlay that lands
550
+ * the surface (`bg`) exactly `intensity` tones lighter in every scheme — for
551
+ * dark-mode elevation, edge highlights, and lightening state layers.
552
+ */
553
+ interface GlowColorDef {
554
+ type: 'glow';
555
+ /**
556
+ * The surface the glow falls on. Must reference a color that is not a
557
+ * shadow or glow.
558
+ */
559
+ bg: string;
560
+ /**
561
+ * The object emitting the glow. A lighter object glows stronger, a darker
562
+ * one weaker — see `LightingTuning.objectInfluence`. Omit for an exact tint
563
+ * of `intensity` tones. Must reference a color that is not a shadow or glow.
564
+ */
565
+ fg?: string;
566
+ /**
567
+ * Tone step, 0–100: how many tones lighter than `bg` the composite lands.
568
+ * Supports [normal, highContrast] pair.
569
+ */
570
+ intensity: HCPair<number>;
571
+ /** Override default tuning. Merged field-by-field with global `glowTuning`. */
572
+ tuning?: GlowTuning;
573
+ /**
574
+ * Per-color override for the hue-independent "safe" chroma limit used in
575
+ * OKHSL↔sRGB conversions (luminance, contrast solving, output formatting).
576
+ * Falls through to the per-theme / per-token `pastel` override when omitted.
577
+ * @see GlazeConfigOverride.pastel
331
578
  */
332
579
  pastel?: boolean;
333
580
  /**
@@ -357,9 +604,12 @@ interface MixColorDef {
357
604
  /**
358
605
  * Interpolation color space for opaque blending. Default: 'okhsl'.
359
606
  * - 'okhsl': perceptually uniform, consistent with Glaze's internal model.
360
- * - 'srgb': linear sRGB interpolation, matches browser compositing.
607
+ * - 'srgb': linear-light sRGB interpolation (like CSS
608
+ * `color-mix(in srgb-linear, …)`).
361
609
  *
362
- * Ignored for 'transparent' blend (always composites in linear sRGB).
610
+ * Ignored for 'transparent' blend: the browser composites that one, and
611
+ * its contrast solve models the browser (`source-over` on gamma-encoded
612
+ * sRGB).
363
613
  */
364
614
  space?: 'okhsl' | 'srgb';
365
615
  /**
@@ -373,8 +623,8 @@ interface MixColorDef {
373
623
  /**
374
624
  * Per-color override for the hue-independent "safe" chroma limit used in
375
625
  * OKHSL↔sRGB conversions (luminance, contrast solving, output formatting).
376
- * Falls through to the global / per-theme `pastel` config when omitted.
377
- * @see GlazeConfig.pastel
626
+ * Falls through to the per-theme / per-token `pastel` override when omitted.
627
+ * @see GlazeConfigOverride.pastel
378
628
  */
379
629
  pastel?: boolean;
380
630
  /**
@@ -389,7 +639,7 @@ interface MixColorDef {
389
639
  */
390
640
  inherit?: boolean;
391
641
  }
392
- type ColorDef = RegularColorDef | ShadowColorDef | MixColorDef;
642
+ type ColorDef = RegularColorDef | ShadowColorDef | GlowColorDef | MixColorDef;
393
643
  type ColorMap = Record<string, ColorDef>;
394
644
  /**
395
645
  * Resolved color for a single scheme variant.
@@ -413,6 +663,35 @@ interface ResolvedColorVariant {
413
663
  * gamut mapping applied during resolution.
414
664
  */
415
665
  pastel?: boolean;
666
+ /**
667
+ * HDR tier of a glow whose step crosses SDR white, present only when the
668
+ * glow's `headroom` is above 0, its target tone passes 100, and SDR is
669
+ * pinned at its brightest — the pigment at full opacity, or a white surface
670
+ * (see `GlowTuning.headroom`). Absent everywhere else — shadows, mixes,
671
+ * regular colors, a glow whose pigment cannot lift a non-white surface, and
672
+ * every glow at the default headroom of 0.
673
+ */
674
+ hdr?: ResolvedHdr;
675
+ }
676
+ /**
677
+ * The HDR value of a glow past SDR white: an opaque base plus a gain. The base
678
+ * is what SDR paints at its brightest — the glow's pigment at full opacity, or
679
+ * the white surface it cannot lift — and the HDR color is that base's linear
680
+ * sRGB times `2^exposure`. Format it with `glaze.formatHdr()`.
681
+ *
682
+ * Stored in OKHST like the variant. `s` is always standard (non-pastel)
683
+ * OKHSL saturation, whatever the variant's `pastel`, since the base is an
684
+ * arbitrary in-gamut composite.
685
+ */
686
+ interface ResolvedHdr {
687
+ /** Base OKHSL hue (0–360). */
688
+ h: number;
689
+ /** Base OKHSL saturation (0–1, non-pastel). */
690
+ s: number;
691
+ /** Base canonical tone (0–1, reference eps). */
692
+ t: number;
693
+ /** Gain over the base in stops (`log2` of the linear factor), above 0. */
694
+ exposure: number;
416
695
  }
417
696
  /** Fully resolved color across all scheme variants. */
418
697
  interface ResolvedColor {
@@ -421,7 +700,7 @@ interface ResolvedColor {
421
700
  dark: ResolvedColorVariant;
422
701
  lightContrast: ResolvedColorVariant;
423
702
  darkContrast: ResolvedColorVariant;
424
- /** Adaptation mode. Present only for regular colors, omitted for shadows. */
703
+ /** Adaptation mode of a regular color; `undefined` for shadow / glow / mix. */
425
704
  mode?: AdaptationMode;
426
705
  }
427
706
  /**
@@ -445,15 +724,58 @@ interface GlazeConfig {
445
724
  darkTone?: ToneWindow;
446
725
  /** Saturation reduction factor for dark scheme (0–1). Default: 0.1. */
447
726
  darkDesaturation?: number;
448
- /** State alias names for token export. */
727
+ /**
728
+ * State alias names for Tasty token export. Default to media-query states
729
+ * (`'@media(prefers-color-scheme: dark)'` / `'@media(prefers-contrast: more)'`
730
+ * / `'@media(dynamic-range: high)'`) so tokens react to the OS preference
731
+ * and the display without registering custom states. `hdr` keys an HDR
732
+ * glow value and is used only with `modes.hdr`.
733
+ */
449
734
  states?: {
450
735
  dark?: string;
451
736
  highContrast?: string;
737
+ hdr?: string;
452
738
  };
453
- /** Which scheme variants to include in exports. Default: both true. */
739
+ /**
740
+ * Which scheme variants to include in exports. Defaults: `dark: true`,
741
+ * `highContrast: false`, `hdr: false`.
742
+ */
454
743
  modes?: GlazeOutputModes;
455
- /** Default tuning for all shadow colors. Per-color tuning merges field-by-field. */
744
+ /**
745
+ * Default tuning for all shadow colors. Per-color tuning merges field-by-field.
746
+ * A `headroom` here throws: it is a glow-only field.
747
+ */
456
748
  shadowTuning?: ShadowTuning;
749
+ /**
750
+ * Default tuning for all glow colors, including HDR `headroom`. Per-color
751
+ * tuning merges field-by-field.
752
+ */
753
+ glowTuning?: GlowTuning;
754
+ /**
755
+ * Manual contrast level — a 0–100 slider from normal contrast (`0`) to high
756
+ * contrast (`100`), or `'auto'` (the default) to take the authored normal
757
+ * entries as-is.
758
+ *
759
+ * With a number, Glaze resolves the `light` / `dark` variants *at* that level.
760
+ * That is all it does: `lightContrast` / `darkContrast` stay the true
761
+ * high-contrast resolution at every level, and `modes.highContrast` alone
762
+ * decides whether they are emitted. Level `0` therefore reproduces `'auto'`
763
+ * bit for bit. At `100` the normal variants *are* the high-contrast ones, so a
764
+ * separate tier would duplicate them and a global level of 100 drops it.
765
+ *
766
+ * The level interpolates all three things that make high contrast differ:
767
+ * authored `[normal, highContrast]` pairs, the tone-window widening, and
768
+ * the `AA → AAA` / APCA `+15 Lc` escalation. Contrast floors are re-solved
769
+ * at the interpolated target, so every level is a solution rather than an
770
+ * approximation, and a color keeps to one side of its base as the level moves.
771
+ *
772
+ * Pass `'auto'` to leave manual mode; `configure()` never clears a field by
773
+ * omission. See `docs/reference/contrast.md` for the ramp's edge cases (un-interpolable
774
+ * pairs, side changes) and the export-freeze rule.
775
+ *
776
+ * @default 'auto'
777
+ */
778
+ contrastLevel?: number | 'auto';
457
779
  /**
458
780
  * Automatically flip tone direction when contrast can't be met.
459
781
  *
@@ -469,13 +791,6 @@ interface GlazeConfig {
469
791
  * falls back to the originally requested tone).
470
792
  */
471
793
  autoFlip?: boolean;
472
- /**
473
- * If true, uses a hue-independent "safe" chroma limit across all colors
474
- * so that scaling saturation never exceeds the sRGB boundary at any hue
475
- * for the given lightness.
476
- * @default false
477
- */
478
- pastel?: boolean;
479
794
  /**
480
795
  * If true (default), infer a color's `role` from its name when no explicit
481
796
  * `role` is set. Set to `false` to opt out of name-based inference (the
@@ -491,17 +806,31 @@ interface GlazeConfigResolved {
491
806
  states: {
492
807
  dark: string;
493
808
  highContrast: string;
809
+ hdr: string;
494
810
  };
495
811
  modes: Required<GlazeOutputModes>;
496
812
  shadowTuning?: ShadowTuning;
813
+ glowTuning?: GlowTuning;
497
814
  autoFlip: boolean;
815
+ /**
816
+ * Manual contrast level (0–100) for the normal variants, or `'auto'` to take
817
+ * the authored normal entries as-is.
818
+ * @see GlazeConfig.contrastLevel
819
+ */
820
+ contrastLevel: number | 'auto';
821
+ /**
822
+ * Instance-level pastel default (`def.pastel ?? config.pastel`).
823
+ * Not set via `glaze.configure()` — only via per-theme / per-token
824
+ * `GlazeConfigOverride` (default `false`).
825
+ */
498
826
  pastel: boolean;
499
827
  inferRole: boolean;
500
828
  }
501
829
  /**
502
830
  * Per-instance config override for `glaze.color()` and `glaze()` themes.
503
831
  * Fields that are set take priority over the live global config. Fields
504
- * that are omitted fall through to the live global at resolve time.
832
+ * that are omitted fall through to the live global at resolve time
833
+ * (`pastel` is instance-only and defaults to `false` when omitted).
505
834
  *
506
835
  * `false` for a tone window disables clamping (full range at reference eps).
507
836
  */
@@ -515,9 +844,22 @@ interface GlazeConfigOverride {
515
844
  /** Whether to auto-flip tone when contrast can't be met. */
516
845
  autoFlip?: boolean;
517
846
  /**
518
- * If true, uses a hue-independent "safe" chroma limit across all colors
519
- * so that scaling saturation never exceeds the sRGB boundary at any hue
520
- * for the given lightness.
847
+ * Manual contrast level (0–100) for this instance, or `'auto'` to opt out of a
848
+ * global level and take the authored normal entries as-is. Either way the
849
+ * instance keeps its high-contrast tier; a per-instance level never changes
850
+ * which modes are emitted.
851
+ *
852
+ * Only an instance-authored level is frozen into `.export()` snapshots — a
853
+ * level inherited from the global config is treated as a live preference and
854
+ * re-read at restore time.
855
+ *
856
+ * @see GlazeConfig.contrastLevel
857
+ */
858
+ contrastLevel?: number | 'auto';
859
+ /**
860
+ * Instance-level pastel default for colors that omit per-color `pastel`.
861
+ * Not available on `glaze.configure()` — set here or per-color.
862
+ * @default false
521
863
  */
522
864
  pastel?: boolean;
523
865
  /**
@@ -530,32 +872,67 @@ interface GlazeConfigOverride {
530
872
  * standalone color tokens.
531
873
  */
532
874
  shadowTuning?: ShadowTuning;
875
+ /**
876
+ * Glow tuning defaults. Only meaningful for themes; harmless on
877
+ * standalone color tokens.
878
+ */
879
+ glowTuning?: GlowTuning;
533
880
  }
881
+ /**
882
+ * Current authoring-export schema version. Bump when the export shape
883
+ * changes in a non-compatible way. Written on every `.export()` snapshot.
884
+ *
885
+ * - `1` — Glaze 2.x.
886
+ * - `2` — Glaze 3: `shadow` `intensity` / `fg` / `tuning` changed meaning and
887
+ * `glow` was added. Version-1 theme snapshots that carry shadow defs or a
888
+ * `shadowTuning` are rejected on restore.
889
+ */
890
+ declare const GLAZE_EXPORT_VERSION: 2;
891
+ /** Literal type of {@link GLAZE_EXPORT_VERSION}. */
892
+ type GlazeExportVersion = typeof GLAZE_EXPORT_VERSION;
893
+ /** Discriminator for authoring export snapshots. */
894
+ type GlazeExportKind = 'theme' | 'color' | 'palette';
534
895
  /** Serialized theme configuration (no resolved values). */
535
896
  interface GlazeThemeExport {
897
+ /** Snapshot kind. Always written by `theme.export()`; optional on legacy hand-written configs. */
898
+ kind?: 'theme';
899
+ /** Schema version. Always written by `theme.export()`; optional on legacy configs. */
900
+ version?: number;
536
901
  hue: number;
537
902
  saturation: number;
903
+ /** Dark-scheme seed hue (0–360). Omitted when the theme reuses `hue`. */
904
+ darkHue?: number;
905
+ /** Dark-scheme seed saturation (0–100). Omitted when the theme reuses `saturation`. */
906
+ darkSaturation?: number;
538
907
  colors: ColorMap;
539
- /** Per-theme config override, if any. */
908
+ /**
909
+ * Effective config freeze from `.export()` —
910
+ * `getConfig() ∪ instance local ∪ exportArg`. May be sparse on legacy
911
+ * snapshots (omitted fields fall through to the live global at restore).
912
+ */
540
913
  config?: GlazeConfigOverride;
541
914
  }
542
- /** Input for `glaze.shadow()` standalone factory. */
543
- interface GlazeShadowInput {
915
+ /** Input for the `glaze.shadow()` and `glaze.glow()` standalone factories. */
916
+ interface GlazeLightingInput {
544
917
  /**
545
- * Background color — accepts any `GlazeColorValue` form: hex
546
- * (`#rgb` / `#rrggbb` / `#rrggbbaa`), `rgb()` / `hsl()` / `okhsl()`
547
- * / `oklch()` strings, or literal objects (`{ r, g, b }`, `{ h, s, l }`,
548
- * `{ l, c, h }`). Alpha components are dropped with a warning.
918
+ * The surface — accepts any `GlazeColorValue` form: hex
919
+ * (`#rgb` / `#rgba` / `#rrggbb` / `#rrggbbaa`), `rgb()` / `hsl()` /
920
+ * `okhsl()` / `okhst()` / `oklch()` strings, or literal objects
921
+ * (`{ r, g, b }`, `{ h, s, l }`, `{ h, s, t }`, `{ l, c, h }`). Alpha
922
+ * components are dropped with a warning.
549
923
  */
550
924
  bg: GlazeColorValue;
551
- /**
552
- * Foreground color for tinting + intensity modulation. Accepts the
553
- * same forms as `bg`.
554
- */
925
+ /** The object casting the shadow / emitting the glow. Same forms as `bg`. */
555
926
  fg?: GlazeColorValue;
556
- /** Intensity 0-100. */
927
+ /** Tone step, 0–100. */
557
928
  intensity: number;
558
- tuning?: ShadowTuning;
929
+ tuning?: LightingTuning;
930
+ }
931
+ /** Input for `glaze.shadow()`. */
932
+ type GlazeShadowInput = GlazeLightingInput;
933
+ /** Input for `glaze.glow()`: the shared input, with glow tuning (`headroom`). */
934
+ interface GlazeGlowInput extends GlazeLightingInput {
935
+ tuning?: GlowTuning;
559
936
  }
560
937
  /** Input for the structured `glaze.color()` overload. */
561
938
  interface GlazeColorInput {
@@ -563,9 +940,18 @@ interface GlazeColorInput {
563
940
  saturation: number;
564
941
  tone: HCPair<number | ExtremeValue>;
565
942
  saturationFactor?: number;
943
+ /**
944
+ * Dark-scheme hue. Number is absolute (0–360); `'+N'`/`'-N'` is relative
945
+ * to the dark seed hue, which defaults to `hue`. Falls back to `hue`.
946
+ */
947
+ darkHue?: number | RelativeValue;
948
+ /** Dark-scheme seed saturation (0–100). Defaults to `saturation`. */
949
+ darkSaturation?: number;
950
+ /** Dark-scheme multiplier on the dark seed (0–1). Defaults to `saturationFactor`. */
951
+ darkSaturationFactor?: number;
566
952
  mode?: AdaptationMode;
567
953
  /** Flip out-of-bounds results instead of clamping. Default: global `autoFlip`. */
568
- flip?: boolean;
954
+ autoFlip?: boolean;
569
955
  /**
570
956
  * Fixed opacity (0–1). Output includes alpha in the CSS value.
571
957
  * Combining with `contrast` is not recommended (perceived tone
@@ -579,29 +965,33 @@ interface GlazeColorInput {
579
965
  */
580
966
  base?: GlazeColorToken | GlazeColorValue;
581
967
  /**
582
- * Contrast floor against `base`. Requires `base` to be set. A bare
968
+ * Contrast floor. Without `base`, solved against the token's own input
969
+ * color (`hue`, `saturation`, normal `tone`), held fixed in every scheme;
970
+ * with `base`, against the base's resolved variant per scheme. A bare
583
971
  * number/preset is WCAG; use `{ wcag }` / `{ apca }` to pick the metric.
584
972
  */
585
973
  contrast?: HCPair<ContrastSpec>;
586
974
  /**
587
975
  * Optional human-readable name for the token. Used in error and
588
976
  * warning messages (otherwise an internal name like `"value"` is
589
- * used). Does not affect output keys.
977
+ * used) and as `resolve().name`. A debug label only: it does not
978
+ * affect output keys or the token's `role`.
590
979
  */
591
980
  name?: string;
592
981
  /**
593
982
  * Per-color override for the hue-independent "safe" chroma limit used in
594
983
  * OKHSL↔sRGB conversions (luminance, contrast solving, output formatting).
595
- * Falls through to the global / per-theme `pastel` config when omitted.
596
- * @see GlazeConfig.pastel
984
+ * Falls through to the per-theme / per-token `pastel` override when omitted.
985
+ * @see GlazeConfigOverride.pastel
597
986
  */
598
987
  pastel?: boolean;
599
988
  /**
600
- * Semantic role against `base` / the literal seed: how this token is used.
601
- * Fixes APCA contrast polarity. Same resolution chain as
602
- * `RegularColorDef.role` (explicit → name inference → opposite of base →
603
- * `'text'`). For standalone tokens the name is internal, so set `role`
604
- * explicitly or rely on the base-opposite / foreground default.
989
+ * Semantic role against the token's anchor (`base`, or its own input
990
+ * color when `contrast` is set without one): how this token is used.
991
+ * Fixes APCA contrast polarity; WCAG ignores it. Defaults to `'text'` —
992
+ * the token is the foreground and the anchor the background, with or
993
+ * without `base`. Unlike a theme color, a token never infers its role
994
+ * from `name`, so `inferRole` has no effect on it.
605
995
  */
606
996
  role?: RoleInput;
607
997
  }
@@ -609,17 +999,35 @@ interface GlazeColorInput {
609
999
  * Any single-color input form accepted by the value-shorthand
610
1000
  * overload of `glaze.color()`.
611
1001
  *
612
- * Strings cover hex (`#rgb` / `#rrggbb` / `#rrggbbaa`, alpha dropped
613
- * with a warning) and the four CSS color functions Glaze itself emits:
614
- * `rgb()`, `hsl()`, `okhsl()`, `oklch()` (alpha components also dropped
615
- * with a warning).
1002
+ * Strings cover hex with a leading `#` (`#rgb` / `#rgba` / `#rrggbb` /
1003
+ * `#rrggbbaa`) and the five color functions Glaze itself emits: `rgb()`,
1004
+ * `hsl()`, `okhsl()`, `okhst()`, `oklch()`, plus the `rgba()` / `hsla()`
1005
+ * aliases. Function names are case-insensitive; components are numbers or
1006
+ * percentages, separated by spaces or commas. Their scales:
1007
+ * - `rgb()` — channels 0–255, or percentages (`100%` is 255).
1008
+ * - `hsl()` / `okhsl()` / `okhst()` — saturation, lightness and tone are
1009
+ * percentages or bare 0–1 fractions: `hsl(120 50% 50%)` or
1010
+ * `hsl(120 0.5 0.5)`. Unlike CSS, a bare `hsl(120 50 50)` does not mean
1011
+ * 50%; it is out of range and resolves to white without a warning.
1012
+ * - `oklch()` — L 0–1 or a percentage; C bare, or a percentage where
1013
+ * `100%` is 0.4.
1014
+ * - Hue is in degrees, bare or with `deg`. Other angle units (`turn`,
1015
+ * `rad`, `grad`) are misread as degrees rather than rejected.
1016
+ *
1017
+ * An alpha component (hex alpha, `/ A`, or any fourth component) is
1018
+ * dropped with a warning. Named colors, other color functions, and
1019
+ * relative color syntax throw.
616
1020
  *
617
- * Literal object forms:
618
- * - `{ h, s, l }` — OKHSL (h: 0–360, s/l: 0–1). Passing 0–100 for `s`/`l`
619
- * throws with a hint to use the structured form.
620
- * - `{ h, s, t }` — OKHST (h: 0–360, s/t: 0–1). Tone in 0–1.
1021
+ * Literal object forms, told apart by their keys:
621
1022
  * - `{ r, g, b }` — sRGB 0–255.
622
1023
  * - `{ l, c, h }` — OKLCh (L/C: 0–1, H: degrees), same as `oklch()` strings.
1024
+ * - `{ h, s, t }` — OKHST (h: 0–360, s/t: 0–1). Tone in 0–1, so `t: 0.7` is
1025
+ * tone 70.
1026
+ * - `{ h, s, l }` — OKHSL (h: 0–360, s/l: 0–1).
1027
+ *
1028
+ * An object `s`, `l`, `t` or `c` above 1.5 throws, which catches 0–100
1029
+ * input; the OKHSL / OKHST errors point to the structured form. String
1030
+ * components have no such check.
623
1031
  */
624
1032
  type GlazeColorValue = string | OkhslColor | OkhstColor | RgbColor | OklchColor;
625
1033
  /** Color overrides for the `from` and value-shorthand inputs. */
@@ -632,6 +1040,23 @@ interface GlazeColorOverrides {
632
1040
  hue?: number | RelativeValue;
633
1041
  /** Override seed saturation (0–100). Default: extracted from value. */
634
1042
  saturation?: number;
1043
+ /**
1044
+ * Dark-scheme hue. Number is absolute (0–360); `'+N'`/`'-N'` is relative
1045
+ * to the dark seed hue, which defaults to the (possibly overridden) seed
1046
+ * hue. Falls back to `hue` when omitted.
1047
+ */
1048
+ darkHue?: number | RelativeValue;
1049
+ /**
1050
+ * Dark-scheme seed saturation (0–100). Defaults to `saturation`. When set,
1051
+ * the global `darkDesaturation` reduction is not applied on top.
1052
+ */
1053
+ darkSaturation?: number;
1054
+ /**
1055
+ * Dark-scheme multiplier on the dark seed (0–1). Defaults to
1056
+ * `saturationFactor`. When set, the global `darkDesaturation` reduction is
1057
+ * not applied on top.
1058
+ */
1059
+ darkSaturationFactor?: number;
635
1060
  /**
636
1061
  * Override tone. Number is absolute (0–100, contrast-uniform); `'+N'`/`'-N'`
637
1062
  * is relative to the literal seed (the value passed to `glaze.color()`);
@@ -644,11 +1069,11 @@ interface GlazeColorOverrides {
644
1069
  /**
645
1070
  * Adaptation mode. Defaults to `'auto'` for every input form, so
646
1071
  * colors automatically adapt between light and dark like an ordinary
647
- * theme color. All value-shorthand inputs (strings and literal objects)
648
- * preserve light tone (`lightTone: false`) and snapshot
649
- * `globalConfig.darkTone` on the dark side. Only the structured
650
- * `{ hue, saturation, tone }` form also snapshots
651
- * `globalConfig.lightTone`.
1072
+ * theme color. Value-shorthand inputs (strings and literal objects)
1073
+ * preserve light tone via a local `lightTone: false` default; other
1074
+ * omitted config fields fall through to the live global at resolve
1075
+ * time. Structured `{ hue, saturation, tone }` form also falls
1076
+ * through for both tone windows unless overridden.
652
1077
  *
653
1078
  * Pass `'fixed'` explicitly to opt back into the linear, non-
654
1079
  * inverting mapping; pass `'static'` to pin the same tone
@@ -660,7 +1085,7 @@ interface GlazeColorOverrides {
660
1085
  * `contrast`) to the opposite side instead of clamping. Defaults to
661
1086
  * the global `autoFlip`.
662
1087
  */
663
- flip?: boolean;
1088
+ autoFlip?: boolean;
664
1089
  /**
665
1090
  * Contrast floor. By default solved against the literal seed
666
1091
  * (the value itself); when `base` is set, solved against the base's
@@ -681,11 +1106,10 @@ interface GlazeColorOverrides {
681
1106
  * tone per-scheme (matches theme behavior for dependent colors).
682
1107
  * - Relative `hue: '+N'` / `'-N'` still anchors to the seed (the
683
1108
  * value passed to `glaze.color()`), not the base.
684
- * - When the base was created via the structured form (with explicit
685
- * `hue`/`saturation`/`tone`), it is resolved at full range
686
- * (`lightTone: false`) for the linking math — ensuring the
687
- * contrast/tone anchor matches the input tone, not the
688
- * windowed output. The base's own `.resolve()` output is unaffected.
1109
+ * - The link resolves against the base token's own variants in every
1110
+ * scheme: what the base paints, under its own config, whatever form
1111
+ * the base was made in. A light `'+0'` lands on the base and a light
1112
+ * `contrast` floor is measured against it.
689
1113
  *
690
1114
  * The base token's `.resolve()` is called lazily on first resolve and
691
1115
  * its result is captured by reference; later mutations to the base's
@@ -701,20 +1125,24 @@ interface GlazeColorOverrides {
701
1125
  /**
702
1126
  * Optional human-readable name for the token. Used in error and
703
1127
  * warning messages (otherwise an internal name like `"value"` is
704
- * used). Does not affect output keys.
1128
+ * used) and as `resolve().name`. A debug label only: it does not
1129
+ * affect output keys or the token's `role`.
705
1130
  */
706
1131
  name?: string;
707
1132
  /**
708
1133
  * Per-color override for the hue-independent "safe" chroma limit used in
709
1134
  * OKHSL↔sRGB conversions (luminance, contrast solving, output formatting).
710
- * Falls through to the global / per-theme `pastel` config when omitted.
711
- * @see GlazeConfig.pastel
1135
+ * Falls through to the per-theme / per-token `pastel` override when omitted.
1136
+ * @see GlazeConfigOverride.pastel
712
1137
  */
713
1138
  pastel?: boolean;
714
1139
  /**
715
- * Semantic role against `base` / the literal seed: how this token is used.
716
- * Fixes APCA contrast polarity. Same resolution chain as
717
- * `RegularColorDef.role`.
1140
+ * Semantic role against the token's anchor (`base`, or the literal seed
1141
+ * when `contrast` / a relative `tone` is set without one): how this token
1142
+ * is used. Fixes APCA contrast polarity; WCAG ignores it. Defaults to
1143
+ * `'text'` — the token is the foreground and the anchor the background,
1144
+ * with or without `base`. Unlike a theme color, a token never infers its
1145
+ * role from `name`, so `inferRole` has no effect on it.
718
1146
  */
719
1147
  role?: RoleInput;
720
1148
  }
@@ -739,13 +1167,19 @@ interface GlazeColorCssOptions {
739
1167
  * `name: 'brand'` → `--brand-color: …`.
740
1168
  */
741
1169
  name: string;
742
- /** Output color format. Default: 'rgb' (matches `theme.css` default). */
1170
+ /** Output color format. Default: 'oklch'. */
743
1171
  format?: GlazeColorFormat;
744
1172
  /**
745
1173
  * Suffix appended to the name. Default: '-color' (matches
746
1174
  * `theme.css` default).
747
1175
  */
748
1176
  suffix?: string;
1177
+ /**
1178
+ * Emit hue as a separate `--{name}-hue` custom property, referenced via
1179
+ * `var()`. Requires `format: 'oklch'` (any other format throws) and a
1180
+ * pastel token.
1181
+ */
1182
+ splitHue?: boolean;
749
1183
  }
750
1184
  /** Return type for `glaze.color()`. */
751
1185
  interface GlazeColorToken {
@@ -755,7 +1189,7 @@ interface GlazeColorToken {
755
1189
  token(options?: GlazeTokenOptions): Record<string, string>;
756
1190
  /**
757
1191
  * Export as a tasty style-to-state binding (no color name key).
758
- * Uses `#name` keys and state aliases (`''`, `@dark`, etc.).
1192
+ * Uses `#name` keys and state aliases (`''`, `'@media(prefers-color-scheme: dark)'`, etc.).
759
1193
  * @see https://tasty.style/docs
760
1194
  */
761
1195
  tasty(options?: GlazeTokenOptions): Record<string, string>;
@@ -763,18 +1197,42 @@ interface GlazeColorToken {
763
1197
  json(options?: GlazeJsonOptions): Record<string, string>;
764
1198
  /** Export as CSS custom property declarations grouped by scheme variant. */
765
1199
  css(options: GlazeColorCssOptions): GlazeCssResult;
1200
+ /**
1201
+ * Export as W3C DTCG color tokens (one per scheme variant, no color name
1202
+ * key). Each entry is a full `{ $type: 'color', $value }` token.
1203
+ * @see https://www.designtokens.org/
1204
+ */
1205
+ dtcg(options?: GlazeDtcgOptions): GlazeColorDtcgResult;
1206
+ /**
1207
+ * Export as a single W3C DTCG Resolver-Module document for this color,
1208
+ * keyed by `name` across all scheme variants. `name` is required.
1209
+ * @see https://www.designtokens.org/
1210
+ */
1211
+ dtcgResolver(options: GlazeColorDtcgResolverOptions): GlazeDtcgResolverDocument;
1212
+ /**
1213
+ * Export as a Tailwind CSS v4 `@theme` block (light baseline) plus dark /
1214
+ * high-contrast overrides. Returns a single ready-to-paste CSS string.
1215
+ * `name` is required (forms `--color-<name>`).
1216
+ * @see https://tailwindcss.com/docs/theme
1217
+ */
1218
+ tailwind(options: GlazeColorTailwindOptions): string;
766
1219
  /**
767
1220
  * Serialize the token as a JSON-safe object. Captures the original
768
1221
  * input value, overrides, and config so it can be rehydrated via
769
1222
  * `glaze.colorFrom(...)`. `base` is recursively serialized.
1223
+ * Optional `override` is merged over the instance local at export time.
770
1224
  */
771
- export(): GlazeColorTokenExport;
1225
+ export(override?: GlazeConfigOverride): GlazeColorTokenExport;
772
1226
  }
773
1227
  /**
774
1228
  * JSON-safe serialization of a `glaze.color()` token. Pass to
775
1229
  * `glaze.colorFrom(...)` to rehydrate.
776
1230
  */
777
1231
  interface GlazeColorTokenExport {
1232
+ /** Snapshot kind. Always written by `token.export()`; optional on legacy snapshots. */
1233
+ kind?: 'color';
1234
+ /** Schema version. Always written by `token.export()`; optional on legacy snapshots. */
1235
+ version?: number;
778
1236
  /**
779
1237
  * Discriminator for the source overload that created the token.
780
1238
  * - `'value'`: created via `glaze.color(value)` or `glaze.color({ from, ...overrides })`.
@@ -789,14 +1247,26 @@ interface GlazeColorTokenExport {
789
1247
  */
790
1248
  overrides?: GlazeColorOverridesExport;
791
1249
  /**
792
- * Effective config snapshot at creation time — captures the merged
793
- * result of the global config + any per-call override at the moment
794
- * the token was created. Only fields that differ from their
795
- * post-merge defaults are present. Used by `glaze.colorFrom()` to
796
- * reproduce deterministic behavior across `configure()` calls.
1250
+ * Effective config freeze from `.export()` — `getConfig() ∪ local ∪
1251
+ * exportArg` at call time. Used by `glaze.colorFrom()` to pin
1252
+ * deterministic behavior across later `configure()` calls.
797
1253
  */
798
1254
  config?: GlazeConfigOverride;
799
1255
  }
1256
+ /**
1257
+ * JSON-safe serialization of a `glaze.palette()` composition.
1258
+ * Pass to `glaze.paletteFrom(...)` to rehydrate.
1259
+ */
1260
+ interface GlazePaletteExport {
1261
+ /** Snapshot kind. Always written by `palette.export()`. */
1262
+ kind?: 'palette';
1263
+ /** Schema version. Always written by `palette.export()`. */
1264
+ version?: number;
1265
+ /** Per-theme authoring snapshots keyed by theme name. */
1266
+ themes: Record<string, GlazeThemeExport>;
1267
+ /** Primary theme name, if set on the palette. */
1268
+ primary?: string;
1269
+ }
800
1270
  /**
801
1271
  * Serializable shape of a structured `glaze.color({...})` input.
802
1272
  * Differs from `GlazeColorInput` only in that `base` is replaced by an
@@ -807,8 +1277,11 @@ interface GlazeColorInputExport {
807
1277
  saturation: number;
808
1278
  tone: HCPair<number | ExtremeValue>;
809
1279
  saturationFactor?: number;
1280
+ darkHue?: number | RelativeValue;
1281
+ darkSaturation?: number;
1282
+ darkSaturationFactor?: number;
810
1283
  mode?: AdaptationMode;
811
- flip?: boolean;
1284
+ autoFlip?: boolean;
812
1285
  opacity?: number;
813
1286
  base?: GlazeColorTokenExport | GlazeColorValue;
814
1287
  contrast?: HCPair<ContrastSpec>;
@@ -823,10 +1296,13 @@ interface GlazeColorInputExport {
823
1296
  interface GlazeColorOverridesExport {
824
1297
  hue?: number | RelativeValue;
825
1298
  saturation?: number;
1299
+ darkHue?: number | RelativeValue;
1300
+ darkSaturation?: number;
1301
+ darkSaturationFactor?: number;
826
1302
  tone?: HCPair<ToneValue>;
827
1303
  saturationFactor?: number;
828
1304
  mode?: AdaptationMode;
829
- flip?: boolean;
1305
+ autoFlip?: boolean;
830
1306
  contrast?: HCPair<ContrastSpec>;
831
1307
  base?: GlazeColorTokenExport | GlazeColorValue;
832
1308
  opacity?: number;
@@ -834,11 +1310,33 @@ interface GlazeColorOverridesExport {
834
1310
  pastel?: boolean;
835
1311
  role?: RoleInput;
836
1312
  }
1313
+ /**
1314
+ * The hue/saturation seed pair a theme resolves against.
1315
+ *
1316
+ * `darkHue` / `darkSaturation` are a separate seed for the `dark` and
1317
+ * `darkContrast` variants; each falls back to its light counterpart. Setting
1318
+ * `darkSaturation` opts the theme out of the global `darkDesaturation`
1319
+ * reduction — the authored value is used literally.
1320
+ */
1321
+ interface GlazeThemeSeed {
1322
+ /** Light-scheme hue seed (0–360). */
1323
+ hue: number;
1324
+ /** Light-scheme saturation seed (0–100). */
1325
+ saturation: number;
1326
+ /** Dark-scheme hue seed (0–360). Defaults to `hue`. */
1327
+ darkHue?: number;
1328
+ /** Dark-scheme saturation seed (0–100). Defaults to `saturation`. */
1329
+ darkSaturation?: number;
1330
+ }
837
1331
  interface GlazeTheme {
838
1332
  /** The hue seed (0–360). */
839
1333
  readonly hue: number;
840
1334
  /** The saturation seed (0–100). */
841
1335
  readonly saturation: number;
1336
+ /** The dark-scheme hue seed (0–360), or `undefined` when `hue` is reused. */
1337
+ readonly darkHue: number | undefined;
1338
+ /** The dark-scheme saturation seed (0–100), or `undefined` when `saturation` is reused. */
1339
+ readonly darkSaturation: number | undefined;
842
1340
  /** The effective config for this theme. */
843
1341
  getConfig(): GlazeConfigResolved;
844
1342
  /** Add/replace colors (additive merge with existing definitions). */
@@ -856,7 +1354,7 @@ interface GlazeTheme {
856
1354
  /** Clear all color definitions. */
857
1355
  reset(): void;
858
1356
  /** Export the theme configuration as a JSON-safe object. */
859
- export(): GlazeThemeExport;
1357
+ export(override?: GlazeConfigOverride): GlazeThemeExport;
860
1358
  /** Create a child theme inheriting all color definitions. */
861
1359
  extend(options: GlazeExtendOptions): GlazeTheme;
862
1360
  /** Resolve all colors and return the result map. */
@@ -868,59 +1366,368 @@ interface GlazeTheme {
868
1366
  * theme.tokens()
869
1367
  * // → { light: { surface: 'okhsl(...)' }, dark: { surface: 'okhsl(...)' } }
870
1368
  * ```
1369
+ *
1370
+ * With `modes.hdr`, each scheme map is followed by a dense `<scheme>Hdr`
1371
+ * map (`lightHdr`, `darkHdr`, …): every glow with an HDR value in any
1372
+ * emitted scheme, as `color(srgb-linear …)`, or its SDR value where that
1373
+ * scheme has none.
871
1374
  */
872
1375
  tokens(options?: GlazeJsonOptions): Record<string, Record<string, string>>;
873
1376
  /**
874
1377
  * Export as tasty style-to-state bindings.
875
- * Uses `#name` color token keys and state aliases (`''`, `@dark`, etc.).
1378
+ * Uses `#name` color token keys and state aliases (`''`, `'@media(prefers-color-scheme: dark)'`, etc.).
876
1379
  * Spread into component styles or register as a recipe via `configure({ recipes })`.
1380
+ * With `modes.hdr`, a glow's HDR value follows each SDR key that has one,
1381
+ * under `states.hdr` (`'@media(dynamic-range: high)'`) combined with it.
877
1382
  * @see https://tasty.style/docs
878
1383
  */
879
- tasty(options?: GlazeTokenOptions): Record<string, Record<string, string>>;
880
- /** Export as plain JSON. */
1384
+ tasty(options?: GlazeTastyOptions): Record<string, Record<string, string>>;
1385
+ /**
1386
+ * Export as plain JSON. With `modes.hdr`, a glow variant with an HDR value
1387
+ * adds a `<scheme>Hdr` key (`lightHdr`, …) right after its SDR key.
1388
+ */
881
1389
  json(options?: GlazeJsonOptions): Record<string, Record<string, string>>;
882
1390
  /** Export as CSS custom property declarations. */
883
1391
  css(options?: GlazeCssOptions): GlazeCssResult;
1392
+ /**
1393
+ * Export as W3C Design Tokens Format Module (2025.10) documents, one per
1394
+ * scheme variant. Consumable by Figma, Tokens Studio, Style Dictionary,
1395
+ * Terrazzo, and any DTCG-compatible tool.
1396
+ * @see https://www.designtokens.org/
1397
+ */
1398
+ dtcg(options?: GlazeDtcgOptions): GlazeDtcgResult;
1399
+ /**
1400
+ * Export as a single W3C DTCG Resolver-Module document describing every
1401
+ * scheme variant as one `sets` entry plus a single `scheme` modifier with a
1402
+ * context per variant. Consumable by resolver tools such as Dispersa.
1403
+ * @see https://www.designtokens.org/
1404
+ */
1405
+ dtcgResolver(options?: GlazeDtcgResolverOptions): GlazeDtcgResolverDocument;
1406
+ /**
1407
+ * Export as a Tailwind CSS v4 `@theme` block (light baseline) plus dark /
1408
+ * high-contrast overrides under configurable selectors. Returns a single
1409
+ * ready-to-paste CSS string.
1410
+ * @see https://tailwindcss.com/docs/theme
1411
+ */
1412
+ tailwind(options?: GlazeTailwindOptions): string;
884
1413
  }
885
1414
  interface GlazeExtendOptions {
886
1415
  hue?: number;
887
1416
  saturation?: number;
1417
+ /** Replace the dark-scheme hue seed (0–360). Defaults to the parent's. */
1418
+ darkHue?: number;
1419
+ /** Replace the dark-scheme saturation seed (0–100). Defaults to the parent's. */
1420
+ darkSaturation?: number;
888
1421
  colors?: ColorMap;
889
1422
  /** Config override for the child theme. Merged with the parent's override. */
890
1423
  config?: GlazeConfigOverride;
891
1424
  }
1425
+ /**
1426
+ * Options for `token.token()` / `token.tasty()`, and the base of the theme and
1427
+ * palette `tasty()` options: output format, scheme variants, state aliases.
1428
+ */
892
1429
  interface GlazeTokenOptions {
893
- /** Prefix mode. `true` uses "<themeName>-", or provide a custom map. */
894
- prefix?: boolean | Record<string, string>;
895
- /** Override state aliases for this export. */
1430
+ /**
1431
+ * Override state aliases for this export. `hdr` keys an HDR glow value
1432
+ * (with `modes.hdr`); a `glaze.color()` token is never a glow, so it never
1433
+ * uses it.
1434
+ */
896
1435
  states?: {
897
1436
  dark?: string;
898
1437
  highContrast?: string;
1438
+ hdr?: string;
899
1439
  };
900
1440
  /** Override which scheme variants to include. */
901
1441
  modes?: GlazeOutputModes;
902
- /** Output color format. Default: 'okhsl'. */
1442
+ /** Output color format. Default: 'oklch'. */
903
1443
  format?: GlazeColorFormat;
904
1444
  }
1445
+ /** Options for `theme.tasty()`. */
1446
+ interface GlazeTastyOptions extends GlazeTokenOptions {
1447
+ /**
1448
+ * Emit hue as a separate custom property, referenced via `var()`.
1449
+ * Requires `format: 'oklch'` (any other format throws) and every color to
1450
+ * be pastel.
1451
+ */
1452
+ splitHue?: boolean;
1453
+ /**
1454
+ * Base name for the theme-level hue var (`--{name}-hue`). Default: `'theme'`.
1455
+ * Must not equal the name of a color that sets its own hue (throws).
1456
+ */
1457
+ name?: string;
1458
+ }
1459
+ /** Options for `theme.tokens()` / `theme.json()` and their palette and token counterparts. */
905
1460
  interface GlazeJsonOptions {
906
1461
  /** Override which scheme variants to include. */
907
1462
  modes?: GlazeOutputModes;
908
- /** Output color format. Default: 'okhsl'. */
1463
+ /** Output color format. Default: 'oklch'. */
909
1464
  format?: GlazeColorFormat;
910
1465
  }
1466
+ /** Options for `theme.css()`. */
911
1467
  interface GlazeCssOptions {
912
- /** Output color format. Default: 'rgb'. */
1468
+ /** Output color format. Default: 'oklch'. */
913
1469
  format?: GlazeColorFormat;
914
1470
  /** Suffix appended to each CSS custom property name. Default: '-color'. */
915
1471
  suffix?: string;
1472
+ /**
1473
+ * Emit hue as a separate custom property, referenced via `var()`.
1474
+ * Requires `format: 'oklch'` (any other format throws) and every color to
1475
+ * be pastel.
1476
+ */
1477
+ splitHue?: boolean;
1478
+ /**
1479
+ * Base name for the theme-level hue var (`--{name}-hue`). Default: `'theme'`.
1480
+ * Must not equal the name of a color that sets its own hue (throws).
1481
+ */
1482
+ name?: string;
1483
+ /**
1484
+ * Add the HDR tier as `result.hdr` (see {@link GlazeCssResult.hdr}).
1485
+ * Default: the global `modes.hdr` (false). `css()` has no `modes` — it
1486
+ * always emits all four schemes — so this is its HDR switch.
1487
+ */
1488
+ hdr?: boolean;
916
1489
  }
917
- /** CSS custom property declarations grouped by scheme variant. */
918
- interface GlazeCssResult {
1490
+ /** CSS custom property declarations for each scheme variant. */
1491
+ interface GlazeCssSchemeBlocks {
919
1492
  light: string;
920
1493
  dark: string;
921
1494
  lightContrast: string;
922
1495
  darkContrast: string;
923
1496
  }
1497
+ /** CSS custom property declarations grouped by scheme variant. */
1498
+ interface GlazeCssResult extends GlazeCssSchemeBlocks {
1499
+ /**
1500
+ * The HDR tier, present when HDR output is on — the `hdr` option, which
1501
+ * defaults to the global `modes.hdr`: one block per scheme,
1502
+ * each re-declaring every color that has an HDR value in any scheme — its
1503
+ * HDR value (`color(srgb-linear R G B)`) where that scheme has one, and
1504
+ * the scheme's SDR declaration where it has none. The blocks are dense so
1505
+ * a light HDR value can never leak into dark: wire each one after the SDR
1506
+ * blocks, under the same selector plus `(dynamic-range: high)`. Every block
1507
+ * is an empty string when no color has an HDR value.
1508
+ */
1509
+ hdr?: GlazeCssSchemeBlocks;
1510
+ }
1511
+ /**
1512
+ * Color space used for DTCG `$value` color objects.
1513
+ * @see https://www.designtokens.org/
1514
+ */
1515
+ type DtcgColorSpace = 'srgb' | 'oklch';
1516
+ /**
1517
+ * A DTCG color `$value` in the sRGB color space: gamma sRGB components in
1518
+ * 0–1 plus a 6-digit `hex` hint. Universally understood by Figma, Tokens
1519
+ * Studio, Style Dictionary, and every DTCG reader.
1520
+ */
1521
+ interface DtcgSrgbColorValue {
1522
+ colorSpace: 'srgb';
1523
+ components: [number, number, number];
1524
+ hex: HexColor;
1525
+ /** Opacity 0–1. Omitted when fully opaque (alpha = 1). */
1526
+ alpha?: number;
1527
+ }
1528
+ /**
1529
+ * A DTCG color `$value` in the OKLCH color space: `[L, C, H]` components
1530
+ * (L/C: 0–1, H: degrees). Wide-gamut and Glaze-native; no `hex` is emitted.
1531
+ */
1532
+ interface DtcgOklchColorValue {
1533
+ colorSpace: 'oklch';
1534
+ components: [number, number, number];
1535
+ /** Opacity 0–1. Omitted when fully opaque (alpha = 1). */
1536
+ alpha?: number;
1537
+ }
1538
+ /** A DTCG `$value` for a color token, in either supported color space. */
1539
+ type DtcgColorValue = DtcgSrgbColorValue | DtcgOklchColorValue;
1540
+ /**
1541
+ * The HDR value of a glow past SDR white, carried in a DTCG token's
1542
+ * `$extensions['me.tenphi.glaze'].hdr` (`$value` stays the SDR color, since
1543
+ * DTCG color components stop at 1). Maps onto native APIs: UIKit / AppKit
1544
+ * `exposure`, SwiftUI `exposureAdjust(exposure)` on `base`, or Android
1545
+ * `Color.pack(…components, LINEAR_EXTENDED_SRGB)` from `value`.
1546
+ */
1547
+ interface GlazeDtcgHdr {
1548
+ /** Gain over `base` in stops (`log2` of the linear factor), above 0. */
1549
+ exposure: number;
1550
+ /**
1551
+ * Luminance tone of the HDR value (e.g. `112`) — past 100 when the headroom
1552
+ * covers the step; a saturated base capped by `headroom` can land below 100,
1553
+ * though its brightest channel still exceeds 1.
1554
+ */
1555
+ tone: number;
1556
+ /** The opaque base (what SDR paints), as a DTCG sRGB color. */
1557
+ base: DtcgSrgbColorValue;
1558
+ /**
1559
+ * The HDR color: linear sRGB `base × 2^exposure`, its brightest channel
1560
+ * above 1 (the others may stay below).
1561
+ */
1562
+ value: {
1563
+ colorSpace: 'srgb-linear';
1564
+ components: [number, number, number];
1565
+ };
1566
+ }
1567
+ /** The Glaze namespace of a DTCG token's `$extensions`. */
1568
+ interface GlazeDtcgExtensions {
1569
+ 'me.tenphi.glaze': {
1570
+ /** Present only with `modes.hdr`, on a glow variant that has an HDR value. */hdr: GlazeDtcgHdr;
1571
+ };
1572
+ }
1573
+ /**
1574
+ * A single W3C DTCG color token: `{ $type: 'color', $value }`.
1575
+ * `$description` is optional and not populated by Glaze today. `$extensions`
1576
+ * is set only with `modes.hdr`, on a glow variant with an HDR value.
1577
+ */
1578
+ interface DtcgColorToken {
1579
+ $type: 'color';
1580
+ $value: DtcgColorValue;
1581
+ $description?: string;
1582
+ $extensions?: GlazeDtcgExtensions;
1583
+ }
1584
+ /**
1585
+ * A W3C Design Tokens Format Module (2025.10) token tree for one scheme.
1586
+ * Glaze emits one document per scheme variant — the most tool-compatible
1587
+ * convention (one file per Style Dictionary theme / Tokens Studio set /
1588
+ * Figma variable mode).
1589
+ */
1590
+ type DtcgDocument = Record<string, DtcgColorToken>;
1591
+ /** DTCG token documents grouped by scheme variant. Light is always present. */
1592
+ interface GlazeDtcgResult {
1593
+ light: DtcgDocument;
1594
+ dark?: DtcgDocument;
1595
+ lightContrast?: DtcgDocument;
1596
+ darkContrast?: DtcgDocument;
1597
+ }
1598
+ /** A single DTCG color token grouped by scheme variant (standalone tokens). */
1599
+ interface GlazeColorDtcgResult {
1600
+ light: DtcgColorToken;
1601
+ dark?: DtcgColorToken;
1602
+ lightContrast?: DtcgColorToken;
1603
+ darkContrast?: DtcgColorToken;
1604
+ }
1605
+ /** Options for `theme.dtcg()` / `palette.dtcg()`. */
1606
+ interface GlazeDtcgOptions {
1607
+ /** Override which scheme variants to include. */
1608
+ modes?: GlazeOutputModes;
1609
+ /**
1610
+ * DTCG color space. `'srgb'` (default) emits sRGB components plus a `hex`
1611
+ * hint — universally understood (Figma, Tokens Studio). `'oklch'` emits
1612
+ * OKLCH components with no hex — Glaze-native, wide-gamut.
1613
+ */
1614
+ colorSpace?: DtcgColorSpace;
1615
+ }
1616
+ /**
1617
+ * A node in a DTCG Resolver-Module token tree: either a leaf color token or a
1618
+ * nested group. A flat `DtcgDocument` (top-level color-name keys) is a valid
1619
+ * shallow tree, so Glaze's per-scheme documents slot directly into
1620
+ * `sets.<name>.sources` and `modifiers.<name>.contexts.<ctx>` entries.
1621
+ */
1622
+ interface DtcgTokenTree {
1623
+ [key: string]: DtcgColorToken | DtcgTokenTree;
1624
+ }
1625
+ /** A named set in a resolver document: a list of token-tree sources. */
1626
+ interface DtcgResolverSet {
1627
+ sources: DtcgTokenTree[];
1628
+ }
1629
+ /**
1630
+ * A named modifier (an axis such as `scheme`) in a resolver document.
1631
+ * `default` names the context applied when no override is selected; `contexts`
1632
+ * maps each context name to the token-tree overrides applied for it.
1633
+ */
1634
+ interface DtcgResolverModifier {
1635
+ default: string;
1636
+ contexts: Record<string, DtcgTokenTree[]>;
1637
+ }
1638
+ /** A JSON-pointer `$ref` entry in a resolver document's `resolutionOrder`. */
1639
+ interface DtcgResolverRef {
1640
+ $ref: string;
1641
+ }
1642
+ /**
1643
+ * A W3C DTCG Resolver-Module document. Describes every scheme variant in a
1644
+ * single file as `sets` (base token sources) plus `modifiers` (per-context
1645
+ * overrides) composed in `resolutionOrder`. Consumable by resolver tools such
1646
+ * as Dispersa.
1647
+ * @see https://www.designtokens.org/
1648
+ */
1649
+ interface GlazeDtcgResolverDocument {
1650
+ version: string;
1651
+ sets: Record<string, DtcgResolverSet>;
1652
+ modifiers: Record<string, DtcgResolverModifier>;
1653
+ resolutionOrder: DtcgResolverRef[];
1654
+ }
1655
+ /** Renaming of the four scheme variants as resolver-modifier context names. */
1656
+ interface GlazeDtcgResolverContextNames {
1657
+ light?: string;
1658
+ dark?: string;
1659
+ lightContrast?: string;
1660
+ darkContrast?: string;
1661
+ }
1662
+ /**
1663
+ * Options for `theme.dtcgResolver()` / `palette.dtcgResolver()`. Extends
1664
+ * `GlazeDtcgOptions` so `modes` + `colorSpace` pass through to the underlying
1665
+ * per-scheme `dtcg()` build.
1666
+ */
1667
+ interface GlazeDtcgResolverOptions extends GlazeDtcgOptions {
1668
+ /** Name of the single set holding the default (light) token tree. Default `'base'`. */
1669
+ setName?: string;
1670
+ /**
1671
+ * Name of the modifier describing the scheme axis. Default `'scheme'`
1672
+ * (Glaze's term for the light / dark / high-contrast axis).
1673
+ */
1674
+ modifierName?: string;
1675
+ /**
1676
+ * Override the four context names emitted on the modifier. Defaults to the
1677
+ * Glaze variant keys: `light` / `dark` / `lightContrast` / `darkContrast`.
1678
+ */
1679
+ contextNames?: GlazeDtcgResolverContextNames;
1680
+ /** Resolver document version. Default `'2025.10'`. */
1681
+ version?: string;
1682
+ }
1683
+ /** Options for `glaze.color().dtcgResolver()`. `name` is required. */
1684
+ interface GlazeColorDtcgResolverOptions extends GlazeDtcgResolverOptions {
1685
+ /** Token name keying the color within each token tree. Required. */
1686
+ name: string;
1687
+ }
1688
+ /** Options for `theme.tailwind()` / `palette.tailwind()`. */
1689
+ interface GlazeTailwindOptions {
1690
+ /** Override which scheme variants to include. */
1691
+ modes?: GlazeOutputModes;
1692
+ /** Output color format. Default: 'oklch'. */
1693
+ format?: GlazeColorFormat;
1694
+ /**
1695
+ * CSS custom property namespace, forming `--<namespace><name>`.
1696
+ * Default: `'color-'` → `--color-surface`. (Tailwind v4's `--color-*`
1697
+ * convention, which auto-generates `bg-*` / `text-*` / `border-*`.)
1698
+ *
1699
+ * Named `namespace` rather than `prefix` to avoid clashing with the
1700
+ * palette theme-prefix option on `palette.tailwind()`.
1701
+ */
1702
+ namespace?: string;
1703
+ /**
1704
+ * Selector wrapping the dark-scheme overrides. Default: `'.dark'`.
1705
+ * Pass a media query such as `'@media (prefers-color-scheme: dark)'` to
1706
+ * drive dark mode from the OS preference instead of a class.
1707
+ */
1708
+ darkSelector?: string;
1709
+ /**
1710
+ * Selector wrapping the light high-contrast overrides. Default:
1711
+ * `'.high-contrast'`. The combined dark + high-contrast overrides are
1712
+ * emitted under `${darkSelector}${highContrastSelector}` (e.g.
1713
+ * `.dark.high-contrast`).
1714
+ */
1715
+ highContrastSelector?: string;
1716
+ /**
1717
+ * Selector wrapping the HDR overrides, emitted with `modes.hdr` after every
1718
+ * SDR block. Default: `'@media (dynamic-range: high)'`. Each HDR block is
1719
+ * dense (every color with an HDR value in any emitted scheme, SDR value
1720
+ * where the scheme has none) and is scoped by this selector alone (light),
1721
+ * combined with `darkSelector` (dark), with `highContrastSelector` (light
1722
+ * high contrast), or with both.
1723
+ */
1724
+ hdrSelector?: string;
1725
+ }
1726
+ /** Options for `glaze.color().tailwind()`. `name` is required. */
1727
+ interface GlazeColorTailwindOptions extends GlazeTailwindOptions {
1728
+ /** Custom property base name (without leading `--`). Required. */
1729
+ name: string;
1730
+ }
924
1731
  /** Options for `glaze.palette()` creation. */
925
1732
  interface GlazePaletteOptions {
926
1733
  /**
@@ -937,7 +1744,10 @@ interface GlazePaletteOptions {
937
1744
  */
938
1745
  primary?: string;
939
1746
  }
940
- /** Options shared by palette `tokens()`, `tasty()`, and `css()` exports. */
1747
+ /**
1748
+ * Options shared by every palette export except `json()`: `tokens()`,
1749
+ * `tasty()`, `css()`, `dtcg()`, `dtcgResolver()`, and `tailwind()`.
1750
+ */
941
1751
  interface GlazePaletteExportOptions {
942
1752
  /**
943
1753
  * Prefix mode. `true` uses `"<themeName>-"`, or provide a custom map.
@@ -953,7 +1763,58 @@ interface GlazePaletteExportOptions {
953
1763
  */
954
1764
  primary?: string | false;
955
1765
  }
1766
+ /**
1767
+ * Options for `palette.tasty()`. There is no `name`: each theme's hue var is
1768
+ * named after the theme.
1769
+ */
1770
+ interface GlazePaletteTastyOptions extends GlazeTokenOptions, GlazePaletteExportOptions {
1771
+ /**
1772
+ * Emit hue as a separate custom property, referenced via `var()`.
1773
+ * Requires `format: 'oklch'` (any other format throws) and every color to
1774
+ * be pastel. The theme-level hue var is `--{themeName}-hue`.
1775
+ */
1776
+ splitHue?: boolean;
1777
+ }
1778
+ /**
1779
+ * Options for `palette.css()`. There is no `name`: each theme's hue var is
1780
+ * named after the theme.
1781
+ */
1782
+ interface GlazePaletteCssOptions extends GlazePaletteExportOptions {
1783
+ /** Output color format. Default: 'oklch'. */
1784
+ format?: GlazeColorFormat;
1785
+ /** Suffix appended to each CSS custom property name. Default: '-color'. */
1786
+ suffix?: string;
1787
+ /**
1788
+ * Emit hue as a separate custom property, referenced via `var()`.
1789
+ * Requires `format: 'oklch'` (any other format throws) and every color to
1790
+ * be pastel. The theme-level hue var is `--{themeName}-hue`.
1791
+ */
1792
+ splitHue?: boolean;
1793
+ /**
1794
+ * Add the HDR tier as `result.hdr` (see {@link GlazeCssResult.hdr}).
1795
+ * Default: the global `modes.hdr` (false).
1796
+ */
1797
+ hdr?: boolean;
1798
+ }
956
1799
  interface GlazePalette {
1800
+ /** Theme names in insertion order. */
1801
+ list(): string[];
1802
+ /** Primary theme name, if set at palette creation. */
1803
+ readonly primary: string | undefined;
1804
+ /** Get a theme by name. Returns the live instance held by the palette. */
1805
+ theme(name: string): GlazeTheme | undefined;
1806
+ /**
1807
+ * Shallow copy of the theme map (same instances the palette holds).
1808
+ * Mutating a returned theme affects subsequent palette exports.
1809
+ */
1810
+ themes(): Record<string, GlazeTheme>;
1811
+ /**
1812
+ * Export the palette authoring configuration as a JSON-safe object.
1813
+ * Restorable via `glaze.paletteFrom(...)`. Distinct from `json()`, which
1814
+ * emits resolved color strings. Optional `override` is forwarded to each
1815
+ * nested `theme.export(override)`.
1816
+ */
1817
+ export(override?: GlazeConfigOverride): GlazePaletteExport;
957
1818
  /**
958
1819
  * Export all themes as a flat token map grouped by scheme variant.
959
1820
  * Prefix defaults to `true` — all tokens are prefixed with the theme name.
@@ -968,17 +1829,37 @@ interface GlazePalette {
968
1829
  tokens(options?: GlazeJsonOptions & GlazePaletteExportOptions): Record<string, Record<string, string>>;
969
1830
  /**
970
1831
  * Export all themes as tasty style-to-state bindings.
971
- * Uses `#name` color token keys and state aliases (`''`, `@dark`, etc.).
1832
+ * Uses `#name` color token keys and state aliases (`''`, `'@media(prefers-color-scheme: dark)'`, etc.).
972
1833
  * Prefix defaults to `true`. Inherits the palette-level `primary`.
973
1834
  * @see https://tasty.style/docs
974
1835
  */
975
- tasty(options?: GlazeTokenOptions & GlazePaletteExportOptions): Record<string, Record<string, string>>;
976
- /** Export all themes as plain JSON grouped by theme name. */
977
- json(options?: GlazeJsonOptions & {
978
- prefix?: boolean | Record<string, string>;
979
- }): Record<string, Record<string, Record<string, string>>>;
1836
+ tasty(options?: GlazePaletteTastyOptions): Record<string, Record<string, string>>;
1837
+ /**
1838
+ * Export all themes as plain JSON grouped by theme name. No prefix, primary
1839
+ * alias, or collision handling: each theme keeps its own key.
1840
+ */
1841
+ json(options?: GlazeJsonOptions): Record<string, Record<string, Record<string, string>>>;
980
1842
  /** Export all themes as CSS custom property declarations. */
981
- css(options?: GlazeCssOptions & GlazePaletteExportOptions): GlazeCssResult;
1843
+ css(options?: GlazePaletteCssOptions): GlazeCssResult;
1844
+ /**
1845
+ * Export all themes as W3C DTCG documents, one per scheme variant.
1846
+ * Prefix defaults to `true`. Inherits the palette-level `primary`.
1847
+ * @see https://www.designtokens.org/
1848
+ */
1849
+ dtcg(options?: GlazeDtcgOptions & GlazePaletteExportOptions): GlazeDtcgResult;
1850
+ /**
1851
+ * Export all themes as a single W3C DTCG Resolver-Module document. Prefix
1852
+ * defaults to `true`. Inherits the palette-level `primary`.
1853
+ * @see https://www.designtokens.org/
1854
+ */
1855
+ dtcgResolver(options?: GlazeDtcgResolverOptions & GlazePaletteExportOptions): GlazeDtcgResolverDocument;
1856
+ /**
1857
+ * Export all themes as a Tailwind CSS v4 `@theme` block plus dark /
1858
+ * high-contrast overrides. Returns a single ready-to-paste CSS string.
1859
+ * Prefix defaults to `true`.
1860
+ * @see https://tailwindcss.com/docs/theme
1861
+ */
1862
+ tailwind(options?: GlazeTailwindOptions & GlazePaletteExportOptions): string;
982
1863
  }
983
1864
  //#endregion
984
1865
  //#region src/glaze.d.ts
@@ -992,33 +1873,109 @@ type PaletteInput = Record<string, GlazeTheme>;
992
1873
  * the theme still reacts to later `configure()` calls for fields it
993
1874
  * didn't override.
994
1875
  *
1876
+ * The object form also accepts `darkHue` / `darkSaturation` — a separate
1877
+ * seed pair for the dark and dark-high-contrast schemes. Both default to
1878
+ * their light counterparts, and an explicit `darkSaturation` opts the theme
1879
+ * out of the global `darkDesaturation` reduction.
1880
+ *
995
1881
  * @example
996
1882
  * ```ts
997
1883
  * const primary = glaze(280, 80);
998
1884
  * // or shorthand:
999
1885
  * const primary = glaze({ hue: 280, saturation: 80 });
1886
+ * // with a retuned dark seed:
1887
+ * const primary = glaze({ hue: 280, saturation: 80, darkHue: 268, darkSaturation: 65 });
1000
1888
  * // with config override:
1001
1889
  * const raw = glaze(280, 80, { lightTone: false });
1890
+ * const rawSeed = glaze({ hue: 280, saturation: 80 }, { lightTone: false });
1002
1891
  * ```
1003
1892
  */
1004
- declare function glaze(hueOrOptions: number | {
1005
- hue: number;
1006
- saturation: number;
1007
- }, saturation?: number, config?: GlazeConfigOverride): GlazeTheme;
1893
+ declare function glaze(hue: number, saturation?: number, config?: GlazeConfigOverride): GlazeTheme;
1894
+ declare function glaze(seed: GlazeThemeSeed, config?: GlazeConfigOverride): GlazeTheme;
1895
+ /** @deprecated Pass the config second: `glaze(seed, config)`. */
1896
+ declare function glaze(seed: GlazeThemeSeed, saturation: undefined, config?: GlazeConfigOverride): GlazeTheme;
1008
1897
  declare namespace glaze {
1009
1898
  var configure: (config: GlazeConfig) => void;
1010
1899
  var palette: (themes: PaletteInput, options?: GlazePaletteOptions) => GlazePalette;
1900
+ var themeFrom: (data: GlazeThemeExport) => GlazeTheme;
1011
1901
  var from: (data: GlazeThemeExport) => GlazeTheme;
1012
1902
  var color: (input: GlazeFromInput | GlazeColorInput | GlazeColorValue, config?: GlazeConfigOverride) => GlazeColorToken;
1013
1903
  var shadow: (input: GlazeShadowInput) => ResolvedColorVariant;
1904
+ var glow: (input: GlazeGlowInput) => ResolvedColorVariant;
1014
1905
  var format: (variant: ResolvedColorVariant, colorFormat?: GlazeColorFormat, pastel?: boolean) => string;
1906
+ var formatHdr: (variant: ResolvedColorVariant, hdrFormat?: GlazeHdrFormat) => string | undefined;
1015
1907
  var fromHex: (hex: string) => GlazeTheme;
1016
1908
  var fromRgb: (r: number, g: number, b: number) => GlazeTheme;
1017
1909
  var colorFrom: (data: GlazeColorTokenExport) => GlazeColorToken;
1910
+ var paletteFrom: (data: GlazePaletteExport) => GlazePalette;
1911
+ var isThemeExport: (value: unknown) => value is GlazeThemeExport;
1912
+ var isColorTokenExport: (value: unknown) => value is GlazeColorTokenExport;
1913
+ var isPaletteExport: (value: unknown) => value is GlazePaletteExport;
1018
1914
  var getConfig: () => GlazeConfigResolved;
1019
1915
  var resetConfig: () => void;
1020
1916
  }
1021
1917
  //#endregion
1918
+ //#region src/serialize.d.ts
1919
+ /** Type guard for theme authoring snapshots (prefers `kind`, falls back to shape). */
1920
+ declare function isThemeExport(data: unknown): data is GlazeThemeExport;
1921
+ /** Type guard for color-token authoring snapshots. */
1922
+ declare function isColorTokenExport(data: unknown): data is GlazeColorTokenExport;
1923
+ /** Type guard for palette authoring snapshots. */
1924
+ declare function isPaletteExport(data: unknown): data is GlazePaletteExport;
1925
+ //#endregion
1926
+ //#region src/channels.d.ts
1927
+ interface HueDeclaration {
1928
+ prop: string;
1929
+ value: string;
1930
+ }
1931
+ interface HuePlan {
1932
+ /** CSS `var()` reference spliced into `oklch(L C <hueVar>)`. */
1933
+ hueVar: string;
1934
+ /** When true, emit a full inline color (shadow/glow/mix/achromatic). */
1935
+ inline: boolean;
1936
+ /** Scheme-independent `--*-hue` declarations for this color. */
1937
+ declarations: HueDeclaration[];
1938
+ }
1939
+ interface ChannelCtx {
1940
+ seedHue: number;
1941
+ /**
1942
+ * Dark-scheme seed hue, when the theme authored one. Drives the dark
1943
+ * `--{baseName}-hue` value; falls back to `seedHue`.
1944
+ */
1945
+ darkSeedHue?: number;
1946
+ /** Theme-level hue var base name (without `--` / `-hue`). */
1947
+ baseName: string;
1948
+ /** Token / custom-property name prefix used for hue var naming (`brand-` etc.). */
1949
+ prefix: string;
1950
+ defs: ColorMap;
1951
+ mode: 'theme' | 'standalone';
1952
+ /** Standalone: resolved hue from the primary variant (scheme-independent). */
1953
+ resolvedHue?: number;
1954
+ /**
1955
+ * When false, hue declarations are not emitted (the pass only references
1956
+ * hue vars already declared by a sibling pass). Used by palette primary
1957
+ * unprefixed aliases so they reference the themed `--{themeName}-*-hue`
1958
+ * vars without re-declaring (and colliding with) other themes' base vars.
1959
+ * Defaults to true.
1960
+ */
1961
+ emitDeclarations?: boolean;
1962
+ }
1963
+ //#endregion
1964
+ //#region src/format-guard.d.ts
1965
+ /**
1966
+ * Throw when a non-native Glaze color space is requested for an export that
1967
+ * emits raw CSS or non-Tasty token maps.
1968
+ */
1969
+ declare function assertNativeFormat(format: GlazeColorFormat | undefined, method: string): void;
1970
+ /** The modes `assertAllPastel` reads: which schemes are emitted. */
1971
+ type SchemeModes = Required<Pick<GlazeOutputModes, 'dark' | 'highContrast'>>;
1972
+ /**
1973
+ * Throw when `splitHue` is enabled but any exported color is not pastel.
1974
+ * Hue rotation is only clip-free when chroma is bounded by the hue-independent
1975
+ * safe chroma (`computeSafeChromaOKLCH`).
1976
+ */
1977
+ declare function assertAllPastel(resolved: Map<string, ResolvedColor>, modes: SchemeModes): void;
1978
+ //#endregion
1022
1979
  //#region src/roles.d.ts
1023
1980
  /**
1024
1981
  * Normalize a `RoleInput` (canonical value or alias) into a canonical `Role`.
@@ -1091,7 +2048,11 @@ declare function okhslToOkhst(c: {
1091
2048
  };
1092
2049
  /**
1093
2050
  * Edge adapter: a resolved variant stores canonical tone `t` (0–1). Convert
1094
- * it to the OKHSL `{ h, s, l }` the formatters and luminance pipeline expect.
2051
+ * it to the OKHSL `{ h, s, l }` the formatters and luminance pipeline expect —
2052
+ * `s` and `l` on the same 0–1 factor scale, so `const { h, s, l } =
2053
+ * variantToOkhsl(v)` destructures straight into `formatOkhsl` / `formatRgb` /
2054
+ * `okhslToLinearSrgb` with no rescaling. `alpha` stays on the variant; use
2055
+ * `glaze.format(v, format)` for a string that carries it.
1095
2056
  */
1096
2057
  declare function variantToOkhsl(v: {
1097
2058
  h: number;
@@ -1109,7 +2070,11 @@ declare function variantToOkhsl(v: {
1109
2070
  *
1110
2071
  * Provides bidirectional OKHSL ↔ sRGB conversion, luminance computation
1111
2072
  * for both contrast metrics (WCAG 2 relative luminance and APCA screen
1112
- * luminance `Ys`), and multi-format output (okhsl, rgb, hsl, oklch).
2073
+ * luminance `Ys` — both the exact functions the contrast solver uses, and
2074
+ * both public), strict hex parsing (`parseHex` / `parseHexAlpha` check every
2075
+ * digit), and multi-format output (okhsl, rgb, hsl, oklch, plus the
2076
+ * `color(srgb-linear …)` writer for HDR glow values, whose channels may exceed
2077
+ * 1).
1113
2078
  */
1114
2079
  type Vec3 = [number, number, number];
1115
2080
  /**
@@ -1146,6 +2111,7 @@ declare function relativeLuminanceFromLinearRgb(rgb: [number, number, number]):
1146
2111
  declare function contrastRatioFromLuminance(yA: number, yB: number): number;
1147
2112
  /**
1148
2113
  * Convert OKHSL to gamma-encoded sRGB (clamped to 0–1).
2114
+ * h: 0–360, s: 0–1, l: 0–1. Returns [r, g, b] with each channel 0–1.
1149
2115
  */
1150
2116
  declare function okhslToSrgb(h: number, s: number, l: number, pastel?: boolean): [number, number, number];
1151
2117
  /**
@@ -1154,6 +2120,22 @@ declare function okhslToSrgb(h: number, s: number, l: number, pastel?: boolean):
1154
2120
  * This avoids over/under-estimating luminance for out-of-gamut OKHSL colors.
1155
2121
  */
1156
2122
  declare function gamutClampedLuminance(linearRgb: [number, number, number]): number;
2123
+ /**
2124
+ * Compute APCA screen luminance (`Ys`) from linear sRGB.
2125
+ *
2126
+ * APCA does not use the WCAG piecewise sRGB EOTF; it defines its own
2127
+ * luminance as `0.2126·R^2.4 + 0.7152·G^2.4 + 0.0722·B^2.4` over the
2128
+ * gamma-encoded (display) channels with a simple 2.4 exponent. The APCA
2129
+ * soft-clamp threshold in `apcaContrast` is calibrated against this basis,
2130
+ * so the solver must feed it `Ys`, not WCAG relative luminance. Channels
2131
+ * are gamut-clamped to [0, 1] first, matching `gamutClampedLuminance`.
2132
+ *
2133
+ * This is exactly the luminance Glaze's contrast solver (and its drift
2134
+ * check) feeds `apcaContrast`, so
2135
+ * `apcaContrast(apcaLuminanceFromLinearRgb(text), apcaLuminanceFromLinearRgb(bg))`
2136
+ * reproduces the solver's Lc for a resolved pair.
2137
+ */
2138
+ declare function apcaLuminanceFromLinearRgb(linearRgb: [number, number, number]): number;
1157
2139
  /**
1158
2140
  * Convert OKLab to OKHSL.
1159
2141
  * Input: [L, a, b] where L: 0–1, a/b: roughly -0.5 to 0.5.
@@ -1163,6 +2145,11 @@ declare const oklabToOkhsl: (lab: Vec3, pastel?: boolean) => Vec3;
1163
2145
  /**
1164
2146
  * Convert gamma-encoded sRGB (0–1 per channel) to OKHSL.
1165
2147
  * Returns [h, s, l] where h: 0–360, s: 0–1, l: 0–1.
2148
+ *
2149
+ * A gray reads with saturation 0 (its computed hue is kept). Its OKLab
2150
+ * residue would otherwise come out as a saturation that grows toward white
2151
+ * (`#fefefe` reads 1.6e-5, `rgb(254.999 254.999 254.999)` 1.6e-2), which a
2152
+ * color carries into every scheme and an OKHSL mix reads as a hue.
1166
2153
  */
1167
2154
  declare function srgbToOkhsl(rgb: [number, number, number], pastel?: boolean): [number, number, number];
1168
2155
  /**
@@ -1175,7 +2162,9 @@ declare function srgbToOkhsl(rgb: [number, number, number], pastel?: boolean): [
1175
2162
  declare function hslToSrgb(h: number, s: number, l: number): [number, number, number];
1176
2163
  /**
1177
2164
  * Parse a hex color string (#rgb or #rrggbb) to sRGB [r, g, b] in 0–1 range.
1178
- * Returns null if the string is not a valid hex color.
2165
+ * The leading `#` is optional. Returns null if the string is not a valid hex
2166
+ * color: a wrong length, any character outside `0-9a-f` (case-insensitive),
2167
+ * or a form with alpha.
1179
2168
  *
1180
2169
  * For 8-digit hex (`#rrggbbaa`) and 4-digit hex (`#rgba`) with alpha,
1181
2170
  * use {@link parseHexAlpha}.
@@ -1184,7 +2173,12 @@ declare function parseHex(hex: string): [number, number, number] | null;
1184
2173
  /**
1185
2174
  * Parse a hex color string (#rgb, #rrggbb, #rgba, or #rrggbbaa) to
1186
2175
  * sRGB [r, g, b] in 0–1 range plus an optional alpha (0–1).
1187
- * Returns null if the string is not a valid hex color.
2176
+ * The leading `#` is optional. Returns null if the string is not a valid hex
2177
+ * color: a wrong length or any character outside `0-9a-f`
2178
+ * (case-insensitive).
2179
+ *
2180
+ * Every digit is checked up front: `parseInt` alone stops at the first bad
2181
+ * character, so `'#1z2z3z'` would otherwise read as `#010203`.
1188
2182
  */
1189
2183
  declare function parseHexAlpha(hex: string): {
1190
2184
  rgb: [number, number, number];
@@ -1192,25 +2186,53 @@ declare function parseHexAlpha(hex: string): {
1192
2186
  } | null;
1193
2187
  /**
1194
2188
  * Format OKHSL values as a CSS `okhsl(H S% L%)` string.
1195
- * h: 0–360, s: 0–100, l: 0–100 (percentage scale for s and l).
2189
+ * h: 0–360, s: 0–1, l: 0–1 (factor scale, as returned by every converter).
1196
2190
  */
1197
2191
  declare function formatOkhsl(h: number, s: number, l: number, pastel?: boolean): string;
2192
+ /**
2193
+ * Format OKHST values as a CSS `okhst(H S% T%)` string.
2194
+ * h: 0–360, s: 0–1, t: 0–1 (factor scale, matching `ResolvedColorVariant`).
2195
+ *
2196
+ * Pastel recompute matches `formatOkhsl`: convert via OKLab so external
2197
+ * parsers that only understand non-pastel OKHST render identically.
2198
+ */
2199
+ declare function formatOkhst(h: number, s: number, t: number, pastel?: boolean): string;
1198
2200
  /**
1199
2201
  * Format OKHSL values as a CSS `rgb(R G B)` string.
1200
2202
  * Uses 2 decimal places to avoid 8-bit quantization contrast loss.
1201
- * h: 0–360, s: 0–100, l: 0–100 (percentage scale for s and l).
2203
+ * h: 0–360, s: 0–1, l: 0–1 (factor scale, as returned by every converter).
1202
2204
  */
1203
2205
  declare function formatRgb(h: number, s: number, l: number, pastel?: boolean): string;
1204
2206
  /**
1205
2207
  * Format OKHSL values as a CSS `hsl(H S% L%)` string.
1206
- * h: 0–360, s: 0–100, l: 0–100 (percentage scale for s and l).
2208
+ * h: 0–360, s: 0–1, l: 0–1 (factor scale, as returned by every converter).
1207
2209
  */
1208
2210
  declare function formatHsl(h: number, s: number, l: number, pastel?: boolean): string;
1209
2211
  /**
1210
2212
  * Format OKHSL values as a CSS `oklch(L C H)` string.
1211
- * h: 0–360, s: 0–100, l: 0–100 (percentage scale for s and l).
2213
+ * h: 0–360, s: 0–1, l: 0–1 (factor scale, as returned by every converter).
1212
2214
  */
1213
2215
  declare function formatOklch(h: number, s: number, l: number, pastel?: boolean): string;
2216
+ /**
2217
+ * Format linear sRGB channels as a CSS `color(srgb-linear R G B)` string with
2218
+ * 4 decimals. Unlike the other writers it takes linear light, not OKHSL, and
2219
+ * does not clamp: a channel may exceed 1, which is how an HDR glow value
2220
+ * (`glaze.formatHdr()`) goes past SDR white — `1` is SDR white, `2` one stop
2221
+ * above it. Opaque only; HDR values carry no alpha.
2222
+ */
2223
+ declare function formatSrgbLinear(rgb: [number, number, number]): string;
2224
+ /**
2225
+ * Convert gamma-encoded sRGB channels (0–1) to a 6-digit lowercase hex
2226
+ * string (`#rrggbb`). Channels are clamped to [0,1] and rounded to 8-bit.
2227
+ * Alpha is not encoded here — DTCG carries it as a separate `alpha` field.
2228
+ */
2229
+ declare function srgbToHex(rgb: [number, number, number]): `#${string}`;
2230
+ /**
2231
+ * Convert OKHSL (h: 0–360, s: 0–1, l: 0–1) to OKLCH components `[L, C, H]`.
2232
+ * L: 0–1, C: 0–~0.4 (chroma), H: 0–360 (hue). Shared by `formatOklch` and
2233
+ * the DTCG `oklch` colorSpace exporter so the two never drift apart.
2234
+ */
2235
+ declare function okhslToOklch(h: number, s: number, l: number, pastel?: boolean): [number, number, number];
1214
2236
  //#endregion
1215
- export { type AdaptationMode, type ApcaPreset, type ColorDef, type ColorMap, type ContrastPreset, type ContrastSpec, type ExtremeValue, type FindToneForContrastOptions, type FindToneForContrastResult, type FindValueForMixContrastOptions, type FindValueForMixContrastResult, type GlazeColorCssOptions, type GlazeColorFormat, type GlazeColorInput, type GlazeColorInputExport, type GlazeColorOverrides, type GlazeColorOverridesExport, type GlazeColorToken, type GlazeColorTokenExport, type GlazeColorValue, type GlazeConfig, type GlazeConfigOverride, type GlazeConfigResolved, type GlazeCssOptions, type GlazeCssResult, type GlazeExtendOptions, type GlazeFromInput, type GlazeJsonOptions, type GlazeOutputModes, type GlazePalette, type GlazePaletteExportOptions, type GlazePaletteOptions, type GlazeShadowInput, type GlazeTheme, type GlazeThemeExport, type GlazeTokenOptions, type HCPair, type HexColor, type MinContrast, type MixColorDef, type OkhslColor, type OkhstColor, type OklchColor, type Polarity, REF_EPS, type RegularColorDef, type RelativeValue, type ResolvedColor, type ResolvedColorVariant, type ResolvedContrast, type RgbColor, type Role, type RoleInput, type ShadowColorDef, type ShadowTuning, type ToneValue, type ToneWindow, apcaContrast, contrastRatioFromLuminance, cuspLightness, findToneForContrast, findValueForMixContrast, formatHsl, formatOkhsl, formatOklch, formatRgb, fromTone, gamutClampedLuminance, glaze, hslToSrgb, inferRoleFromName, normalizeRole, okhslToLinearSrgb, okhslToOkhst, okhslToOklab, okhslToSrgb, okhstToOkhsl, oklabToOkhsl, oppositeRole, parseHex, parseHexAlpha, relativeLuminanceFromLinearRgb, resolveApcaTarget, resolveContrastForMode, resolveMinContrast, roleToPolarity, srgbToOkhsl, toTone, toneFromY, variantToOkhsl, yFromTone };
2237
+ export { APCA_HC_ENHANCEMENT, APCA_MAX_LC, APCA_PRESETS, type AdaptationMode, type ApcaPreset, type ChannelCtx, type ColorDef, type ColorMap, type ContrastPreset, type ContrastSpec, type DtcgColorSpace, type DtcgColorToken, type DtcgColorValue, type DtcgDocument, type DtcgOklchColorValue, type DtcgResolverModifier, type DtcgResolverRef, type DtcgResolverSet, type DtcgSrgbColorValue, type DtcgTokenTree, type ExtremeValue, type FindToneForContrastOptions, type FindToneForContrastResult, type FindValueForMixContrastOptions, type FindValueForMixContrastResult, GLAZE_EXPORT_VERSION, type GlazeColorCssOptions, type GlazeColorDtcgResolverOptions, type GlazeColorDtcgResult, type GlazeColorFormat, type GlazeColorInput, type GlazeColorInputExport, type GlazeColorOverrides, type GlazeColorOverridesExport, type GlazeColorTailwindOptions, type GlazeColorToken, type GlazeColorTokenExport, type GlazeColorValue, type GlazeConfig, type GlazeConfigOverride, type GlazeConfigResolved, type GlazeCssOptions, type GlazeCssResult, type GlazeCssSchemeBlocks, type GlazeDtcgExtensions, type GlazeDtcgHdr, type GlazeDtcgOptions, type GlazeDtcgResolverContextNames, type GlazeDtcgResolverDocument, type GlazeDtcgResolverOptions, type GlazeDtcgResult, type GlazeExportKind, type GlazeExportVersion, type GlazeExtendOptions, type GlazeFromInput, type GlazeGlowInput, type GlazeHdrFormat, type GlazeJsonOptions, type GlazeLightingInput, type GlazeOutputModes, type GlazePalette, type GlazePaletteCssOptions, type GlazePaletteExport, type GlazePaletteExportOptions, type GlazePaletteOptions, type GlazePaletteTastyOptions, type GlazeShadowInput, type GlazeTailwindOptions, type GlazeTastyOptions, type GlazeTheme, type GlazeThemeExport, type GlazeThemeSeed, type GlazeTokenOptions, type GlowColorDef, type GlowTuning, type HCPair, type HexColor, type HueDeclaration, type HuePlan, type LightingTuning, type MinContrast, type MixColorDef, type OkhslColor, type OkhstColor, type OklchColor, type Polarity, REF_EPS, type RegularColorDef, type RelativeValue, type ResolvedColor, type ResolvedColorVariant, type ResolvedContrast, type ResolvedHdr, type RgbColor, type Role, type RoleInput, type ShadowColorDef, type ShadowTuning, type ToneValue, type ToneWindow, apcaContrast, apcaLuminanceFromLinearRgb, assertAllPastel, assertNativeFormat, contrastRatioFromLuminance, cuspLightness, findToneForContrast, findValueForMixContrast, formatHsl, formatOkhsl, formatOkhst, formatOklch, formatRgb, formatSrgbLinear, fromTone, gamutClampedLuminance, glaze, hslToSrgb, inferRoleFromName, isColorTokenExport, isPaletteExport, isThemeExport, normalizeRole, okhslToLinearSrgb, okhslToOkhst, okhslToOklab, okhslToOklch, okhslToSrgb, okhstToOkhsl, oklabToOkhsl, oppositeRole, parseHex, parseHexAlpha, relativeLuminanceFromLinearRgb, resolveApcaTarget, resolveContrastForLevel, resolveContrastForMode, resolveMinContrast, roleToPolarity, srgbToHex, srgbToOkhsl, toTone, toneFromY, variantToOkhsl, yFromTone };
1216
2238
  //# sourceMappingURL=index.d.cts.map