@tenphi/glaze 0.0.0-snapshot.99c649d → 0.0.0-snapshot.9dc79fc

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