@tenphi/glaze 0.0.0-snapshot.432b23c → 0.0.0-snapshot.575cb1c
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 +24 -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 +24 -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≈29 (power curve), sign flips → L=29+52=81
|
|
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 power curve within the configured window:
|
|
641
643
|
|
|
642
644
|
```ts
|
|
643
645
|
const [lo, hi] = darkLightness; // default: [15, 95]
|
|
644
|
-
const
|
|
646
|
+
const d = (100 - lightness) / 100;
|
|
647
|
+
const invertedL = lo + (hi - lo) * Math.pow(d, darkCurve); // darkCurve default: 0.5
|
|
645
648
|
```
|
|
646
649
|
|
|
647
|
-
|
|
650
|
+
The `darkCurve` exponent (default `0.5`) expands small light-theme deltas near white into larger usable deltas in dark mode. This preserves subtle surface hierarchy (e.g. L=97 vs L=95) that would otherwise collapse to near-identical dark values. Set `darkCurve: 1` for linear (legacy) behavior.
|
|
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 | 28.9 | 17.4 | 92.6 |
|
|
661
|
+
| accent-fill (L=52) | 52 | 70.4 | 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 pure inversion (`100 - L`), 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, // Power-curve exponent for dark auto-inversion (0–1)
|
|
910
918
|
states: {
|
|
911
919
|
dark: '@dark', // State alias for dark mode tokens
|
|
912
920
|
highContrast: '@high-contrast',
|
package/dist/index.cjs
CHANGED
|
@@ -470,7 +470,7 @@ function formatOklch(h, s, l) {
|
|
|
470
470
|
const C = Math.sqrt(a * a + b * b);
|
|
471
471
|
let hh = Math.atan2(b, a) * (180 / Math.PI);
|
|
472
472
|
hh = constrainAngle(hh);
|
|
473
|
-
return `oklch(${fmt$1(L, 4)} ${fmt$1(C, 4)} ${fmt$1(hh,
|
|
473
|
+
return `oklch(${fmt$1(L, 4)} ${fmt$1(C, 4)} ${fmt$1(hh, 2)})`;
|
|
474
474
|
}
|
|
475
475
|
|
|
476
476
|
//#endregion
|
|
@@ -620,7 +620,7 @@ function coarseScan(h, s, lo, hi, yBase, target, epsilon, maxIter) {
|
|
|
620
620
|
function findLightnessForContrast(options) {
|
|
621
621
|
const { hue, saturation, preferredLightness, baseLinearRgb, contrast: contrastInput, lightnessRange = [0, 1], epsilon = 1e-4, maxIterations = 14 } = options;
|
|
622
622
|
const target = resolveMinContrast(contrastInput);
|
|
623
|
-
const searchTarget = target * 1.
|
|
623
|
+
const searchTarget = target * 1.007;
|
|
624
624
|
const yBase = gamutClampedLuminance(baseLinearRgb);
|
|
625
625
|
const crPref = contrastRatioFromLuminance(cachedLuminance(hue, saturation, preferredLightness), yBase);
|
|
626
626
|
if (crPref >= searchTarget) return {
|
|
@@ -744,7 +744,7 @@ function searchMixBranch(lo, hi, yBase, target, epsilon, maxIter, preferred, lum
|
|
|
744
744
|
function findValueForMixContrast(options) {
|
|
745
745
|
const { preferredValue, baseLinearRgb, contrast: contrastInput, luminanceAtValue, epsilon = 1e-4, maxIterations = 20 } = options;
|
|
746
746
|
const target = resolveMinContrast(contrastInput);
|
|
747
|
-
const searchTarget = target * 1.
|
|
747
|
+
const searchTarget = target * 1.01;
|
|
748
748
|
const yBase = gamutClampedLuminance(baseLinearRgb);
|
|
749
749
|
const crPref = contrastRatioFromLuminance(luminanceAtValue(preferredValue), yBase);
|
|
750
750
|
if (crPref >= searchTarget) return {
|
|
@@ -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,23 +962,26 @@ function topoSort(defs) {
|
|
|
961
962
|
for (const name of Object.keys(defs)) visit(name);
|
|
962
963
|
return result;
|
|
963
964
|
}
|
|
964
|
-
function mapLightnessLight(l, mode) {
|
|
965
|
-
if (mode === "static") return l;
|
|
965
|
+
function mapLightnessLight(l, mode, isHighContrast) {
|
|
966
|
+
if (mode === "static" || isHighContrast) return l;
|
|
966
967
|
const [lo, hi] = globalConfig.lightLightness;
|
|
967
968
|
return l * (hi - lo) / 100 + lo;
|
|
968
969
|
}
|
|
969
|
-
function mapLightnessDark(l, mode) {
|
|
970
|
+
function mapLightnessDark(l, mode, isHighContrast) {
|
|
970
971
|
if (mode === "static") return l;
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
972
|
+
if (isHighContrast) return mode === "fixed" ? l : 100 - l;
|
|
973
|
+
const [darkLo, darkHi] = globalConfig.darkLightness;
|
|
974
|
+
if (mode === "fixed") return l * (darkHi - darkLo) / 100 + darkLo;
|
|
975
|
+
const [lightLo, lightHi] = globalConfig.lightLightness;
|
|
976
|
+
const t = (lightHi - (l * (lightHi - lightLo) / 100 + lightLo)) / (lightHi - lightLo);
|
|
977
|
+
return darkLo + (darkHi - darkLo) * Math.pow(t, globalConfig.darkCurve);
|
|
974
978
|
}
|
|
975
979
|
function mapSaturationDark(s, mode) {
|
|
976
980
|
if (mode === "static") return s;
|
|
977
981
|
return s * (1 - globalConfig.darkDesaturation);
|
|
978
982
|
}
|
|
979
|
-
function schemeLightnessRange(isDark, mode) {
|
|
980
|
-
if (mode === "static") return [0, 1];
|
|
983
|
+
function schemeLightnessRange(isDark, mode, isHighContrast) {
|
|
984
|
+
if (mode === "static" || isHighContrast) return [0, 1];
|
|
981
985
|
const [lo, hi] = isDark ? globalConfig.darkLightness : globalConfig.lightLightness;
|
|
982
986
|
return [lo / 100, hi / 100];
|
|
983
987
|
}
|
|
@@ -1040,23 +1044,23 @@ function resolveDependentColor(name, def, ctx, isHighContrast, isDark, effective
|
|
|
1040
1044
|
let delta = parsed.value;
|
|
1041
1045
|
if (isDark && mode === "auto") delta = -delta;
|
|
1042
1046
|
preferredL = clamp(baseL + delta, 0, 100);
|
|
1043
|
-
} else if (isDark) preferredL = mapLightnessDark(parsed.value, mode);
|
|
1044
|
-
else preferredL = mapLightnessLight(parsed.value, mode);
|
|
1047
|
+
} else if (isDark) preferredL = mapLightnessDark(parsed.value, mode, isHighContrast);
|
|
1048
|
+
else preferredL = mapLightnessLight(parsed.value, mode, isHighContrast);
|
|
1045
1049
|
}
|
|
1046
1050
|
const rawContrast = def.contrast;
|
|
1047
1051
|
if (rawContrast !== void 0) {
|
|
1048
1052
|
const minCr = isHighContrast ? pairHC(rawContrast) : pairNormal(rawContrast);
|
|
1049
1053
|
const effectiveSat = isDark ? mapSaturationDark(satFactor * ctx.saturation / 100, mode) : satFactor * ctx.saturation / 100;
|
|
1050
1054
|
const baseLinearRgb = okhslToLinearSrgb(baseVariant.h, baseVariant.s, baseVariant.l);
|
|
1051
|
-
const
|
|
1055
|
+
const windowRange = schemeLightnessRange(isDark, mode, isHighContrast);
|
|
1052
1056
|
return {
|
|
1053
1057
|
l: findLightnessForContrast({
|
|
1054
1058
|
hue: effectiveHue,
|
|
1055
1059
|
saturation: effectiveSat,
|
|
1056
|
-
preferredLightness: clamp(preferredL / 100,
|
|
1060
|
+
preferredLightness: clamp(preferredL / 100, windowRange[0], windowRange[1]),
|
|
1057
1061
|
baseLinearRgb,
|
|
1058
1062
|
contrast: minCr,
|
|
1059
|
-
lightnessRange
|
|
1063
|
+
lightnessRange: [0, 1]
|
|
1060
1064
|
}).lightness * 100,
|
|
1061
1065
|
satFactor
|
|
1062
1066
|
};
|
|
@@ -1093,13 +1097,13 @@ function resolveColorForScheme(name, def, ctx, isDark, isHighContrast) {
|
|
|
1093
1097
|
let finalL;
|
|
1094
1098
|
let finalSat;
|
|
1095
1099
|
if (isDark && isRoot) {
|
|
1096
|
-
finalL = mapLightnessDark(lightL, mode);
|
|
1100
|
+
finalL = mapLightnessDark(lightL, mode, isHighContrast);
|
|
1097
1101
|
finalSat = mapSaturationDark(satFactor * ctx.saturation / 100, mode);
|
|
1098
1102
|
} else if (isDark && !isRoot) {
|
|
1099
1103
|
finalL = lightL;
|
|
1100
1104
|
finalSat = mapSaturationDark(satFactor * ctx.saturation / 100, mode);
|
|
1101
1105
|
} else if (isRoot) {
|
|
1102
|
-
finalL = mapLightnessLight(lightL, mode);
|
|
1106
|
+
finalL = mapLightnessLight(lightL, mode, isHighContrast);
|
|
1103
1107
|
finalSat = satFactor * ctx.saturation / 100;
|
|
1104
1108
|
} else {
|
|
1105
1109
|
finalL = lightL;
|
|
@@ -1565,6 +1569,7 @@ glaze.configure = function configure(config) {
|
|
|
1565
1569
|
lightLightness: config.lightLightness ?? globalConfig.lightLightness,
|
|
1566
1570
|
darkLightness: config.darkLightness ?? globalConfig.darkLightness,
|
|
1567
1571
|
darkDesaturation: config.darkDesaturation ?? globalConfig.darkDesaturation,
|
|
1572
|
+
darkCurve: config.darkCurve ?? globalConfig.darkCurve,
|
|
1568
1573
|
states: {
|
|
1569
1574
|
dark: config.states?.dark ?? globalConfig.states.dark,
|
|
1570
1575
|
highContrast: config.states?.highContrast ?? globalConfig.states.highContrast
|
|
@@ -1664,6 +1669,7 @@ glaze.resetConfig = function resetConfig() {
|
|
|
1664
1669
|
lightLightness: [10, 100],
|
|
1665
1670
|
darkLightness: [15, 95],
|
|
1666
1671
|
darkDesaturation: .1,
|
|
1672
|
+
darkCurve: .5,
|
|
1667
1673
|
states: {
|
|
1668
1674
|
dark: "@dark",
|
|
1669
1675
|
highContrast: "@high-contrast"
|