@tenphi/glaze 0.0.0-snapshot.709df23 → 0.0.0-snapshot.75f81fa
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 +20 -12
- package/dist/index.cjs +39 -18
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -0
- package/dist/index.d.mts +7 -0
- package/dist/index.mjs +39 -18
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -194,6 +194,8 @@ A single value applies to both modes. All control is local and explicit.
|
|
|
194
194
|
'muted': { base: 'surface', lightness: ['-35', '-50'], contrast: ['AA-large', 'AA'] }
|
|
195
195
|
```
|
|
196
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
|
+
|
|
197
199
|
## Theme Color Management
|
|
198
200
|
|
|
199
201
|
### Adding Colors
|
|
@@ -601,7 +603,7 @@ Modes control how colors adapt across schemes:
|
|
|
601
603
|
|
|
602
604
|
```ts
|
|
603
605
|
// Light: surface L=97, text lightness='-52' → L=45 (dark text on light bg)
|
|
604
|
-
// Dark: surface inverts to L≈
|
|
606
|
+
// Dark: surface inverts to L≈20 (Möbius curve), sign flips → L=20+52=72
|
|
605
607
|
// contrast solver may push further (light text on dark bg)
|
|
606
608
|
```
|
|
607
609
|
|
|
@@ -625,7 +627,7 @@ const [lo, hi] = lightLightness; // default: [10, 100]
|
|
|
625
627
|
const mappedL = (lightness * (hi - lo)) / 100 + lo;
|
|
626
628
|
```
|
|
627
629
|
|
|
628
|
-
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`).
|
|
629
631
|
|
|
630
632
|
| Color | Raw L | Mapped L (default [10, 100]) |
|
|
631
633
|
|---|---|---|
|
|
@@ -637,24 +639,29 @@ Both `auto` and `fixed` modes use the same linear formula. `static` mode bypasse
|
|
|
637
639
|
|
|
638
640
|
### Lightness
|
|
639
641
|
|
|
640
|
-
**`auto`** — inverted within the configured window:
|
|
642
|
+
**`auto`** — inverted with a Möbius transformation within the configured window:
|
|
641
643
|
|
|
642
644
|
```ts
|
|
643
645
|
const [lo, hi] = darkLightness; // default: [15, 95]
|
|
644
|
-
const
|
|
646
|
+
const t = (100 - lightness) / 100;
|
|
647
|
+
const invertedL = lo + (hi - lo) * t / (t + darkCurve * (1 - t)); // darkCurve default: 0.5
|
|
645
648
|
```
|
|
646
649
|
|
|
647
|
-
|
|
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. 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`):
|
|
648
653
|
|
|
649
654
|
```ts
|
|
650
655
|
const mappedL = (lightness * (hi - lo)) / 100 + lo;
|
|
651
656
|
```
|
|
652
657
|
|
|
653
|
-
| Color | Light L | Auto (
|
|
654
|
-
|
|
655
|
-
| surface (L=97) | 97 | 17.4 | 92.6 |
|
|
656
|
-
| accent-fill (L=52) | 52 | 53.4 | 56.6 |
|
|
657
|
-
| 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 same Möbius curve over the full [0, 100] range. Fixed uses identity (`L`). This allows HC colors to reach the full 0–100 range.
|
|
658
665
|
|
|
659
666
|
### Saturation
|
|
660
667
|
|
|
@@ -904,9 +911,10 @@ Resolution priority (highest first):
|
|
|
904
911
|
|
|
905
912
|
```ts
|
|
906
913
|
glaze.configure({
|
|
907
|
-
lightLightness: [10, 100], // Light scheme lightness window [lo, hi]
|
|
908
|
-
darkLightness: [15, 95], // Dark scheme lightness window [lo, hi]
|
|
914
|
+
lightLightness: [10, 100], // Light scheme lightness window [lo, hi] (bypassed in HC)
|
|
915
|
+
darkLightness: [15, 95], // Dark scheme lightness window [lo, hi] (bypassed in HC)
|
|
909
916
|
darkDesaturation: 0.1, // Saturation reduction in dark scheme (0–1)
|
|
917
|
+
darkCurve: 0.5, // Möbius beta for dark auto-inversion (0–1, lower = more expansion)
|
|
910
918
|
states: {
|
|
911
919
|
dark: '@dark', // State alias for dark mode tokens
|
|
912
920
|
highContrast: '@high-contrast',
|
package/dist/index.cjs
CHANGED
|
@@ -814,6 +814,7 @@ let globalConfig = {
|
|
|
814
814
|
lightLightness: [10, 100],
|
|
815
815
|
darkLightness: [15, 95],
|
|
816
816
|
darkDesaturation: .1,
|
|
817
|
+
darkCurve: .5,
|
|
817
818
|
states: {
|
|
818
819
|
dark: "@dark",
|
|
819
820
|
highContrast: "@high-contrast"
|
|
@@ -961,24 +962,42 @@ function topoSort(defs) {
|
|
|
961
962
|
for (const name of Object.keys(defs)) visit(name);
|
|
962
963
|
return result;
|
|
963
964
|
}
|
|
964
|
-
function
|
|
965
|
+
function lightnessWindow(isHighContrast, kind) {
|
|
966
|
+
if (isHighContrast) return [0, 100];
|
|
967
|
+
return kind === "dark" ? globalConfig.darkLightness : globalConfig.lightLightness;
|
|
968
|
+
}
|
|
969
|
+
function mapLightnessLight(l, mode, isHighContrast) {
|
|
965
970
|
if (mode === "static") return l;
|
|
966
|
-
const [lo, hi] =
|
|
971
|
+
const [lo, hi] = lightnessWindow(isHighContrast, "light");
|
|
967
972
|
return l * (hi - lo) / 100 + lo;
|
|
968
973
|
}
|
|
969
|
-
function
|
|
974
|
+
function mobiusCurve(t, beta) {
|
|
975
|
+
if (beta >= 1) return t;
|
|
976
|
+
return t / (t + beta * (1 - t));
|
|
977
|
+
}
|
|
978
|
+
function mapLightnessDark(l, mode, isHighContrast) {
|
|
970
979
|
if (mode === "static") return l;
|
|
971
|
-
const
|
|
972
|
-
|
|
973
|
-
|
|
980
|
+
const beta = globalConfig.darkCurve;
|
|
981
|
+
const [darkLo, darkHi] = lightnessWindow(isHighContrast, "dark");
|
|
982
|
+
if (mode === "fixed") return l * (darkHi - darkLo) / 100 + darkLo;
|
|
983
|
+
const [lightLo, lightHi] = lightnessWindow(isHighContrast, "light");
|
|
984
|
+
const t = (lightHi - (l * (lightHi - lightLo) / 100 + lightLo)) / (lightHi - lightLo);
|
|
985
|
+
return darkLo + (darkHi - darkLo) * mobiusCurve(t, beta);
|
|
986
|
+
}
|
|
987
|
+
function lightMappedToDark(lightL, isHighContrast) {
|
|
988
|
+
const beta = globalConfig.darkCurve;
|
|
989
|
+
const [lightLo, lightHi] = lightnessWindow(isHighContrast, "light");
|
|
990
|
+
const [darkLo, darkHi] = lightnessWindow(isHighContrast, "dark");
|
|
991
|
+
const t = (lightHi - clamp(lightL, lightLo, lightHi)) / (lightHi - lightLo);
|
|
992
|
+
return darkLo + (darkHi - darkLo) * mobiusCurve(t, beta);
|
|
974
993
|
}
|
|
975
994
|
function mapSaturationDark(s, mode) {
|
|
976
995
|
if (mode === "static") return s;
|
|
977
996
|
return s * (1 - globalConfig.darkDesaturation);
|
|
978
997
|
}
|
|
979
|
-
function schemeLightnessRange(isDark, mode) {
|
|
998
|
+
function schemeLightnessRange(isDark, mode, isHighContrast) {
|
|
980
999
|
if (mode === "static") return [0, 1];
|
|
981
|
-
const [lo, hi] = isDark ?
|
|
1000
|
+
const [lo, hi] = lightnessWindow(isHighContrast, isDark ? "dark" : "light");
|
|
982
1001
|
return [lo / 100, hi / 100];
|
|
983
1002
|
}
|
|
984
1003
|
function clamp(v, min, max) {
|
|
@@ -1037,26 +1056,26 @@ function resolveDependentColor(name, def, ctx, isHighContrast, isDark, effective
|
|
|
1037
1056
|
else {
|
|
1038
1057
|
const parsed = parseRelativeOrAbsolute(isHighContrast ? pairHC(rawLightness) : pairNormal(rawLightness));
|
|
1039
1058
|
if (parsed.relative) {
|
|
1040
|
-
|
|
1041
|
-
if (isDark && mode === "auto")
|
|
1042
|
-
preferredL = clamp(baseL + delta, 0, 100);
|
|
1043
|
-
} else if (isDark) preferredL = mapLightnessDark(parsed.value, mode);
|
|
1044
|
-
else preferredL = mapLightnessLight(parsed.value, mode);
|
|
1059
|
+
const delta = parsed.value;
|
|
1060
|
+
if (isDark && mode === "auto") preferredL = lightMappedToDark(clamp(getSchemeVariant(baseResolved, false, isHighContrast).l * 100 + delta, 0, 100), isHighContrast);
|
|
1061
|
+
else preferredL = clamp(baseL + delta, 0, 100);
|
|
1062
|
+
} else if (isDark) preferredL = mapLightnessDark(parsed.value, mode, isHighContrast);
|
|
1063
|
+
else preferredL = mapLightnessLight(parsed.value, mode, isHighContrast);
|
|
1045
1064
|
}
|
|
1046
1065
|
const rawContrast = def.contrast;
|
|
1047
1066
|
if (rawContrast !== void 0) {
|
|
1048
1067
|
const minCr = isHighContrast ? pairHC(rawContrast) : pairNormal(rawContrast);
|
|
1049
1068
|
const effectiveSat = isDark ? mapSaturationDark(satFactor * ctx.saturation / 100, mode) : satFactor * ctx.saturation / 100;
|
|
1050
1069
|
const baseLinearRgb = okhslToLinearSrgb(baseVariant.h, baseVariant.s, baseVariant.l);
|
|
1051
|
-
const
|
|
1070
|
+
const windowRange = schemeLightnessRange(isDark, mode, isHighContrast);
|
|
1052
1071
|
return {
|
|
1053
1072
|
l: findLightnessForContrast({
|
|
1054
1073
|
hue: effectiveHue,
|
|
1055
1074
|
saturation: effectiveSat,
|
|
1056
|
-
preferredLightness: clamp(preferredL / 100,
|
|
1075
|
+
preferredLightness: clamp(preferredL / 100, windowRange[0], windowRange[1]),
|
|
1057
1076
|
baseLinearRgb,
|
|
1058
1077
|
contrast: minCr,
|
|
1059
|
-
lightnessRange
|
|
1078
|
+
lightnessRange: [0, 1]
|
|
1060
1079
|
}).lightness * 100,
|
|
1061
1080
|
satFactor
|
|
1062
1081
|
};
|
|
@@ -1093,13 +1112,13 @@ function resolveColorForScheme(name, def, ctx, isDark, isHighContrast) {
|
|
|
1093
1112
|
let finalL;
|
|
1094
1113
|
let finalSat;
|
|
1095
1114
|
if (isDark && isRoot) {
|
|
1096
|
-
finalL = mapLightnessDark(lightL, mode);
|
|
1115
|
+
finalL = mapLightnessDark(lightL, mode, isHighContrast);
|
|
1097
1116
|
finalSat = mapSaturationDark(satFactor * ctx.saturation / 100, mode);
|
|
1098
1117
|
} else if (isDark && !isRoot) {
|
|
1099
1118
|
finalL = lightL;
|
|
1100
1119
|
finalSat = mapSaturationDark(satFactor * ctx.saturation / 100, mode);
|
|
1101
1120
|
} else if (isRoot) {
|
|
1102
|
-
finalL = mapLightnessLight(lightL, mode);
|
|
1121
|
+
finalL = mapLightnessLight(lightL, mode, isHighContrast);
|
|
1103
1122
|
finalSat = satFactor * ctx.saturation / 100;
|
|
1104
1123
|
} else {
|
|
1105
1124
|
finalL = lightL;
|
|
@@ -1565,6 +1584,7 @@ glaze.configure = function configure(config) {
|
|
|
1565
1584
|
lightLightness: config.lightLightness ?? globalConfig.lightLightness,
|
|
1566
1585
|
darkLightness: config.darkLightness ?? globalConfig.darkLightness,
|
|
1567
1586
|
darkDesaturation: config.darkDesaturation ?? globalConfig.darkDesaturation,
|
|
1587
|
+
darkCurve: config.darkCurve ?? globalConfig.darkCurve,
|
|
1568
1588
|
states: {
|
|
1569
1589
|
dark: config.states?.dark ?? globalConfig.states.dark,
|
|
1570
1590
|
highContrast: config.states?.highContrast ?? globalConfig.states.highContrast
|
|
@@ -1664,6 +1684,7 @@ glaze.resetConfig = function resetConfig() {
|
|
|
1664
1684
|
lightLightness: [10, 100],
|
|
1665
1685
|
darkLightness: [15, 95],
|
|
1666
1686
|
darkDesaturation: .1,
|
|
1687
|
+
darkCurve: .5,
|
|
1667
1688
|
states: {
|
|
1668
1689
|
dark: "@dark",
|
|
1669
1690
|
highContrast: "@high-contrast"
|