@tenphi/glaze 0.0.0-snapshot.e26a2e0 → 0.0.0-snapshot.e941371
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 +51 -19
- package/dist/index.cjs +51 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +39 -19
- package/dist/index.d.mts +39 -19
- package/dist/index.mjs +51 -13
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -71,8 +71,8 @@ const success = primary.extend({ hue: 157 });
|
|
|
71
71
|
|
|
72
72
|
// Compose into a palette and export
|
|
73
73
|
const palette = glaze.palette({ primary, danger, success });
|
|
74
|
-
const tokens = palette.tokens({
|
|
75
|
-
// → { light: { 'primary-surface': 'okhsl(...)',
|
|
74
|
+
const tokens = palette.tokens({ primary: 'primary' });
|
|
75
|
+
// → { light: { 'primary-surface': 'okhsl(...)', 'surface': 'okhsl(...)', ... }, dark: { ... } }
|
|
76
76
|
```
|
|
77
77
|
|
|
78
78
|
## Core Concepts
|
|
@@ -618,7 +618,7 @@ Modes control how colors adapt across schemes:
|
|
|
618
618
|
|
|
619
619
|
### Lightness
|
|
620
620
|
|
|
621
|
-
|
|
621
|
+
Absolute lightness values (both root colors and dependent colors with absolute lightness) are mapped linearly within the configured `lightLightness` window:
|
|
622
622
|
|
|
623
623
|
```ts
|
|
624
624
|
const [lo, hi] = lightLightness; // default: [10, 100]
|
|
@@ -694,12 +694,12 @@ Combine multiple themes into a single palette:
|
|
|
694
694
|
const palette = glaze.palette({ primary, danger, success, warning });
|
|
695
695
|
```
|
|
696
696
|
|
|
697
|
-
###
|
|
697
|
+
### Prefix Behavior
|
|
698
698
|
|
|
699
|
-
|
|
699
|
+
Palette export methods (`tokens()`, `tasty()`, `css()`) default to `prefix: true` — all tokens are automatically prefixed with the theme name to avoid collisions:
|
|
700
700
|
|
|
701
701
|
```ts
|
|
702
|
-
const tokens = palette.tokens(
|
|
702
|
+
const tokens = palette.tokens();
|
|
703
703
|
// → {
|
|
704
704
|
// light: { 'primary-surface': 'okhsl(...)', 'danger-surface': 'okhsl(...)' },
|
|
705
705
|
// dark: { 'primary-surface': 'okhsl(...)', 'danger-surface': 'okhsl(...)' },
|
|
@@ -712,15 +712,44 @@ Custom prefix mapping:
|
|
|
712
712
|
palette.tokens({ prefix: { primary: 'brand-', danger: 'error-' } });
|
|
713
713
|
```
|
|
714
714
|
|
|
715
|
+
To disable prefixing entirely, pass `prefix: false` explicitly. Note that tokens with the same name will overwrite each other (last theme wins).
|
|
716
|
+
|
|
717
|
+
### Primary Theme
|
|
718
|
+
|
|
719
|
+
Use the `primary` option to designate one theme as the primary. Its tokens are duplicated without prefix, providing convenient short aliases alongside the prefixed versions:
|
|
720
|
+
|
|
721
|
+
```ts
|
|
722
|
+
const palette = glaze.palette({ primary, danger, success });
|
|
723
|
+
const tokens = palette.tokens({ primary: 'primary' });
|
|
724
|
+
// → {
|
|
725
|
+
// light: {
|
|
726
|
+
// 'primary-surface': 'okhsl(...)', // prefixed (all themes)
|
|
727
|
+
// 'danger-surface': 'okhsl(...)',
|
|
728
|
+
// 'success-surface': 'okhsl(...)',
|
|
729
|
+
// 'surface': 'okhsl(...)', // unprefixed alias (primary only)
|
|
730
|
+
// },
|
|
731
|
+
// }
|
|
732
|
+
```
|
|
733
|
+
|
|
734
|
+
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:
|
|
735
|
+
|
|
736
|
+
```ts
|
|
737
|
+
palette.tokens({ prefix: { primary: 'p-', danger: 'd-' }, primary: 'primary' });
|
|
738
|
+
// → 'p-surface' + 'surface' (alias) + 'd-surface'
|
|
739
|
+
```
|
|
740
|
+
|
|
741
|
+
An error is thrown if the primary name doesn't match any theme in the palette.
|
|
742
|
+
|
|
715
743
|
### Tasty Export (for [Tasty](https://cube-ui-kit.vercel.app/?path=/docs/tasty-documentation--docs) style system)
|
|
716
744
|
|
|
717
745
|
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.):
|
|
718
746
|
|
|
719
747
|
```ts
|
|
720
|
-
const tastyTokens = palette.tasty({
|
|
748
|
+
const tastyTokens = palette.tasty({ primary: 'primary' });
|
|
721
749
|
// → {
|
|
722
750
|
// '#primary-surface': { '': 'okhsl(...)', '@dark': 'okhsl(...)' },
|
|
723
751
|
// '#danger-surface': { '': 'okhsl(...)', '@dark': 'okhsl(...)' },
|
|
752
|
+
// '#surface': { '': 'okhsl(...)', '@dark': 'okhsl(...)' }, // alias
|
|
724
753
|
// }
|
|
725
754
|
```
|
|
726
755
|
|
|
@@ -787,8 +816,10 @@ palette.tasty({ states: { dark: '@dark', highContrast: '@hc' } });
|
|
|
787
816
|
|
|
788
817
|
### JSON Export (Framework-Agnostic)
|
|
789
818
|
|
|
819
|
+
JSON export groups by theme name (no prefix needed):
|
|
820
|
+
|
|
790
821
|
```ts
|
|
791
|
-
const data = palette.json(
|
|
822
|
+
const data = palette.json();
|
|
792
823
|
// → {
|
|
793
824
|
// primary: { surface: { light: 'okhsl(...)', dark: 'okhsl(...)' } },
|
|
794
825
|
// danger: { surface: { light: 'okhsl(...)', dark: 'okhsl(...)' } },
|
|
@@ -810,7 +841,7 @@ const css = theme.css();
|
|
|
810
841
|
Use in a stylesheet:
|
|
811
842
|
|
|
812
843
|
```ts
|
|
813
|
-
const css = palette.css({
|
|
844
|
+
const css = palette.css({ primary: 'primary' });
|
|
814
845
|
|
|
815
846
|
const stylesheet = `
|
|
816
847
|
:root { ${css.light} }
|
|
@@ -826,7 +857,8 @@ Options:
|
|
|
826
857
|
|---|---|---|
|
|
827
858
|
| `format` | `'rgb'` | Color format (`'rgb'`, `'hsl'`, `'okhsl'`, `'oklch'`) |
|
|
828
859
|
| `suffix` | `'-color'` | Suffix appended to each CSS property name |
|
|
829
|
-
| `prefix` |
|
|
860
|
+
| `prefix` | `true` (palette) | (palette only) `true` uses `"<themeName>-"`, or provide a custom map |
|
|
861
|
+
| `primary` | — | (palette only) Theme name to duplicate without prefix |
|
|
830
862
|
|
|
831
863
|
```ts
|
|
832
864
|
// Custom suffix
|
|
@@ -837,9 +869,9 @@ theme.css({ suffix: '' });
|
|
|
837
869
|
theme.css({ format: 'hsl' });
|
|
838
870
|
// → "--surface-color: hsl(...);"
|
|
839
871
|
|
|
840
|
-
// Palette with
|
|
841
|
-
palette.css({
|
|
842
|
-
// → "--primary-surface-color: rgb(...);\n--danger-surface-color: rgb(...);"
|
|
872
|
+
// Palette with primary
|
|
873
|
+
palette.css({ primary: 'primary' });
|
|
874
|
+
// → "--primary-surface-color: rgb(...);\n--surface-color: rgb(...);\n--danger-surface-color: rgb(...);"
|
|
843
875
|
```
|
|
844
876
|
|
|
845
877
|
## Output Modes
|
|
@@ -1011,16 +1043,16 @@ const note = primary.extend({ hue: 302 });
|
|
|
1011
1043
|
|
|
1012
1044
|
const palette = glaze.palette({ primary, danger, success, warning, note });
|
|
1013
1045
|
|
|
1014
|
-
// Export as flat token map grouped by variant
|
|
1015
|
-
const tokens = palette.tokens({
|
|
1016
|
-
// tokens.light → { 'primary-surface': '
|
|
1046
|
+
// Export as flat token map grouped by variant (prefix defaults to true)
|
|
1047
|
+
const tokens = palette.tokens({ primary: 'primary' });
|
|
1048
|
+
// tokens.light → { 'primary-surface': '...', 'surface': '...', 'danger-surface': '...' }
|
|
1017
1049
|
|
|
1018
1050
|
// Export as tasty style-to-state bindings (for Tasty style system)
|
|
1019
|
-
const tastyTokens = palette.tasty({
|
|
1051
|
+
const tastyTokens = palette.tasty({ primary: 'primary' });
|
|
1020
1052
|
|
|
1021
1053
|
// Export as CSS custom properties (rgb format by default)
|
|
1022
|
-
const css = palette.css({
|
|
1023
|
-
// css.light → "--primary-surface-color: rgb(...);\n--
|
|
1054
|
+
const css = palette.css({ primary: 'primary' });
|
|
1055
|
+
// css.light → "--primary-surface-color: rgb(...);\n--surface-color: rgb(...);\n--danger-surface-color: rgb(...);"
|
|
1024
1056
|
|
|
1025
1057
|
// Standalone shadow computation
|
|
1026
1058
|
const v = glaze.shadow({ bg: '#f0eef5', fg: '#1a1a2e', intensity: 10 });
|
package/dist/index.cjs
CHANGED
|
@@ -433,12 +433,13 @@ function formatOkhsl(h, s, l) {
|
|
|
433
433
|
return `okhsl(${fmt$1(h, 2)} ${fmt$1(s, 2)}% ${fmt$1(l, 2)}%)`;
|
|
434
434
|
}
|
|
435
435
|
/**
|
|
436
|
-
* Format OKHSL values as a CSS `rgb(R G B)` string
|
|
436
|
+
* Format OKHSL values as a CSS `rgb(R G B)` string.
|
|
437
|
+
* Uses 2 decimal places to avoid 8-bit quantization contrast loss.
|
|
437
438
|
* h: 0–360, s: 0–100, l: 0–100 (percentage scale for s and l).
|
|
438
439
|
*/
|
|
439
440
|
function formatRgb(h, s, l) {
|
|
440
441
|
const [r, g, b] = okhslToSrgb(h, s / 100, l / 100);
|
|
441
|
-
return `rgb(${
|
|
442
|
+
return `rgb(${parseFloat((r * 255).toFixed(2))} ${parseFloat((g * 255).toFixed(2))} ${parseFloat((b * 255).toFixed(2))})`;
|
|
442
443
|
}
|
|
443
444
|
/**
|
|
444
445
|
* Format OKHSL values as a CSS `hsl(H S% L%)` string.
|
|
@@ -619,7 +620,7 @@ function coarseScan(h, s, lo, hi, yBase, target, epsilon, maxIter) {
|
|
|
619
620
|
function findLightnessForContrast(options) {
|
|
620
621
|
const { hue, saturation, preferredLightness, baseLinearRgb, contrast: contrastInput, lightnessRange = [0, 1], epsilon = 1e-4, maxIterations = 14 } = options;
|
|
621
622
|
const target = resolveMinContrast(contrastInput);
|
|
622
|
-
const searchTarget = target * 1.
|
|
623
|
+
const searchTarget = target * 1.005;
|
|
623
624
|
const yBase = gamutClampedLuminance(baseLinearRgb);
|
|
624
625
|
const crPref = contrastRatioFromLuminance(cachedLuminance(hue, saturation, preferredLightness), yBase);
|
|
625
626
|
if (crPref >= searchTarget) return {
|
|
@@ -743,7 +744,7 @@ function searchMixBranch(lo, hi, yBase, target, epsilon, maxIter, preferred, lum
|
|
|
743
744
|
function findValueForMixContrast(options) {
|
|
744
745
|
const { preferredValue, baseLinearRgb, contrast: contrastInput, luminanceAtValue, epsilon = 1e-4, maxIterations = 20 } = options;
|
|
745
746
|
const target = resolveMinContrast(contrastInput);
|
|
746
|
-
const searchTarget = target * 1.
|
|
747
|
+
const searchTarget = target * 1.005;
|
|
747
748
|
const yBase = gamutClampedLuminance(baseLinearRgb);
|
|
748
749
|
const crPref = contrastRatioFromLuminance(luminanceAtValue(preferredValue), yBase);
|
|
749
750
|
if (crPref >= searchTarget) return {
|
|
@@ -975,6 +976,11 @@ function mapSaturationDark(s, mode) {
|
|
|
975
976
|
if (mode === "static") return s;
|
|
976
977
|
return s * (1 - globalConfig.darkDesaturation);
|
|
977
978
|
}
|
|
979
|
+
function schemeLightnessRange(isDark, mode) {
|
|
980
|
+
if (mode === "static") return [0, 1];
|
|
981
|
+
const [lo, hi] = isDark ? globalConfig.darkLightness : globalConfig.lightLightness;
|
|
982
|
+
return [lo / 100, hi / 100];
|
|
983
|
+
}
|
|
978
984
|
function clamp(v, min, max) {
|
|
979
985
|
return Math.max(min, Math.min(max, v));
|
|
980
986
|
}
|
|
@@ -1035,20 +1041,22 @@ function resolveDependentColor(name, def, ctx, isHighContrast, isDark, effective
|
|
|
1035
1041
|
if (isDark && mode === "auto") delta = -delta;
|
|
1036
1042
|
preferredL = clamp(baseL + delta, 0, 100);
|
|
1037
1043
|
} else if (isDark) preferredL = mapLightnessDark(parsed.value, mode);
|
|
1038
|
-
else preferredL =
|
|
1044
|
+
else preferredL = mapLightnessLight(parsed.value, mode);
|
|
1039
1045
|
}
|
|
1040
1046
|
const rawContrast = def.contrast;
|
|
1041
1047
|
if (rawContrast !== void 0) {
|
|
1042
1048
|
const minCr = isHighContrast ? pairHC(rawContrast) : pairNormal(rawContrast);
|
|
1043
1049
|
const effectiveSat = isDark ? mapSaturationDark(satFactor * ctx.saturation / 100, mode) : satFactor * ctx.saturation / 100;
|
|
1044
1050
|
const baseLinearRgb = okhslToLinearSrgb(baseVariant.h, baseVariant.s, baseVariant.l);
|
|
1051
|
+
const lightnessRange = schemeLightnessRange(isDark, mode);
|
|
1045
1052
|
return {
|
|
1046
1053
|
l: findLightnessForContrast({
|
|
1047
1054
|
hue: effectiveHue,
|
|
1048
1055
|
saturation: effectiveSat,
|
|
1049
|
-
preferredLightness: preferredL / 100,
|
|
1056
|
+
preferredLightness: clamp(preferredL / 100, lightnessRange[0], lightnessRange[1]),
|
|
1050
1057
|
baseLinearRgb,
|
|
1051
|
-
contrast: minCr
|
|
1058
|
+
contrast: minCr,
|
|
1059
|
+
lightnessRange
|
|
1052
1060
|
}).lightness * 100,
|
|
1053
1061
|
satFactor
|
|
1054
1062
|
};
|
|
@@ -1413,26 +1421,40 @@ function createTheme(hue, saturation, initialColors) {
|
|
|
1413
1421
|
}
|
|
1414
1422
|
};
|
|
1415
1423
|
}
|
|
1416
|
-
function resolvePrefix(options, themeName) {
|
|
1417
|
-
|
|
1418
|
-
if (
|
|
1424
|
+
function resolvePrefix(options, themeName, defaultPrefix = false) {
|
|
1425
|
+
const prefix = options?.prefix ?? defaultPrefix;
|
|
1426
|
+
if (prefix === true) return `${themeName}-`;
|
|
1427
|
+
if (typeof prefix === "object" && prefix !== null) return prefix[themeName] ?? `${themeName}-`;
|
|
1419
1428
|
return "";
|
|
1420
1429
|
}
|
|
1430
|
+
function validatePrimaryTheme(primary, themes) {
|
|
1431
|
+
if (primary !== void 0 && !(primary in themes)) {
|
|
1432
|
+
const available = Object.keys(themes).join(", ");
|
|
1433
|
+
throw new Error(`glaze: primary theme "${primary}" not found in palette. Available: ${available}.`);
|
|
1434
|
+
}
|
|
1435
|
+
}
|
|
1421
1436
|
function createPalette(themes) {
|
|
1422
1437
|
return {
|
|
1423
1438
|
tokens(options) {
|
|
1439
|
+
validatePrimaryTheme(options?.primary, themes);
|
|
1424
1440
|
const modes = resolveModes(options?.modes);
|
|
1425
1441
|
const allTokens = {};
|
|
1426
1442
|
for (const [themeName, theme] of Object.entries(themes)) {
|
|
1427
|
-
const
|
|
1443
|
+
const resolved = theme.resolve();
|
|
1444
|
+
const tokens = buildFlatTokenMap(resolved, resolvePrefix(options, themeName, true), modes, options?.format);
|
|
1428
1445
|
for (const variant of Object.keys(tokens)) {
|
|
1429
1446
|
if (!allTokens[variant]) allTokens[variant] = {};
|
|
1430
1447
|
Object.assign(allTokens[variant], tokens[variant]);
|
|
1431
1448
|
}
|
|
1449
|
+
if (themeName === options?.primary) {
|
|
1450
|
+
const unprefixed = buildFlatTokenMap(resolved, "", modes, options?.format);
|
|
1451
|
+
for (const variant of Object.keys(unprefixed)) Object.assign(allTokens[variant], unprefixed[variant]);
|
|
1452
|
+
}
|
|
1432
1453
|
}
|
|
1433
1454
|
return allTokens;
|
|
1434
1455
|
},
|
|
1435
1456
|
tasty(options) {
|
|
1457
|
+
validatePrimaryTheme(options?.primary, themes);
|
|
1436
1458
|
const states = {
|
|
1437
1459
|
dark: options?.states?.dark ?? globalConfig.states.dark,
|
|
1438
1460
|
highContrast: options?.states?.highContrast ?? globalConfig.states.highContrast
|
|
@@ -1440,8 +1462,13 @@ function createPalette(themes) {
|
|
|
1440
1462
|
const modes = resolveModes(options?.modes);
|
|
1441
1463
|
const allTokens = {};
|
|
1442
1464
|
for (const [themeName, theme] of Object.entries(themes)) {
|
|
1443
|
-
const
|
|
1465
|
+
const resolved = theme.resolve();
|
|
1466
|
+
const tokens = buildTokenMap(resolved, resolvePrefix(options, themeName, true), states, modes, options?.format);
|
|
1444
1467
|
Object.assign(allTokens, tokens);
|
|
1468
|
+
if (themeName === options?.primary) {
|
|
1469
|
+
const unprefixed = buildTokenMap(resolved, "", states, modes, options?.format);
|
|
1470
|
+
Object.assign(allTokens, unprefixed);
|
|
1471
|
+
}
|
|
1445
1472
|
}
|
|
1446
1473
|
return allTokens;
|
|
1447
1474
|
},
|
|
@@ -1452,6 +1479,7 @@ function createPalette(themes) {
|
|
|
1452
1479
|
return result;
|
|
1453
1480
|
},
|
|
1454
1481
|
css(options) {
|
|
1482
|
+
validatePrimaryTheme(options?.primary, themes);
|
|
1455
1483
|
const suffix = options?.suffix ?? "-color";
|
|
1456
1484
|
const format = options?.format ?? "rgb";
|
|
1457
1485
|
const allLines = {
|
|
@@ -1461,13 +1489,23 @@ function createPalette(themes) {
|
|
|
1461
1489
|
darkContrast: []
|
|
1462
1490
|
};
|
|
1463
1491
|
for (const [themeName, theme] of Object.entries(themes)) {
|
|
1464
|
-
const
|
|
1492
|
+
const resolved = theme.resolve();
|
|
1493
|
+
const css = buildCssMap(resolved, resolvePrefix(options, themeName, true), suffix, format);
|
|
1465
1494
|
for (const key of [
|
|
1466
1495
|
"light",
|
|
1467
1496
|
"dark",
|
|
1468
1497
|
"lightContrast",
|
|
1469
1498
|
"darkContrast"
|
|
1470
1499
|
]) if (css[key]) allLines[key].push(css[key]);
|
|
1500
|
+
if (themeName === options?.primary) {
|
|
1501
|
+
const unprefixed = buildCssMap(resolved, "", suffix, format);
|
|
1502
|
+
for (const key of [
|
|
1503
|
+
"light",
|
|
1504
|
+
"dark",
|
|
1505
|
+
"lightContrast",
|
|
1506
|
+
"darkContrast"
|
|
1507
|
+
]) if (unprefixed[key]) allLines[key].push(unprefixed[key]);
|
|
1508
|
+
}
|
|
1471
1509
|
}
|
|
1472
1510
|
return {
|
|
1473
1511
|
light: allLines.light.join("\n"),
|