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