@tenphi/glaze 0.0.0-snapshot.c84faa6 → 0.0.0-snapshot.cdd8acc
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +339 -47
- package/dist/index.cjs +601 -102
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +233 -29
- package/dist/index.d.mts +233 -29
- package/dist/index.mjs +598 -103
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -22,6 +22,7 @@ Glaze generates robust **light**, **dark**, and **high-contrast** color schemes
|
|
|
22
22
|
|
|
23
23
|
- **OKHSL color space** — perceptually uniform hue and saturation
|
|
24
24
|
- **WCAG 2 contrast solving** — automatic lightness adjustment to meet AA/AAA targets
|
|
25
|
+
- **Mix colors** — blend two colors with OKHSL or sRGB interpolation, opaque or transparent, with optional contrast solving
|
|
25
26
|
- **Shadow colors** — OKHSL-native shadow computation with automatic alpha, fg/bg tinting, and per-scheme adaptation
|
|
26
27
|
- **Light + Dark + High-Contrast** — all schemes from one definition
|
|
27
28
|
- **Per-color hue override** — absolute or relative hue shifts within a theme
|
|
@@ -69,9 +70,9 @@ const danger = primary.extend({ hue: 23 });
|
|
|
69
70
|
const success = primary.extend({ hue: 157 });
|
|
70
71
|
|
|
71
72
|
// Compose into a palette and export
|
|
72
|
-
const palette = glaze.palette({ primary, danger, success });
|
|
73
|
-
const tokens = palette.tokens(
|
|
74
|
-
// → { light: { 'primary-surface': 'okhsl(...)',
|
|
73
|
+
const palette = glaze.palette({ primary, danger, success }, { primary: 'primary' });
|
|
74
|
+
const tokens = palette.tokens();
|
|
75
|
+
// → { light: { 'primary-surface': 'okhsl(...)', 'surface': 'okhsl(...)', ... }, dark: { ... } }
|
|
75
76
|
```
|
|
76
77
|
|
|
77
78
|
## Core Concepts
|
|
@@ -193,6 +194,8 @@ A single value applies to both modes. All control is local and explicit.
|
|
|
193
194
|
'muted': { base: 'surface', lightness: ['-35', '-50'], contrast: ['AA-large', 'AA'] }
|
|
194
195
|
```
|
|
195
196
|
|
|
197
|
+
**Full lightness spectrum in HC mode:** In high-contrast variants, the `lightLightness` and `darkLightness` window constraints are bypassed entirely. Colors can reach the full 0–100 lightness range, maximizing perceivable contrast. Normal (non-HC) variants continue to use the configured windows.
|
|
198
|
+
|
|
196
199
|
## Theme Color Management
|
|
197
200
|
|
|
198
201
|
### Adding Colors
|
|
@@ -262,13 +265,70 @@ Create a single color token without a full theme:
|
|
|
262
265
|
```ts
|
|
263
266
|
const accent = glaze.color({ hue: 280, saturation: 80, lightness: 52, mode: 'fixed' });
|
|
264
267
|
|
|
265
|
-
accent.resolve();
|
|
266
|
-
accent.token();
|
|
267
|
-
accent.tasty();
|
|
268
|
-
accent.json();
|
|
268
|
+
accent.resolve(); // → ResolvedColor with light/dark/lightContrast/darkContrast
|
|
269
|
+
accent.token(); // → { '': 'okhsl(...)', '@dark': 'okhsl(...)' } (tasty format)
|
|
270
|
+
accent.tasty(); // → { '': 'okhsl(...)', '@dark': 'okhsl(...)' } (same as token)
|
|
271
|
+
accent.json(); // → { light: 'okhsl(...)', dark: 'okhsl(...)' }
|
|
272
|
+
accent.css({ name: 'accent' });
|
|
273
|
+
// → { light: '--accent-color: rgb(...);', dark: '--accent-color: rgb(...);', ... }
|
|
269
274
|
```
|
|
270
275
|
|
|
271
|
-
|
|
276
|
+
### Value Shorthand
|
|
277
|
+
|
|
278
|
+
The first argument can also be a color value — Glaze extracts the seed
|
|
279
|
+
hue/saturation/lightness for you. All forms support the same exports
|
|
280
|
+
(`resolve / token / tasty / json / css`):
|
|
281
|
+
|
|
282
|
+
```ts
|
|
283
|
+
// Hex (3 or 6 digits)
|
|
284
|
+
glaze.color('#26fcb2').tasty();
|
|
285
|
+
|
|
286
|
+
// CSS color functions Glaze itself emits (`rgb()`, `hsl()`, `okhsl()`, `oklch()`)
|
|
287
|
+
// — anything from theme.tasty()/json()/css() round-trips back in.
|
|
288
|
+
glaze.color('rgb(38 252 178)').tasty();
|
|
289
|
+
glaze.color('hsl(152 97% 57%)').tasty();
|
|
290
|
+
glaze.color('okhsl(152 95% 74%)').tasty();
|
|
291
|
+
glaze.color('oklch(0.85 0.18 152)').tasty();
|
|
292
|
+
|
|
293
|
+
// OKHSL object — Glaze's native shape (h: 0–360, s/l: 0–1)
|
|
294
|
+
glaze.color({ h: 152, s: 0.95, l: 0.74 }).tasty();
|
|
295
|
+
|
|
296
|
+
// RGB tuple, 0–255 (same range as glaze.fromRgb)
|
|
297
|
+
glaze.color([38, 252, 178]).tasty();
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
Optional second argument supplies overrides — including `base` (any color
|
|
301
|
+
value), the WCAG `contrast` solver, and relative `hue` / `lightness`:
|
|
302
|
+
|
|
303
|
+
```ts
|
|
304
|
+
// Brand color seeded from a hex, with seed/lightness/mode overrides
|
|
305
|
+
glaze.color('#26fcb2', { saturation: 80, mode: 'fixed' }).tasty();
|
|
306
|
+
|
|
307
|
+
// Brand text guaranteed AAA against a brand-tinted dark surface
|
|
308
|
+
glaze.color('#26fcb2', {
|
|
309
|
+
base: '#1a1a2e', // any GlazeColorValue
|
|
310
|
+
lightness: '+48', // relative offset from base lightness
|
|
311
|
+
contrast: 'AAA',
|
|
312
|
+
mode: 'fixed',
|
|
313
|
+
}).tasty();
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
All overrides:
|
|
317
|
+
|
|
318
|
+
| Option | Notes |
|
|
319
|
+
|---|---|
|
|
320
|
+
| `hue` | Number (absolute 0–360) or `'+N'`/`'-N'` (relative to seed) |
|
|
321
|
+
| `saturation` | Override seed saturation (0–100) |
|
|
322
|
+
| `lightness` | Number (absolute 0–100) or `'+N'`/`'-N'` (relative to base — requires `base`). Supports `[normal, hc]` pairs |
|
|
323
|
+
| `saturationFactor` | Multiplier on seed (0–1, default 1) |
|
|
324
|
+
| `mode` | `'auto'` (default) / `'fixed'` / `'static'` — see [Adaptation Modes](#adaptation-modes) |
|
|
325
|
+
| `base` | Any `GlazeColorValue` — required for relative `lightness` and `contrast` |
|
|
326
|
+
| `contrast` | WCAG floor against `base`. Same shape as `RegularColorDef.contrast` |
|
|
327
|
+
|
|
328
|
+
Alpha components in `rgb(... / A)` / `hsl(... / A)` / `rgba(...)` / `hsla(...)` are
|
|
329
|
+
parsed but the alpha channel is dropped with a `console.warn` (standalone
|
|
330
|
+
colors have no opacity field — use `opacity` on a theme color if you need
|
|
331
|
+
alpha). Named CSS colors (`'red'`, `'blueviolet'`) are not supported.
|
|
272
332
|
|
|
273
333
|
## From Existing Colors
|
|
274
334
|
|
|
@@ -413,6 +473,139 @@ const css = glaze.format(v, 'oklch');
|
|
|
413
473
|
}
|
|
414
474
|
```
|
|
415
475
|
|
|
476
|
+
## Mix Colors
|
|
477
|
+
|
|
478
|
+
Mix colors blend two existing colors together. Use them for hover overlays, tints, shades, and any derived color that sits between two reference colors.
|
|
479
|
+
|
|
480
|
+
### Opaque Mix
|
|
481
|
+
|
|
482
|
+
Produces a solid color by interpolating between `base` and `target`:
|
|
483
|
+
|
|
484
|
+
```ts
|
|
485
|
+
theme.colors({
|
|
486
|
+
surface: { lightness: 95 },
|
|
487
|
+
accent: { lightness: 30 },
|
|
488
|
+
|
|
489
|
+
// 30% of the way from surface toward accent
|
|
490
|
+
tint: { type: 'mix', base: 'surface', target: 'accent', value: 30 },
|
|
491
|
+
});
|
|
492
|
+
```
|
|
493
|
+
|
|
494
|
+
- `value` — mix ratio 0–100 (0 = pure base, 100 = pure target)
|
|
495
|
+
- The result is a fully opaque color (alpha = 1)
|
|
496
|
+
- Adapts to light/dark/HC schemes automatically via the resolved base and target
|
|
497
|
+
|
|
498
|
+
### Transparent Mix
|
|
499
|
+
|
|
500
|
+
Produces the target color with a controlled opacity — useful for hover overlays:
|
|
501
|
+
|
|
502
|
+
```ts
|
|
503
|
+
theme.colors({
|
|
504
|
+
surface: { lightness: 95 },
|
|
505
|
+
black: { lightness: 0, saturation: 0 },
|
|
506
|
+
|
|
507
|
+
hover: {
|
|
508
|
+
type: 'mix',
|
|
509
|
+
base: 'surface',
|
|
510
|
+
target: 'black',
|
|
511
|
+
value: 8,
|
|
512
|
+
blend: 'transparent',
|
|
513
|
+
},
|
|
514
|
+
});
|
|
515
|
+
// hover → target color (black) with alpha = 0.08
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
The output color has `h`, `s`, `l` from the target and `alpha = value / 100`.
|
|
519
|
+
|
|
520
|
+
### Blend Space
|
|
521
|
+
|
|
522
|
+
By default, opaque mixing interpolates in OKHSL (perceptually uniform, consistent with Glaze's model). Use `space: 'srgb'` for linear sRGB interpolation, which matches browser compositing:
|
|
523
|
+
|
|
524
|
+
```ts
|
|
525
|
+
theme.colors({
|
|
526
|
+
surface: { lightness: 95 },
|
|
527
|
+
accent: { lightness: 30 },
|
|
528
|
+
|
|
529
|
+
// sRGB blend — matches what the browser would render
|
|
530
|
+
hover: { type: 'mix', base: 'surface', target: 'accent', value: 20, space: 'srgb' },
|
|
531
|
+
});
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
| Space | Behavior | Best for |
|
|
535
|
+
|---|---|---|
|
|
536
|
+
| `'okhsl'` (default) | Perceptually uniform OKHSL interpolation | Design token derivation |
|
|
537
|
+
| `'srgb'` | Linear sRGB channel interpolation | Matching browser compositing |
|
|
538
|
+
|
|
539
|
+
The `space` option only affects opaque blending. Transparent blending always composites in linear sRGB (matching browser alpha compositing).
|
|
540
|
+
|
|
541
|
+
### Contrast Solving
|
|
542
|
+
|
|
543
|
+
Mix colors support the same `contrast` prop as regular colors. The solver adjusts the mix ratio (opaque) or opacity (transparent) to meet the WCAG target:
|
|
544
|
+
|
|
545
|
+
```ts
|
|
546
|
+
theme.colors({
|
|
547
|
+
surface: { lightness: 95 },
|
|
548
|
+
accent: { lightness: 30 },
|
|
549
|
+
|
|
550
|
+
// Ensure the mixed color has at least AA contrast against surface
|
|
551
|
+
tint: {
|
|
552
|
+
type: 'mix',
|
|
553
|
+
base: 'surface',
|
|
554
|
+
target: 'accent',
|
|
555
|
+
value: 10,
|
|
556
|
+
contrast: 'AA',
|
|
557
|
+
},
|
|
558
|
+
|
|
559
|
+
// Ensure the transparent overlay has at least 3:1 contrast
|
|
560
|
+
overlay: {
|
|
561
|
+
type: 'mix',
|
|
562
|
+
base: 'surface',
|
|
563
|
+
target: 'accent',
|
|
564
|
+
value: 5,
|
|
565
|
+
blend: 'transparent',
|
|
566
|
+
contrast: 3,
|
|
567
|
+
},
|
|
568
|
+
});
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
### High-Contrast Pairs
|
|
572
|
+
|
|
573
|
+
Both `value` and `contrast` support `[normal, highContrast]` pairs:
|
|
574
|
+
|
|
575
|
+
```ts
|
|
576
|
+
theme.colors({
|
|
577
|
+
surface: { lightness: 95 },
|
|
578
|
+
accent: { lightness: 30 },
|
|
579
|
+
|
|
580
|
+
tint: {
|
|
581
|
+
type: 'mix',
|
|
582
|
+
base: 'surface',
|
|
583
|
+
target: 'accent',
|
|
584
|
+
value: [20, 40], // stronger mix in high-contrast mode
|
|
585
|
+
contrast: [3, 'AAA'], // stricter contrast in high-contrast mode
|
|
586
|
+
},
|
|
587
|
+
});
|
|
588
|
+
```
|
|
589
|
+
|
|
590
|
+
### Achromatic Colors
|
|
591
|
+
|
|
592
|
+
When mixing with achromatic colors (saturation near zero, e.g., white or black) in `okhsl` space, the hue comes from whichever color has saturation. This prevents meaningless hue artifacts and matches CSS `color-mix()` "missing component" behavior. For purely achromatic mixes, prefer `space: 'srgb'` where hue is irrelevant.
|
|
593
|
+
|
|
594
|
+
### Mix Chaining
|
|
595
|
+
|
|
596
|
+
Mix colors can reference other mix colors, enabling multi-step derivations:
|
|
597
|
+
|
|
598
|
+
```ts
|
|
599
|
+
theme.colors({
|
|
600
|
+
white: { lightness: 100, saturation: 0 },
|
|
601
|
+
black: { lightness: 0, saturation: 0 },
|
|
602
|
+
gray: { type: 'mix', base: 'white', target: 'black', value: 50, space: 'srgb' },
|
|
603
|
+
lightGray: { type: 'mix', base: 'white', target: 'gray', value: 50, space: 'srgb' },
|
|
604
|
+
});
|
|
605
|
+
```
|
|
606
|
+
|
|
607
|
+
Mix colors cannot reference shadow colors (same restriction as regular dependent colors).
|
|
608
|
+
|
|
416
609
|
## Output Formats
|
|
417
610
|
|
|
418
611
|
Control the color format in exports with the `format` option:
|
|
@@ -431,7 +624,7 @@ theme.tokens({ format: 'hsl' }); // → 'hsl(270.5 45.2% 95.8%)'
|
|
|
431
624
|
theme.tokens({ format: 'oklch' }); // → 'oklch(0.965 0.0123 280)'
|
|
432
625
|
```
|
|
433
626
|
|
|
434
|
-
The `format` option works on all export methods: `theme.tokens()`, `theme.tasty()`, `theme.json()`, `theme.css()`, `palette.tokens()`, `palette.tasty()`, `palette.json()`, `palette.css()`, and standalone `glaze.color().token()` / `.tasty()` / `.json()`.
|
|
627
|
+
The `format` option works on all export methods: `theme.tokens()`, `theme.tasty()`, `theme.json()`, `theme.css()`, `palette.tokens()`, `palette.tasty()`, `palette.json()`, `palette.css()`, and standalone `glaze.color().token()` / `.tasty()` / `.json()` / `.css()`.
|
|
435
628
|
|
|
436
629
|
Colors with `alpha < 1` (shadow colors, or regular colors with `opacity`) include an alpha component:
|
|
437
630
|
|
|
@@ -467,7 +660,7 @@ Modes control how colors adapt across schemes:
|
|
|
467
660
|
|
|
468
661
|
```ts
|
|
469
662
|
// Light: surface L=97, text lightness='-52' → L=45 (dark text on light bg)
|
|
470
|
-
// Dark: surface inverts to L≈
|
|
663
|
+
// Dark: surface inverts to L≈20 (Möbius curve), sign flips → L=20+52=72
|
|
471
664
|
// contrast solver may push further (light text on dark bg)
|
|
472
665
|
```
|
|
473
666
|
|
|
@@ -484,14 +677,14 @@ Modes control how colors adapt across schemes:
|
|
|
484
677
|
|
|
485
678
|
### Lightness
|
|
486
679
|
|
|
487
|
-
|
|
680
|
+
Absolute lightness values (both root colors and dependent colors with absolute lightness) are mapped linearly within the configured `lightLightness` window:
|
|
488
681
|
|
|
489
682
|
```ts
|
|
490
683
|
const [lo, hi] = lightLightness; // default: [10, 100]
|
|
491
684
|
const mappedL = (lightness * (hi - lo)) / 100 + lo;
|
|
492
685
|
```
|
|
493
686
|
|
|
494
|
-
Both `auto` and `fixed` modes use the same linear formula. `static` mode
|
|
687
|
+
Both `auto` and `fixed` modes use the same linear formula. `static` mode and high-contrast variants bypass the mapping entirely (identity: `mappedL = l`).
|
|
495
688
|
|
|
496
689
|
| Color | Raw L | Mapped L (default [10, 100]) |
|
|
497
690
|
|---|---|---|
|
|
@@ -503,24 +696,29 @@ Both `auto` and `fixed` modes use the same linear formula. `static` mode bypasse
|
|
|
503
696
|
|
|
504
697
|
### Lightness
|
|
505
698
|
|
|
506
|
-
**`auto`** — inverted within the configured window:
|
|
699
|
+
**`auto`** — inverted with a Möbius transformation within the configured window:
|
|
507
700
|
|
|
508
701
|
```ts
|
|
509
702
|
const [lo, hi] = darkLightness; // default: [15, 95]
|
|
510
|
-
const
|
|
703
|
+
const t = (100 - lightness) / 100;
|
|
704
|
+
const invertedL = lo + (hi - lo) * t / (t + darkCurve * (1 - t)); // darkCurve default: 0.5
|
|
511
705
|
```
|
|
512
706
|
|
|
513
|
-
|
|
707
|
+
The `darkCurve` parameter (default `0.5`, range 0–1) controls how much the dark-mode inversion expands lightness deltas. Lower values produce stronger expansion; `1` gives linear (legacy) behavior. Accepts a `[normal, highContrast]` pair for separate HC tuning (e.g. `darkCurve: [0.5, 0.3]`); a single number applies to both. Unlike a power curve, the Möbius transformation provides **proportional expansion** — small and large deltas are scaled by similar ratios, preserving the visual hierarchy of the light theme.
|
|
708
|
+
|
|
709
|
+
**`fixed`** — mapped without inversion (not affected by `darkCurve`):
|
|
514
710
|
|
|
515
711
|
```ts
|
|
516
712
|
const mappedL = (lightness * (hi - lo)) / 100 + lo;
|
|
517
713
|
```
|
|
518
714
|
|
|
519
|
-
| Color | Light L | Auto (
|
|
520
|
-
|
|
521
|
-
| surface (L=97) | 97 | 17.4 | 92.6 |
|
|
522
|
-
| accent-fill (L=52) | 52 | 53.4 | 56.6 |
|
|
523
|
-
| accent-text (L=100) | 100 | 15 | 95 |
|
|
715
|
+
| Color | Light L | Auto (curve=0.5) | Auto (curve=1, linear) | Fixed (mapped) |
|
|
716
|
+
|---|---|---|---|---|
|
|
717
|
+
| surface (L=97) | 97 | 19.7 | 17.4 | 92.6 |
|
|
718
|
+
| accent-fill (L=52) | 52 | 66.9 | 53.4 | 56.6 |
|
|
719
|
+
| accent-text (L=100) | 100 | 15 | 15 | 95 |
|
|
720
|
+
|
|
721
|
+
In high-contrast variants, the `darkLightness` window is bypassed — auto uses the Möbius curve over the full [0, 100] range, and fixed uses identity (`L`). To use a different curve shape for HC, pass a `[normal, hc]` pair to `darkCurve` (e.g. `darkCurve: [0.5, 0.3]`).
|
|
524
722
|
|
|
525
723
|
### Saturation
|
|
526
724
|
|
|
@@ -560,12 +758,21 @@ Combine multiple themes into a single palette:
|
|
|
560
758
|
const palette = glaze.palette({ primary, danger, success, warning });
|
|
561
759
|
```
|
|
562
760
|
|
|
563
|
-
|
|
761
|
+
Optionally designate a primary theme at creation time:
|
|
762
|
+
|
|
763
|
+
```ts
|
|
764
|
+
const palette = glaze.palette(
|
|
765
|
+
{ primary, danger, success, warning },
|
|
766
|
+
{ primary: 'primary' },
|
|
767
|
+
);
|
|
768
|
+
```
|
|
769
|
+
|
|
770
|
+
### Prefix Behavior
|
|
564
771
|
|
|
565
|
-
|
|
772
|
+
Palette export methods (`tokens()`, `tasty()`, `css()`) default to `prefix: true` — all tokens are automatically prefixed with the theme name to avoid collisions:
|
|
566
773
|
|
|
567
774
|
```ts
|
|
568
|
-
const tokens = palette.tokens(
|
|
775
|
+
const tokens = palette.tokens();
|
|
569
776
|
// → {
|
|
570
777
|
// light: { 'primary-surface': 'okhsl(...)', 'danger-surface': 'okhsl(...)' },
|
|
571
778
|
// dark: { 'primary-surface': 'okhsl(...)', 'danger-surface': 'okhsl(...)' },
|
|
@@ -578,15 +785,68 @@ Custom prefix mapping:
|
|
|
578
785
|
palette.tokens({ prefix: { primary: 'brand-', danger: 'error-' } });
|
|
579
786
|
```
|
|
580
787
|
|
|
581
|
-
|
|
788
|
+
To disable prefixing entirely, pass `prefix: false` explicitly.
|
|
789
|
+
|
|
790
|
+
### Collision Detection
|
|
791
|
+
|
|
792
|
+
When two themes produce the same output key (via `prefix: false`, custom prefix maps, or primary unprefixed aliases), the first-written value wins and a `console.warn` is emitted:
|
|
793
|
+
|
|
794
|
+
```ts
|
|
795
|
+
const palette = glaze.palette({ a, b });
|
|
796
|
+
palette.tokens({ prefix: false });
|
|
797
|
+
// ⚠ glaze: token "surface" from theme "b" collides with theme "a" — skipping.
|
|
798
|
+
```
|
|
799
|
+
|
|
800
|
+
### Primary Theme
|
|
801
|
+
|
|
802
|
+
The primary theme's tokens are duplicated without prefix, providing convenient short aliases alongside the prefixed versions. Set at palette creation to apply to all exports automatically:
|
|
803
|
+
|
|
804
|
+
```ts
|
|
805
|
+
const palette = glaze.palette(
|
|
806
|
+
{ primary, danger, success },
|
|
807
|
+
{ primary: 'primary' },
|
|
808
|
+
);
|
|
809
|
+
const tokens = palette.tokens();
|
|
810
|
+
// → {
|
|
811
|
+
// light: {
|
|
812
|
+
// 'primary-surface': 'okhsl(...)', // prefixed (all themes)
|
|
813
|
+
// 'danger-surface': 'okhsl(...)',
|
|
814
|
+
// 'success-surface': 'okhsl(...)',
|
|
815
|
+
// 'surface': 'okhsl(...)', // unprefixed alias (primary only)
|
|
816
|
+
// },
|
|
817
|
+
// }
|
|
818
|
+
```
|
|
819
|
+
|
|
820
|
+
Override or disable per-export:
|
|
582
821
|
|
|
583
|
-
|
|
822
|
+
```ts
|
|
823
|
+
palette.tokens({ primary: 'danger' }); // use danger as primary for this call
|
|
824
|
+
palette.tokens({ primary: false }); // no primary for this call
|
|
825
|
+
```
|
|
826
|
+
|
|
827
|
+
The `primary` option works on `tokens()`, `tasty()`, and `css()`. It combines with any prefix mode — when using a custom prefix map, primary tokens are still duplicated without prefix:
|
|
828
|
+
|
|
829
|
+
```ts
|
|
830
|
+
palette.tokens({ prefix: { primary: 'p-', danger: 'd-' } });
|
|
831
|
+
// → 'p-surface' + 'surface' (alias from palette-level primary) + 'd-surface'
|
|
832
|
+
```
|
|
833
|
+
|
|
834
|
+
An error is thrown if the primary name doesn't match any theme in the palette.
|
|
835
|
+
|
|
836
|
+
### Tasty Export (for [Tasty](https://tasty.style) style system)
|
|
837
|
+
|
|
838
|
+
The `tasty()` method exports tokens in the [Tasty](https://tasty.style/docs) style-to-state binding format — `#name` color token keys with state aliases (`''`, `@dark`, etc.). See the [Playground](https://tasty.style/playground) for live examples of Glaze integration:
|
|
584
839
|
|
|
585
840
|
```ts
|
|
586
|
-
const
|
|
841
|
+
const palette = glaze.palette(
|
|
842
|
+
{ primary, danger, success },
|
|
843
|
+
{ primary: 'primary' },
|
|
844
|
+
);
|
|
845
|
+
const tastyTokens = palette.tasty();
|
|
587
846
|
// → {
|
|
588
847
|
// '#primary-surface': { '': 'okhsl(...)', '@dark': 'okhsl(...)' },
|
|
589
848
|
// '#danger-surface': { '': 'okhsl(...)', '@dark': 'okhsl(...)' },
|
|
849
|
+
// '#surface': { '': 'okhsl(...)', '@dark': 'okhsl(...)' }, // alias
|
|
590
850
|
// }
|
|
591
851
|
```
|
|
592
852
|
|
|
@@ -653,8 +913,10 @@ palette.tasty({ states: { dark: '@dark', highContrast: '@hc' } });
|
|
|
653
913
|
|
|
654
914
|
### JSON Export (Framework-Agnostic)
|
|
655
915
|
|
|
916
|
+
JSON export groups by theme name (no prefix needed):
|
|
917
|
+
|
|
656
918
|
```ts
|
|
657
|
-
const data = palette.json(
|
|
919
|
+
const data = palette.json();
|
|
658
920
|
// → {
|
|
659
921
|
// primary: { surface: { light: 'okhsl(...)', dark: 'okhsl(...)' } },
|
|
660
922
|
// danger: { surface: { light: 'okhsl(...)', dark: 'okhsl(...)' } },
|
|
@@ -676,7 +938,11 @@ const css = theme.css();
|
|
|
676
938
|
Use in a stylesheet:
|
|
677
939
|
|
|
678
940
|
```ts
|
|
679
|
-
const
|
|
941
|
+
const palette = glaze.palette(
|
|
942
|
+
{ primary, danger, success },
|
|
943
|
+
{ primary: 'primary' },
|
|
944
|
+
);
|
|
945
|
+
const css = palette.css();
|
|
680
946
|
|
|
681
947
|
const stylesheet = `
|
|
682
948
|
:root { ${css.light} }
|
|
@@ -692,7 +958,8 @@ Options:
|
|
|
692
958
|
|---|---|---|
|
|
693
959
|
| `format` | `'rgb'` | Color format (`'rgb'`, `'hsl'`, `'okhsl'`, `'oklch'`) |
|
|
694
960
|
| `suffix` | `'-color'` | Suffix appended to each CSS property name |
|
|
695
|
-
| `prefix` |
|
|
961
|
+
| `prefix` | `true` (palette) | (palette only) `true` uses `"<themeName>-"`, or provide a custom map |
|
|
962
|
+
| `primary` | inherited | (palette only) Override or disable (`false`) the palette-level primary for this call |
|
|
696
963
|
|
|
697
964
|
```ts
|
|
698
965
|
// Custom suffix
|
|
@@ -703,9 +970,9 @@ theme.css({ suffix: '' });
|
|
|
703
970
|
theme.css({ format: 'hsl' });
|
|
704
971
|
// → "--surface-color: hsl(...);"
|
|
705
972
|
|
|
706
|
-
// Palette with
|
|
707
|
-
palette.css(
|
|
708
|
-
// → "--primary-surface-color: rgb(...);\n--danger-surface-color: rgb(...);"
|
|
973
|
+
// Palette with primary (inherited from palette creation)
|
|
974
|
+
palette.css();
|
|
975
|
+
// → "--primary-surface-color: rgb(...);\n--surface-color: rgb(...);\n--danger-surface-color: rgb(...);"
|
|
709
976
|
```
|
|
710
977
|
|
|
711
978
|
## Output Modes
|
|
@@ -738,9 +1005,10 @@ Resolution priority (highest first):
|
|
|
738
1005
|
|
|
739
1006
|
```ts
|
|
740
1007
|
glaze.configure({
|
|
741
|
-
lightLightness: [10, 100], // Light scheme lightness window [lo, hi]
|
|
742
|
-
darkLightness: [15, 95], // Dark scheme lightness window [lo, hi]
|
|
1008
|
+
lightLightness: [10, 100], // Light scheme lightness window [lo, hi] (bypassed in HC)
|
|
1009
|
+
darkLightness: [15, 95], // Dark scheme lightness window [lo, hi] (bypassed in HC)
|
|
743
1010
|
darkDesaturation: 0.1, // Saturation reduction in dark scheme (0–1)
|
|
1011
|
+
darkCurve: 0.5, // Möbius beta for dark auto-inversion (0–1); or [normal, hc] pair
|
|
744
1012
|
states: {
|
|
745
1013
|
dark: '@dark', // State alias for dark mode tokens
|
|
746
1014
|
highContrast: '@high-contrast',
|
|
@@ -758,10 +1026,10 @@ glaze.configure({
|
|
|
758
1026
|
|
|
759
1027
|
## Color Definition Shape
|
|
760
1028
|
|
|
761
|
-
`ColorDef` is a discriminated union of regular colors and
|
|
1029
|
+
`ColorDef` is a discriminated union of regular colors, shadow colors, and mix colors:
|
|
762
1030
|
|
|
763
1031
|
```ts
|
|
764
|
-
type ColorDef = RegularColorDef | ShadowColorDef;
|
|
1032
|
+
type ColorDef = RegularColorDef | ShadowColorDef | MixColorDef;
|
|
765
1033
|
|
|
766
1034
|
interface RegularColorDef {
|
|
767
1035
|
lightness?: HCPair<number | RelativeValue>;
|
|
@@ -780,9 +1048,19 @@ interface ShadowColorDef {
|
|
|
780
1048
|
intensity: HCPair<number>; // 0–100
|
|
781
1049
|
tuning?: ShadowTuning;
|
|
782
1050
|
}
|
|
1051
|
+
|
|
1052
|
+
interface MixColorDef {
|
|
1053
|
+
type: 'mix';
|
|
1054
|
+
base: string; // "from" color name
|
|
1055
|
+
target: string; // "to" color name
|
|
1056
|
+
value: HCPair<number>; // 0–100 (mix ratio or opacity)
|
|
1057
|
+
blend?: 'opaque' | 'transparent'; // default: 'opaque'
|
|
1058
|
+
space?: 'okhsl' | 'srgb'; // default: 'okhsl'
|
|
1059
|
+
contrast?: HCPair<MinContrast>;
|
|
1060
|
+
}
|
|
783
1061
|
```
|
|
784
1062
|
|
|
785
|
-
A root color must have absolute `lightness` (a number). A dependent color must have `base`. Relative `lightness` (a string) requires `base`. Shadow colors use `type: 'shadow'` and must reference a non-shadow `bg` color.
|
|
1063
|
+
A root color must have absolute `lightness` (a number). A dependent color must have `base`. Relative `lightness` (a string) requires `base`. Shadow colors use `type: 'shadow'` and must reference a non-shadow `bg` color. Mix colors use `type: 'mix'` and must reference two non-shadow colors.
|
|
786
1064
|
|
|
787
1065
|
## Validation
|
|
788
1066
|
|
|
@@ -801,6 +1079,12 @@ A root color must have absolute `lightness` (a number). A dependent color must h
|
|
|
801
1079
|
| Regular color `base` references a shadow color | Validation error |
|
|
802
1080
|
| Shadow `intensity` outside 0–100 | Clamp silently |
|
|
803
1081
|
| `contrast` + `opacity` combined | Warning |
|
|
1082
|
+
| Mix `base` references non-existent color | Validation error |
|
|
1083
|
+
| Mix `target` references non-existent color | Validation error |
|
|
1084
|
+
| Mix `base` references a shadow color | Validation error |
|
|
1085
|
+
| Mix `target` references a shadow color | Validation error |
|
|
1086
|
+
| Mix `value` outside 0–100 | Clamp silently |
|
|
1087
|
+
| Circular references involving mix colors | Validation error |
|
|
804
1088
|
|
|
805
1089
|
## Advanced: Color Math Utilities
|
|
806
1090
|
|
|
@@ -846,6 +1130,10 @@ primary.colors({
|
|
|
846
1130
|
'shadow-md': { type: 'shadow', bg: 'surface', fg: 'text', intensity: 10 },
|
|
847
1131
|
'shadow-lg': { type: 'shadow', bg: 'surface', fg: 'text', intensity: 20 },
|
|
848
1132
|
|
|
1133
|
+
// Mix colors — hover overlays and tints
|
|
1134
|
+
'hover': { type: 'mix', base: 'surface', target: 'accent-fill', value: 8, blend: 'transparent' },
|
|
1135
|
+
'tint': { type: 'mix', base: 'surface', target: 'accent-fill', value: 20 },
|
|
1136
|
+
|
|
849
1137
|
// Fixed-alpha overlay
|
|
850
1138
|
overlay: { lightness: 0, opacity: 0.5 },
|
|
851
1139
|
});
|
|
@@ -855,18 +1143,21 @@ const success = primary.extend({ hue: 157 });
|
|
|
855
1143
|
const warning = primary.extend({ hue: 84 });
|
|
856
1144
|
const note = primary.extend({ hue: 302 });
|
|
857
1145
|
|
|
858
|
-
const palette = glaze.palette(
|
|
1146
|
+
const palette = glaze.palette(
|
|
1147
|
+
{ primary, danger, success, warning, note },
|
|
1148
|
+
{ primary: 'primary' },
|
|
1149
|
+
);
|
|
859
1150
|
|
|
860
|
-
// Export as flat token map grouped by variant
|
|
861
|
-
const tokens = palette.tokens(
|
|
862
|
-
// tokens.light → { 'primary-surface': '
|
|
1151
|
+
// Export as flat token map grouped by variant (prefix defaults to true)
|
|
1152
|
+
const tokens = palette.tokens();
|
|
1153
|
+
// tokens.light → { 'primary-surface': '...', 'surface': '...', 'danger-surface': '...' }
|
|
863
1154
|
|
|
864
1155
|
// Export as tasty style-to-state bindings (for Tasty style system)
|
|
865
|
-
const tastyTokens = palette.tasty(
|
|
1156
|
+
const tastyTokens = palette.tasty();
|
|
866
1157
|
|
|
867
1158
|
// Export as CSS custom properties (rgb format by default)
|
|
868
|
-
const css = palette.css(
|
|
869
|
-
// css.light → "--primary-surface-color: rgb(...);\n--
|
|
1159
|
+
const css = palette.css();
|
|
1160
|
+
// css.light → "--primary-surface-color: rgb(...);\n--surface-color: rgb(...);\n--danger-surface-color: rgb(...);"
|
|
870
1161
|
|
|
871
1162
|
// Standalone shadow computation
|
|
872
1163
|
const v = glaze.shadow({ bg: '#f0eef5', fg: '#1a1a2e', intensity: 10 });
|
|
@@ -893,7 +1184,8 @@ brand.colors({ surface: { lightness: 97 }, text: { base: 'surface', lightness: '
|
|
|
893
1184
|
| `glaze.from(data)` | Create a theme from an exported configuration |
|
|
894
1185
|
| `glaze.fromHex(hex)` | Create a theme from a hex color (`#rgb` or `#rrggbb`) |
|
|
895
1186
|
| `glaze.fromRgb(r, g, b)` | Create a theme from RGB values (0–255) |
|
|
896
|
-
| `glaze.color(input)` | Create a standalone color token |
|
|
1187
|
+
| `glaze.color(input)` | Create a standalone color token from `{ hue, saturation, lightness, ... }` |
|
|
1188
|
+
| `glaze.color(value, overrides?)` | Create a standalone color token from a hex string, an `rgb()` / `hsl()` / `okhsl()` / `oklch()` string, an `{ h, s, l }` OKHSL object, or an `[r, g, b]` (0–255) tuple. Overrides accept absolute or relative `hue` / `lightness`, `saturation`, `mode`, `base` (any value form), and `contrast` |
|
|
897
1189
|
| `glaze.shadow(input)` | Compute a standalone shadow color (returns `ResolvedColorVariant`) |
|
|
898
1190
|
| `glaze.format(variant, format?)` | Format any `ResolvedColorVariant` as a CSS string |
|
|
899
1191
|
|
|
@@ -912,7 +1204,7 @@ brand.colors({ surface: { lightness: 97 }, text: { base: 'surface', lightness: '
|
|
|
912
1204
|
| `theme.extend(options)` | Create a child theme |
|
|
913
1205
|
| `theme.resolve()` | Resolve all colors |
|
|
914
1206
|
| `theme.tokens(options?)` | Export as flat token map grouped by variant |
|
|
915
|
-
| `theme.tasty(options?)` | Export as [Tasty](https://
|
|
1207
|
+
| `theme.tasty(options?)` | Export as [Tasty](https://tasty.style/docs) style-to-state bindings |
|
|
916
1208
|
| `theme.json(options?)` | Export as plain JSON |
|
|
917
1209
|
| `theme.css(options?)` | Export as CSS custom property declarations |
|
|
918
1210
|
|
|
@@ -921,7 +1213,7 @@ brand.colors({ surface: { lightness: 97 }, text: { base: 'surface', lightness: '
|
|
|
921
1213
|
| Method | Description |
|
|
922
1214
|
|---|---|
|
|
923
1215
|
| `glaze.configure(config)` | Set global configuration |
|
|
924
|
-
| `glaze.palette(themes)` | Compose themes into a palette |
|
|
1216
|
+
| `glaze.palette(themes, options?)` | Compose themes into a palette (options: `{ primary? }`) |
|
|
925
1217
|
| `glaze.getConfig()` | Get current global config |
|
|
926
1218
|
| `glaze.resetConfig()` | Reset to defaults |
|
|
927
1219
|
|