@tenphi/glaze 0.0.0-snapshot.4c063ef → 0.0.0-snapshot.60e8979
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 +271 -38
- package/dist/index.cjs +381 -76
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +134 -24
- package/dist/index.d.mts +134 -24
- package/dist/index.mjs +380 -77
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
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
|
|
@@ -413,6 +416,139 @@ const css = glaze.format(v, 'oklch');
|
|
|
413
416
|
}
|
|
414
417
|
```
|
|
415
418
|
|
|
419
|
+
## Mix Colors
|
|
420
|
+
|
|
421
|
+
Mix colors blend two existing colors together. Use them for hover overlays, tints, shades, and any derived color that sits between two reference colors.
|
|
422
|
+
|
|
423
|
+
### Opaque Mix
|
|
424
|
+
|
|
425
|
+
Produces a solid color by interpolating between `base` and `target`:
|
|
426
|
+
|
|
427
|
+
```ts
|
|
428
|
+
theme.colors({
|
|
429
|
+
surface: { lightness: 95 },
|
|
430
|
+
accent: { lightness: 30 },
|
|
431
|
+
|
|
432
|
+
// 30% of the way from surface toward accent
|
|
433
|
+
tint: { type: 'mix', base: 'surface', target: 'accent', value: 30 },
|
|
434
|
+
});
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
- `value` — mix ratio 0–100 (0 = pure base, 100 = pure target)
|
|
438
|
+
- The result is a fully opaque color (alpha = 1)
|
|
439
|
+
- Adapts to light/dark/HC schemes automatically via the resolved base and target
|
|
440
|
+
|
|
441
|
+
### Transparent Mix
|
|
442
|
+
|
|
443
|
+
Produces the target color with a controlled opacity — useful for hover overlays:
|
|
444
|
+
|
|
445
|
+
```ts
|
|
446
|
+
theme.colors({
|
|
447
|
+
surface: { lightness: 95 },
|
|
448
|
+
black: { lightness: 0, saturation: 0 },
|
|
449
|
+
|
|
450
|
+
hover: {
|
|
451
|
+
type: 'mix',
|
|
452
|
+
base: 'surface',
|
|
453
|
+
target: 'black',
|
|
454
|
+
value: 8,
|
|
455
|
+
blend: 'transparent',
|
|
456
|
+
},
|
|
457
|
+
});
|
|
458
|
+
// hover → target color (black) with alpha = 0.08
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
The output color has `h`, `s`, `l` from the target and `alpha = value / 100`.
|
|
462
|
+
|
|
463
|
+
### Blend Space
|
|
464
|
+
|
|
465
|
+
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:
|
|
466
|
+
|
|
467
|
+
```ts
|
|
468
|
+
theme.colors({
|
|
469
|
+
surface: { lightness: 95 },
|
|
470
|
+
accent: { lightness: 30 },
|
|
471
|
+
|
|
472
|
+
// sRGB blend — matches what the browser would render
|
|
473
|
+
hover: { type: 'mix', base: 'surface', target: 'accent', value: 20, space: 'srgb' },
|
|
474
|
+
});
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
| Space | Behavior | Best for |
|
|
478
|
+
|---|---|---|
|
|
479
|
+
| `'okhsl'` (default) | Perceptually uniform OKHSL interpolation | Design token derivation |
|
|
480
|
+
| `'srgb'` | Linear sRGB channel interpolation | Matching browser compositing |
|
|
481
|
+
|
|
482
|
+
The `space` option only affects opaque blending. Transparent blending always composites in linear sRGB (matching browser alpha compositing).
|
|
483
|
+
|
|
484
|
+
### Contrast Solving
|
|
485
|
+
|
|
486
|
+
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:
|
|
487
|
+
|
|
488
|
+
```ts
|
|
489
|
+
theme.colors({
|
|
490
|
+
surface: { lightness: 95 },
|
|
491
|
+
accent: { lightness: 30 },
|
|
492
|
+
|
|
493
|
+
// Ensure the mixed color has at least AA contrast against surface
|
|
494
|
+
tint: {
|
|
495
|
+
type: 'mix',
|
|
496
|
+
base: 'surface',
|
|
497
|
+
target: 'accent',
|
|
498
|
+
value: 10,
|
|
499
|
+
contrast: 'AA',
|
|
500
|
+
},
|
|
501
|
+
|
|
502
|
+
// Ensure the transparent overlay has at least 3:1 contrast
|
|
503
|
+
overlay: {
|
|
504
|
+
type: 'mix',
|
|
505
|
+
base: 'surface',
|
|
506
|
+
target: 'accent',
|
|
507
|
+
value: 5,
|
|
508
|
+
blend: 'transparent',
|
|
509
|
+
contrast: 3,
|
|
510
|
+
},
|
|
511
|
+
});
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
### High-Contrast Pairs
|
|
515
|
+
|
|
516
|
+
Both `value` and `contrast` support `[normal, highContrast]` pairs:
|
|
517
|
+
|
|
518
|
+
```ts
|
|
519
|
+
theme.colors({
|
|
520
|
+
surface: { lightness: 95 },
|
|
521
|
+
accent: { lightness: 30 },
|
|
522
|
+
|
|
523
|
+
tint: {
|
|
524
|
+
type: 'mix',
|
|
525
|
+
base: 'surface',
|
|
526
|
+
target: 'accent',
|
|
527
|
+
value: [20, 40], // stronger mix in high-contrast mode
|
|
528
|
+
contrast: [3, 'AAA'], // stricter contrast in high-contrast mode
|
|
529
|
+
},
|
|
530
|
+
});
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
### Achromatic Colors
|
|
534
|
+
|
|
535
|
+
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.
|
|
536
|
+
|
|
537
|
+
### Mix Chaining
|
|
538
|
+
|
|
539
|
+
Mix colors can reference other mix colors, enabling multi-step derivations:
|
|
540
|
+
|
|
541
|
+
```ts
|
|
542
|
+
theme.colors({
|
|
543
|
+
white: { lightness: 100, saturation: 0 },
|
|
544
|
+
black: { lightness: 0, saturation: 0 },
|
|
545
|
+
gray: { type: 'mix', base: 'white', target: 'black', value: 50, space: 'srgb' },
|
|
546
|
+
lightGray: { type: 'mix', base: 'white', target: 'gray', value: 50, space: 'srgb' },
|
|
547
|
+
});
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
Mix colors cannot reference shadow colors (same restriction as regular dependent colors).
|
|
551
|
+
|
|
416
552
|
## Output Formats
|
|
417
553
|
|
|
418
554
|
Control the color format in exports with the `format` option:
|
|
@@ -467,7 +603,7 @@ Modes control how colors adapt across schemes:
|
|
|
467
603
|
|
|
468
604
|
```ts
|
|
469
605
|
// Light: surface L=97, text lightness='-52' → L=45 (dark text on light bg)
|
|
470
|
-
// Dark: surface inverts to L≈
|
|
606
|
+
// Dark: surface inverts to L≈20 (Möbius curve), sign flips → L=20+52=72
|
|
471
607
|
// contrast solver may push further (light text on dark bg)
|
|
472
608
|
```
|
|
473
609
|
|
|
@@ -484,14 +620,14 @@ Modes control how colors adapt across schemes:
|
|
|
484
620
|
|
|
485
621
|
### Lightness
|
|
486
622
|
|
|
487
|
-
|
|
623
|
+
Absolute lightness values (both root colors and dependent colors with absolute lightness) are mapped linearly within the configured `lightLightness` window:
|
|
488
624
|
|
|
489
625
|
```ts
|
|
490
626
|
const [lo, hi] = lightLightness; // default: [10, 100]
|
|
491
627
|
const mappedL = (lightness * (hi - lo)) / 100 + lo;
|
|
492
628
|
```
|
|
493
629
|
|
|
494
|
-
Both `auto` and `fixed` modes use the same linear formula. `static` mode
|
|
630
|
+
Both `auto` and `fixed` modes use the same linear formula. `static` mode and high-contrast variants bypass the mapping entirely (identity: `mappedL = l`).
|
|
495
631
|
|
|
496
632
|
| Color | Raw L | Mapped L (default [10, 100]) |
|
|
497
633
|
|---|---|---|
|
|
@@ -503,24 +639,29 @@ Both `auto` and `fixed` modes use the same linear formula. `static` mode bypasse
|
|
|
503
639
|
|
|
504
640
|
### Lightness
|
|
505
641
|
|
|
506
|
-
**`auto`** — inverted within the configured window:
|
|
642
|
+
**`auto`** — inverted with a Möbius transformation within the configured window:
|
|
507
643
|
|
|
508
644
|
```ts
|
|
509
645
|
const [lo, hi] = darkLightness; // default: [15, 95]
|
|
510
|
-
const
|
|
646
|
+
const t = (100 - lightness) / 100;
|
|
647
|
+
const invertedL = lo + (hi - lo) * t / (t + darkCurve * (1 - t)); // darkCurve default: 0.5
|
|
511
648
|
```
|
|
512
649
|
|
|
513
|
-
|
|
650
|
+
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.
|
|
651
|
+
|
|
652
|
+
**`fixed`** — mapped without inversion (not affected by `darkCurve`):
|
|
514
653
|
|
|
515
654
|
```ts
|
|
516
655
|
const mappedL = (lightness * (hi - lo)) / 100 + lo;
|
|
517
656
|
```
|
|
518
657
|
|
|
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 |
|
|
658
|
+
| Color | Light L | Auto (curve=0.5) | Auto (curve=1, linear) | Fixed (mapped) |
|
|
659
|
+
|---|---|---|---|---|
|
|
660
|
+
| surface (L=97) | 97 | 19.7 | 17.4 | 92.6 |
|
|
661
|
+
| accent-fill (L=52) | 52 | 66.9 | 53.4 | 56.6 |
|
|
662
|
+
| accent-text (L=100) | 100 | 15 | 15 | 95 |
|
|
663
|
+
|
|
664
|
+
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
665
|
|
|
525
666
|
### Saturation
|
|
526
667
|
|
|
@@ -560,12 +701,21 @@ Combine multiple themes into a single palette:
|
|
|
560
701
|
const palette = glaze.palette({ primary, danger, success, warning });
|
|
561
702
|
```
|
|
562
703
|
|
|
563
|
-
|
|
704
|
+
Optionally designate a primary theme at creation time:
|
|
705
|
+
|
|
706
|
+
```ts
|
|
707
|
+
const palette = glaze.palette(
|
|
708
|
+
{ primary, danger, success, warning },
|
|
709
|
+
{ primary: 'primary' },
|
|
710
|
+
);
|
|
711
|
+
```
|
|
712
|
+
|
|
713
|
+
### Prefix Behavior
|
|
564
714
|
|
|
565
|
-
|
|
715
|
+
Palette export methods (`tokens()`, `tasty()`, `css()`) default to `prefix: true` — all tokens are automatically prefixed with the theme name to avoid collisions:
|
|
566
716
|
|
|
567
717
|
```ts
|
|
568
|
-
const tokens = palette.tokens(
|
|
718
|
+
const tokens = palette.tokens();
|
|
569
719
|
// → {
|
|
570
720
|
// light: { 'primary-surface': 'okhsl(...)', 'danger-surface': 'okhsl(...)' },
|
|
571
721
|
// dark: { 'primary-surface': 'okhsl(...)', 'danger-surface': 'okhsl(...)' },
|
|
@@ -578,15 +728,68 @@ Custom prefix mapping:
|
|
|
578
728
|
palette.tokens({ prefix: { primary: 'brand-', danger: 'error-' } });
|
|
579
729
|
```
|
|
580
730
|
|
|
731
|
+
To disable prefixing entirely, pass `prefix: false` explicitly.
|
|
732
|
+
|
|
733
|
+
### Collision Detection
|
|
734
|
+
|
|
735
|
+
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:
|
|
736
|
+
|
|
737
|
+
```ts
|
|
738
|
+
const palette = glaze.palette({ a, b });
|
|
739
|
+
palette.tokens({ prefix: false });
|
|
740
|
+
// ⚠ glaze: token "surface" from theme "b" collides with theme "a" — skipping.
|
|
741
|
+
```
|
|
742
|
+
|
|
743
|
+
### Primary Theme
|
|
744
|
+
|
|
745
|
+
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:
|
|
746
|
+
|
|
747
|
+
```ts
|
|
748
|
+
const palette = glaze.palette(
|
|
749
|
+
{ primary, danger, success },
|
|
750
|
+
{ primary: 'primary' },
|
|
751
|
+
);
|
|
752
|
+
const tokens = palette.tokens();
|
|
753
|
+
// → {
|
|
754
|
+
// light: {
|
|
755
|
+
// 'primary-surface': 'okhsl(...)', // prefixed (all themes)
|
|
756
|
+
// 'danger-surface': 'okhsl(...)',
|
|
757
|
+
// 'success-surface': 'okhsl(...)',
|
|
758
|
+
// 'surface': 'okhsl(...)', // unprefixed alias (primary only)
|
|
759
|
+
// },
|
|
760
|
+
// }
|
|
761
|
+
```
|
|
762
|
+
|
|
763
|
+
Override or disable per-export:
|
|
764
|
+
|
|
765
|
+
```ts
|
|
766
|
+
palette.tokens({ primary: 'danger' }); // use danger as primary for this call
|
|
767
|
+
palette.tokens({ primary: false }); // no primary for this call
|
|
768
|
+
```
|
|
769
|
+
|
|
770
|
+
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:
|
|
771
|
+
|
|
772
|
+
```ts
|
|
773
|
+
palette.tokens({ prefix: { primary: 'p-', danger: 'd-' } });
|
|
774
|
+
// → 'p-surface' + 'surface' (alias from palette-level primary) + 'd-surface'
|
|
775
|
+
```
|
|
776
|
+
|
|
777
|
+
An error is thrown if the primary name doesn't match any theme in the palette.
|
|
778
|
+
|
|
581
779
|
### Tasty Export (for [Tasty](https://cube-ui-kit.vercel.app/?path=/docs/tasty-documentation--docs) style system)
|
|
582
780
|
|
|
583
781
|
The `tasty()` method exports tokens in the [Tasty](https://cube-ui-kit.vercel.app/?path=/docs/tasty-documentation--docs) style-to-state binding format — `#name` color token keys with state aliases (`''`, `@dark`, etc.):
|
|
584
782
|
|
|
585
783
|
```ts
|
|
586
|
-
const
|
|
784
|
+
const palette = glaze.palette(
|
|
785
|
+
{ primary, danger, success },
|
|
786
|
+
{ primary: 'primary' },
|
|
787
|
+
);
|
|
788
|
+
const tastyTokens = palette.tasty();
|
|
587
789
|
// → {
|
|
588
790
|
// '#primary-surface': { '': 'okhsl(...)', '@dark': 'okhsl(...)' },
|
|
589
791
|
// '#danger-surface': { '': 'okhsl(...)', '@dark': 'okhsl(...)' },
|
|
792
|
+
// '#surface': { '': 'okhsl(...)', '@dark': 'okhsl(...)' }, // alias
|
|
590
793
|
// }
|
|
591
794
|
```
|
|
592
795
|
|
|
@@ -653,8 +856,10 @@ palette.tasty({ states: { dark: '@dark', highContrast: '@hc' } });
|
|
|
653
856
|
|
|
654
857
|
### JSON Export (Framework-Agnostic)
|
|
655
858
|
|
|
859
|
+
JSON export groups by theme name (no prefix needed):
|
|
860
|
+
|
|
656
861
|
```ts
|
|
657
|
-
const data = palette.json(
|
|
862
|
+
const data = palette.json();
|
|
658
863
|
// → {
|
|
659
864
|
// primary: { surface: { light: 'okhsl(...)', dark: 'okhsl(...)' } },
|
|
660
865
|
// danger: { surface: { light: 'okhsl(...)', dark: 'okhsl(...)' } },
|
|
@@ -676,7 +881,11 @@ const css = theme.css();
|
|
|
676
881
|
Use in a stylesheet:
|
|
677
882
|
|
|
678
883
|
```ts
|
|
679
|
-
const
|
|
884
|
+
const palette = glaze.palette(
|
|
885
|
+
{ primary, danger, success },
|
|
886
|
+
{ primary: 'primary' },
|
|
887
|
+
);
|
|
888
|
+
const css = palette.css();
|
|
680
889
|
|
|
681
890
|
const stylesheet = `
|
|
682
891
|
:root { ${css.light} }
|
|
@@ -692,7 +901,8 @@ Options:
|
|
|
692
901
|
|---|---|---|
|
|
693
902
|
| `format` | `'rgb'` | Color format (`'rgb'`, `'hsl'`, `'okhsl'`, `'oklch'`) |
|
|
694
903
|
| `suffix` | `'-color'` | Suffix appended to each CSS property name |
|
|
695
|
-
| `prefix` |
|
|
904
|
+
| `prefix` | `true` (palette) | (palette only) `true` uses `"<themeName>-"`, or provide a custom map |
|
|
905
|
+
| `primary` | inherited | (palette only) Override or disable (`false`) the palette-level primary for this call |
|
|
696
906
|
|
|
697
907
|
```ts
|
|
698
908
|
// Custom suffix
|
|
@@ -703,9 +913,9 @@ theme.css({ suffix: '' });
|
|
|
703
913
|
theme.css({ format: 'hsl' });
|
|
704
914
|
// → "--surface-color: hsl(...);"
|
|
705
915
|
|
|
706
|
-
// Palette with
|
|
707
|
-
palette.css(
|
|
708
|
-
// → "--primary-surface-color: rgb(...);\n--danger-surface-color: rgb(...);"
|
|
916
|
+
// Palette with primary (inherited from palette creation)
|
|
917
|
+
palette.css();
|
|
918
|
+
// → "--primary-surface-color: rgb(...);\n--surface-color: rgb(...);\n--danger-surface-color: rgb(...);"
|
|
709
919
|
```
|
|
710
920
|
|
|
711
921
|
## Output Modes
|
|
@@ -738,9 +948,10 @@ Resolution priority (highest first):
|
|
|
738
948
|
|
|
739
949
|
```ts
|
|
740
950
|
glaze.configure({
|
|
741
|
-
lightLightness: [10, 100], // Light scheme lightness window [lo, hi]
|
|
742
|
-
darkLightness: [15, 95], // Dark scheme lightness window [lo, hi]
|
|
951
|
+
lightLightness: [10, 100], // Light scheme lightness window [lo, hi] (bypassed in HC)
|
|
952
|
+
darkLightness: [15, 95], // Dark scheme lightness window [lo, hi] (bypassed in HC)
|
|
743
953
|
darkDesaturation: 0.1, // Saturation reduction in dark scheme (0–1)
|
|
954
|
+
darkCurve: 0.5, // Möbius beta for dark auto-inversion (0–1); or [normal, hc] pair
|
|
744
955
|
states: {
|
|
745
956
|
dark: '@dark', // State alias for dark mode tokens
|
|
746
957
|
highContrast: '@high-contrast',
|
|
@@ -758,10 +969,10 @@ glaze.configure({
|
|
|
758
969
|
|
|
759
970
|
## Color Definition Shape
|
|
760
971
|
|
|
761
|
-
`ColorDef` is a discriminated union of regular colors and
|
|
972
|
+
`ColorDef` is a discriminated union of regular colors, shadow colors, and mix colors:
|
|
762
973
|
|
|
763
974
|
```ts
|
|
764
|
-
type ColorDef = RegularColorDef | ShadowColorDef;
|
|
975
|
+
type ColorDef = RegularColorDef | ShadowColorDef | MixColorDef;
|
|
765
976
|
|
|
766
977
|
interface RegularColorDef {
|
|
767
978
|
lightness?: HCPair<number | RelativeValue>;
|
|
@@ -780,15 +991,24 @@ interface ShadowColorDef {
|
|
|
780
991
|
intensity: HCPair<number>; // 0–100
|
|
781
992
|
tuning?: ShadowTuning;
|
|
782
993
|
}
|
|
994
|
+
|
|
995
|
+
interface MixColorDef {
|
|
996
|
+
type: 'mix';
|
|
997
|
+
base: string; // "from" color name
|
|
998
|
+
target: string; // "to" color name
|
|
999
|
+
value: HCPair<number>; // 0–100 (mix ratio or opacity)
|
|
1000
|
+
blend?: 'opaque' | 'transparent'; // default: 'opaque'
|
|
1001
|
+
space?: 'okhsl' | 'srgb'; // default: 'okhsl'
|
|
1002
|
+
contrast?: HCPair<MinContrast>;
|
|
1003
|
+
}
|
|
783
1004
|
```
|
|
784
1005
|
|
|
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.
|
|
1006
|
+
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
1007
|
|
|
787
1008
|
## Validation
|
|
788
1009
|
|
|
789
1010
|
| Condition | Behavior |
|
|
790
1011
|
|---|---|
|
|
791
|
-
| Both absolute `lightness` and `base` on same color | Warning, `lightness` takes precedence |
|
|
792
1012
|
| `contrast` without `base` | Validation error |
|
|
793
1013
|
| Relative `lightness` without `base` | Validation error |
|
|
794
1014
|
| `lightness` resolves outside 0–100 | Clamp silently |
|
|
@@ -802,6 +1022,12 @@ A root color must have absolute `lightness` (a number). A dependent color must h
|
|
|
802
1022
|
| Regular color `base` references a shadow color | Validation error |
|
|
803
1023
|
| Shadow `intensity` outside 0–100 | Clamp silently |
|
|
804
1024
|
| `contrast` + `opacity` combined | Warning |
|
|
1025
|
+
| Mix `base` references non-existent color | Validation error |
|
|
1026
|
+
| Mix `target` references non-existent color | Validation error |
|
|
1027
|
+
| Mix `base` references a shadow color | Validation error |
|
|
1028
|
+
| Mix `target` references a shadow color | Validation error |
|
|
1029
|
+
| Mix `value` outside 0–100 | Clamp silently |
|
|
1030
|
+
| Circular references involving mix colors | Validation error |
|
|
805
1031
|
|
|
806
1032
|
## Advanced: Color Math Utilities
|
|
807
1033
|
|
|
@@ -847,6 +1073,10 @@ primary.colors({
|
|
|
847
1073
|
'shadow-md': { type: 'shadow', bg: 'surface', fg: 'text', intensity: 10 },
|
|
848
1074
|
'shadow-lg': { type: 'shadow', bg: 'surface', fg: 'text', intensity: 20 },
|
|
849
1075
|
|
|
1076
|
+
// Mix colors — hover overlays and tints
|
|
1077
|
+
'hover': { type: 'mix', base: 'surface', target: 'accent-fill', value: 8, blend: 'transparent' },
|
|
1078
|
+
'tint': { type: 'mix', base: 'surface', target: 'accent-fill', value: 20 },
|
|
1079
|
+
|
|
850
1080
|
// Fixed-alpha overlay
|
|
851
1081
|
overlay: { lightness: 0, opacity: 0.5 },
|
|
852
1082
|
});
|
|
@@ -856,18 +1086,21 @@ const success = primary.extend({ hue: 157 });
|
|
|
856
1086
|
const warning = primary.extend({ hue: 84 });
|
|
857
1087
|
const note = primary.extend({ hue: 302 });
|
|
858
1088
|
|
|
859
|
-
const palette = glaze.palette(
|
|
1089
|
+
const palette = glaze.palette(
|
|
1090
|
+
{ primary, danger, success, warning, note },
|
|
1091
|
+
{ primary: 'primary' },
|
|
1092
|
+
);
|
|
860
1093
|
|
|
861
|
-
// Export as flat token map grouped by variant
|
|
862
|
-
const tokens = palette.tokens(
|
|
863
|
-
// tokens.light → { 'primary-surface': '
|
|
1094
|
+
// Export as flat token map grouped by variant (prefix defaults to true)
|
|
1095
|
+
const tokens = palette.tokens();
|
|
1096
|
+
// tokens.light → { 'primary-surface': '...', 'surface': '...', 'danger-surface': '...' }
|
|
864
1097
|
|
|
865
1098
|
// Export as tasty style-to-state bindings (for Tasty style system)
|
|
866
|
-
const tastyTokens = palette.tasty(
|
|
1099
|
+
const tastyTokens = palette.tasty();
|
|
867
1100
|
|
|
868
1101
|
// Export as CSS custom properties (rgb format by default)
|
|
869
|
-
const css = palette.css(
|
|
870
|
-
// css.light → "--primary-surface-color: rgb(...);\n--
|
|
1102
|
+
const css = palette.css();
|
|
1103
|
+
// css.light → "--primary-surface-color: rgb(...);\n--surface-color: rgb(...);\n--danger-surface-color: rgb(...);"
|
|
871
1104
|
|
|
872
1105
|
// Standalone shadow computation
|
|
873
1106
|
const v = glaze.shadow({ bg: '#f0eef5', fg: '#1a1a2e', intensity: 10 });
|
|
@@ -922,7 +1155,7 @@ brand.colors({ surface: { lightness: 97 }, text: { base: 'surface', lightness: '
|
|
|
922
1155
|
| Method | Description |
|
|
923
1156
|
|---|---|
|
|
924
1157
|
| `glaze.configure(config)` | Set global configuration |
|
|
925
|
-
| `glaze.palette(themes)` | Compose themes into a palette |
|
|
1158
|
+
| `glaze.palette(themes, options?)` | Compose themes into a palette (options: `{ primary? }`) |
|
|
926
1159
|
| `glaze.getConfig()` | Get current global config |
|
|
927
1160
|
| `glaze.resetConfig()` | Reset to defaults |
|
|
928
1161
|
|