@tenphi/glaze 0.11.1 → 0.12.0
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/dist/index.cjs +218 -152
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +114 -33
- package/dist/index.d.mts +114 -33
- package/dist/index.mjs +218 -152
- package/dist/index.mjs.map +1 -1
- package/docs/api.md +17 -11
- package/docs/methodology.md +12 -6
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -577,7 +577,8 @@ function defaultConfig() {
|
|
|
577
577
|
modes: {
|
|
578
578
|
dark: true,
|
|
579
579
|
highContrast: false
|
|
580
|
-
}
|
|
580
|
+
},
|
|
581
|
+
autoFlip: true
|
|
581
582
|
};
|
|
582
583
|
}
|
|
583
584
|
let globalConfig = defaultConfig();
|
|
@@ -616,7 +617,8 @@ function configure(config) {
|
|
|
616
617
|
dark: config.modes?.dark ?? globalConfig.modes.dark,
|
|
617
618
|
highContrast: config.modes?.highContrast ?? globalConfig.modes.highContrast
|
|
618
619
|
},
|
|
619
|
-
shadowTuning: config.shadowTuning ?? globalConfig.shadowTuning
|
|
620
|
+
shadowTuning: config.shadowTuning ?? globalConfig.shadowTuning,
|
|
621
|
+
autoFlip: config.autoFlip ?? globalConfig.autoFlip
|
|
620
622
|
};
|
|
621
623
|
}
|
|
622
624
|
function resetConfig() {
|
|
@@ -825,47 +827,64 @@ function findLightnessForContrast(options) {
|
|
|
825
827
|
branch: "preferred"
|
|
826
828
|
};
|
|
827
829
|
const [minL, maxL] = lightnessRange;
|
|
828
|
-
const
|
|
829
|
-
const
|
|
830
|
-
|
|
831
|
-
if (
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
if (
|
|
835
|
-
if (Math.abs(darkerResult.lightness - preferredLightness) <= Math.abs(lighterResult.lightness - preferredLightness)) return {
|
|
836
|
-
...darkerResult,
|
|
837
|
-
branch: "darker"
|
|
838
|
-
};
|
|
839
|
-
return {
|
|
840
|
-
...lighterResult,
|
|
841
|
-
branch: "lighter"
|
|
842
|
-
};
|
|
843
|
-
}
|
|
844
|
-
if (darkerPasses) return {
|
|
845
|
-
...darkerResult,
|
|
846
|
-
branch: "darker"
|
|
847
|
-
};
|
|
848
|
-
if (lighterPasses) return {
|
|
849
|
-
...lighterResult,
|
|
850
|
-
branch: "lighter"
|
|
851
|
-
};
|
|
852
|
-
const candidates = [];
|
|
853
|
-
if (darkerResult) candidates.push({
|
|
854
|
-
...darkerResult,
|
|
855
|
-
branch: "darker"
|
|
856
|
-
});
|
|
857
|
-
if (lighterResult) candidates.push({
|
|
858
|
-
...lighterResult,
|
|
859
|
-
branch: "lighter"
|
|
860
|
-
});
|
|
861
|
-
if (candidates.length === 0) return {
|
|
830
|
+
const canDarker = preferredLightness > minL;
|
|
831
|
+
const canLighter = preferredLightness < maxL;
|
|
832
|
+
let initialIsDarker;
|
|
833
|
+
if (options.initialDirection !== void 0) initialIsDarker = options.initialDirection === "darker";
|
|
834
|
+
else if (canDarker && !canLighter) initialIsDarker = true;
|
|
835
|
+
else if (!canDarker && canLighter) initialIsDarker = false;
|
|
836
|
+
else if (!canDarker && !canLighter) return {
|
|
862
837
|
lightness: preferredLightness,
|
|
863
838
|
contrast: crPref,
|
|
864
839
|
met: false,
|
|
865
840
|
branch: "preferred"
|
|
866
841
|
};
|
|
867
|
-
|
|
868
|
-
|
|
842
|
+
else {
|
|
843
|
+
const yMinExt = cachedLuminance(hue, saturation, minL);
|
|
844
|
+
const yMaxExt = cachedLuminance(hue, saturation, maxL);
|
|
845
|
+
initialIsDarker = contrastRatioFromLuminance(yMinExt, yBase) >= contrastRatioFromLuminance(yMaxExt, yBase);
|
|
846
|
+
}
|
|
847
|
+
const searchInitial = () => initialIsDarker ? searchBranch(hue, saturation, minL, preferredLightness, yBase, searchTarget, epsilon, maxIterations, preferredLightness) : searchBranch(hue, saturation, preferredLightness, maxL, yBase, searchTarget, epsilon, maxIterations, preferredLightness);
|
|
848
|
+
const searchOpposite = () => initialIsDarker ? searchBranch(hue, saturation, preferredLightness, maxL, yBase, searchTarget, epsilon, maxIterations, preferredLightness) : searchBranch(hue, saturation, minL, preferredLightness, yBase, searchTarget, epsilon, maxIterations, preferredLightness);
|
|
849
|
+
const initialBranchName = initialIsDarker ? "darker" : "lighter";
|
|
850
|
+
const oppositeBranchName = initialIsDarker ? "lighter" : "darker";
|
|
851
|
+
const initialResult = searchInitial();
|
|
852
|
+
initialResult.met = initialResult.contrast >= target;
|
|
853
|
+
if (initialResult.met && !options.flip) return {
|
|
854
|
+
...initialResult,
|
|
855
|
+
branch: initialBranchName
|
|
856
|
+
};
|
|
857
|
+
if (options.flip) {
|
|
858
|
+
const oppositeResult = (initialIsDarker ? canLighter : canDarker) ? searchOpposite() : null;
|
|
859
|
+
if (oppositeResult) oppositeResult.met = oppositeResult.contrast >= target;
|
|
860
|
+
if (initialResult.met && oppositeResult?.met) {
|
|
861
|
+
if (Math.abs(initialResult.lightness - preferredLightness) <= Math.abs(oppositeResult.lightness - preferredLightness)) return {
|
|
862
|
+
...initialResult,
|
|
863
|
+
branch: initialBranchName
|
|
864
|
+
};
|
|
865
|
+
return {
|
|
866
|
+
...oppositeResult,
|
|
867
|
+
branch: oppositeBranchName,
|
|
868
|
+
flipped: true
|
|
869
|
+
};
|
|
870
|
+
}
|
|
871
|
+
if (initialResult.met) return {
|
|
872
|
+
...initialResult,
|
|
873
|
+
branch: initialBranchName
|
|
874
|
+
};
|
|
875
|
+
if (oppositeResult?.met) return {
|
|
876
|
+
...oppositeResult,
|
|
877
|
+
branch: oppositeBranchName,
|
|
878
|
+
flipped: true
|
|
879
|
+
};
|
|
880
|
+
}
|
|
881
|
+
const extreme = initialIsDarker ? minL : maxL;
|
|
882
|
+
return {
|
|
883
|
+
lightness: extreme,
|
|
884
|
+
contrast: contrastRatioFromLuminance(cachedLuminance(hue, saturation, extreme), yBase),
|
|
885
|
+
met: false,
|
|
886
|
+
branch: initialBranchName
|
|
887
|
+
};
|
|
869
888
|
}
|
|
870
889
|
/**
|
|
871
890
|
* Binary-search one branch [lo, hi] for the nearest passing mix value
|
|
@@ -947,53 +966,59 @@ function findValueForMixContrast(options) {
|
|
|
947
966
|
contrast: crPref,
|
|
948
967
|
met: true
|
|
949
968
|
};
|
|
950
|
-
const
|
|
951
|
-
const
|
|
952
|
-
|
|
953
|
-
if (
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
if (darkerPasses && lighterPasses) {
|
|
957
|
-
if (Math.abs(darkerResult.lightness - preferredValue) <= Math.abs(lighterResult.lightness - preferredValue)) return {
|
|
958
|
-
value: darkerResult.lightness,
|
|
959
|
-
contrast: darkerResult.contrast,
|
|
960
|
-
met: true
|
|
961
|
-
};
|
|
962
|
-
return {
|
|
963
|
-
value: lighterResult.lightness,
|
|
964
|
-
contrast: lighterResult.contrast,
|
|
965
|
-
met: true
|
|
966
|
-
};
|
|
967
|
-
}
|
|
968
|
-
if (darkerPasses) return {
|
|
969
|
-
value: darkerResult.lightness,
|
|
970
|
-
contrast: darkerResult.contrast,
|
|
971
|
-
met: true
|
|
972
|
-
};
|
|
973
|
-
if (lighterPasses) return {
|
|
974
|
-
value: lighterResult.lightness,
|
|
975
|
-
contrast: lighterResult.contrast,
|
|
976
|
-
met: true
|
|
977
|
-
};
|
|
978
|
-
const candidates = [];
|
|
979
|
-
if (darkerResult) candidates.push({
|
|
980
|
-
...darkerResult,
|
|
981
|
-
branch: "lower"
|
|
982
|
-
});
|
|
983
|
-
if (lighterResult) candidates.push({
|
|
984
|
-
...lighterResult,
|
|
985
|
-
branch: "upper"
|
|
986
|
-
});
|
|
987
|
-
if (candidates.length === 0) return {
|
|
969
|
+
const canLower = preferredValue > 0;
|
|
970
|
+
const canUpper = preferredValue < 1;
|
|
971
|
+
let initialIsLower;
|
|
972
|
+
if (canLower && !canUpper) initialIsLower = true;
|
|
973
|
+
else if (!canLower && canUpper) initialIsLower = false;
|
|
974
|
+
else if (!canLower && !canUpper) return {
|
|
988
975
|
value: preferredValue,
|
|
989
976
|
contrast: crPref,
|
|
990
977
|
met: false
|
|
991
978
|
};
|
|
992
|
-
|
|
979
|
+
else initialIsLower = contrastRatioFromLuminance(luminanceAtValue(0), yBase) >= contrastRatioFromLuminance(luminanceAtValue(1), yBase);
|
|
980
|
+
const searchInitial = () => initialIsLower ? searchMixBranch(0, preferredValue, yBase, searchTarget, epsilon, maxIterations, preferredValue, luminanceAtValue) : searchMixBranch(preferredValue, 1, yBase, searchTarget, epsilon, maxIterations, preferredValue, luminanceAtValue);
|
|
981
|
+
const searchOpposite = () => initialIsLower ? searchMixBranch(preferredValue, 1, yBase, searchTarget, epsilon, maxIterations, preferredValue, luminanceAtValue) : searchMixBranch(0, preferredValue, yBase, searchTarget, epsilon, maxIterations, preferredValue, luminanceAtValue);
|
|
982
|
+
const initialResult = searchInitial();
|
|
983
|
+
initialResult.met = initialResult.contrast >= target;
|
|
984
|
+
if (initialResult.met && !options.flip) return {
|
|
985
|
+
value: initialResult.lightness,
|
|
986
|
+
contrast: initialResult.contrast,
|
|
987
|
+
met: true
|
|
988
|
+
};
|
|
989
|
+
if (options.flip) {
|
|
990
|
+
const oppositeResult = (initialIsLower ? canUpper : canLower) ? searchOpposite() : null;
|
|
991
|
+
if (oppositeResult) oppositeResult.met = oppositeResult.contrast >= target;
|
|
992
|
+
if (initialResult.met && oppositeResult?.met) {
|
|
993
|
+
if (Math.abs(initialResult.lightness - preferredValue) <= Math.abs(oppositeResult.lightness - preferredValue)) return {
|
|
994
|
+
value: initialResult.lightness,
|
|
995
|
+
contrast: initialResult.contrast,
|
|
996
|
+
met: true
|
|
997
|
+
};
|
|
998
|
+
return {
|
|
999
|
+
value: oppositeResult.lightness,
|
|
1000
|
+
contrast: oppositeResult.contrast,
|
|
1001
|
+
met: true,
|
|
1002
|
+
flipped: true
|
|
1003
|
+
};
|
|
1004
|
+
}
|
|
1005
|
+
if (initialResult.met) return {
|
|
1006
|
+
value: initialResult.lightness,
|
|
1007
|
+
contrast: initialResult.contrast,
|
|
1008
|
+
met: true
|
|
1009
|
+
};
|
|
1010
|
+
if (oppositeResult?.met) return {
|
|
1011
|
+
value: oppositeResult.lightness,
|
|
1012
|
+
contrast: oppositeResult.contrast,
|
|
1013
|
+
met: true,
|
|
1014
|
+
flipped: true
|
|
1015
|
+
};
|
|
1016
|
+
}
|
|
1017
|
+
const extreme = initialIsLower ? 0 : 1;
|
|
993
1018
|
return {
|
|
994
|
-
value:
|
|
995
|
-
contrast:
|
|
996
|
-
met:
|
|
1019
|
+
value: extreme,
|
|
1020
|
+
contrast: contrastRatioFromLuminance(luminanceAtValue(extreme), yBase),
|
|
1021
|
+
met: false
|
|
997
1022
|
};
|
|
998
1023
|
}
|
|
999
1024
|
|
|
@@ -1307,13 +1332,19 @@ function resolveDependentColor(name, def, ctx, isHighContrast, isDark, effective
|
|
|
1307
1332
|
const effectiveSat = isDark ? mapSaturationDark(satFactor * ctx.saturation / 100, mode) : satFactor * ctx.saturation / 100;
|
|
1308
1333
|
const baseLinearRgb = okhslToLinearSrgb(baseVariant.h, baseVariant.s, baseVariant.l);
|
|
1309
1334
|
const windowRange = schemeLightnessRange(isDark, mode, isHighContrast, ctx.scaling);
|
|
1335
|
+
const autoFlip = ctx.autoFlip ?? getConfig().autoFlip;
|
|
1336
|
+
let initialDirection;
|
|
1337
|
+
if (preferredL < baseL) initialDirection = "darker";
|
|
1338
|
+
else if (preferredL > baseL) initialDirection = "lighter";
|
|
1310
1339
|
const result = findLightnessForContrast({
|
|
1311
1340
|
hue: effectiveHue,
|
|
1312
1341
|
saturation: effectiveSat,
|
|
1313
1342
|
preferredLightness: clamp(preferredL / 100, windowRange[0], windowRange[1]),
|
|
1314
1343
|
baseLinearRgb,
|
|
1315
1344
|
contrast: minCr,
|
|
1316
|
-
lightnessRange: [0, 1]
|
|
1345
|
+
lightnessRange: [0, 1],
|
|
1346
|
+
initialDirection,
|
|
1347
|
+
flip: autoFlip
|
|
1317
1348
|
});
|
|
1318
1349
|
if (!result.met) warnContrastUnmet(name, isDark, isHighContrast, minCr, result.contrast);
|
|
1319
1350
|
return {
|
|
@@ -1429,12 +1460,14 @@ function resolveMixForScheme(def, ctx, isDark, isHighContrast) {
|
|
|
1429
1460
|
else luminanceAt = (v) => {
|
|
1430
1461
|
return gamutClampedLuminance(okhslToLinearSrgb(mixHue(baseVariant, targetVariant, v), baseVariant.s + (targetVariant.s - baseVariant.s) * v, baseVariant.l + (targetVariant.l - baseVariant.l) * v));
|
|
1431
1462
|
};
|
|
1463
|
+
const autoFlip = ctx.autoFlip ?? getConfig().autoFlip;
|
|
1432
1464
|
t = findValueForMixContrast({
|
|
1433
1465
|
preferredValue: t,
|
|
1434
1466
|
baseLinearRgb: baseLinear,
|
|
1435
1467
|
targetLinearRgb: targetLinear,
|
|
1436
1468
|
contrast: minCr,
|
|
1437
|
-
luminanceAtValue: luminanceAt
|
|
1469
|
+
luminanceAtValue: luminanceAt,
|
|
1470
|
+
flip: autoFlip
|
|
1438
1471
|
}).value;
|
|
1439
1472
|
}
|
|
1440
1473
|
if (blend === "transparent") return {
|
|
@@ -1495,15 +1528,17 @@ function seedField(order, ctx, field, source) {
|
|
|
1495
1528
|
});
|
|
1496
1529
|
}
|
|
1497
1530
|
}
|
|
1498
|
-
function resolveAllColors(hue, saturation, defs, scaling, externalBases) {
|
|
1531
|
+
function resolveAllColors(hue, saturation, defs, scaling, externalBases, overrideAutoFlip) {
|
|
1499
1532
|
validateColorDefs(defs, externalBases);
|
|
1500
1533
|
const order = topoSort(defs);
|
|
1534
|
+
const cfg = getConfig();
|
|
1501
1535
|
const ctx = {
|
|
1502
1536
|
hue,
|
|
1503
1537
|
saturation,
|
|
1504
1538
|
defs,
|
|
1505
1539
|
resolved: /* @__PURE__ */ new Map(),
|
|
1506
|
-
scaling
|
|
1540
|
+
scaling,
|
|
1541
|
+
autoFlip: overrideAutoFlip ?? cfg.autoFlip
|
|
1507
1542
|
};
|
|
1508
1543
|
if (externalBases) for (const [name, color] of externalBases) ctx.resolved.set(name, color);
|
|
1509
1544
|
const lightMap = runPass(order, defs, ctx, false, false, "light");
|
|
@@ -1625,7 +1660,7 @@ function buildCssMap(resolved, prefix, suffix, format) {
|
|
|
1625
1660
|
* Standalone single-color tokens (`glaze.color()` / `glaze.colorFrom()`).
|
|
1626
1661
|
*
|
|
1627
1662
|
* Owns the value-shorthand parser (hex, `rgb()` / `hsl()` / `okhsl()` /
|
|
1628
|
-
* `oklch()`,
|
|
1663
|
+
* `oklch()`, `{ r, g, b }`, `{ h, s, l }`, `{ l, c, h }`), the structured-input
|
|
1629
1664
|
* validator, the two factory paths (value vs structured), and the
|
|
1630
1665
|
* JSON-safe export / rehydration round-trip.
|
|
1631
1666
|
*
|
|
@@ -1648,29 +1683,24 @@ const RESERVED_STANDALONE_NAMES = new Set([
|
|
|
1648
1683
|
STANDALONE_BASE
|
|
1649
1684
|
]);
|
|
1650
1685
|
/**
|
|
1651
|
-
*
|
|
1652
|
-
*
|
|
1653
|
-
*
|
|
1654
|
-
*
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
*
|
|
1664
|
-
|
|
1665
|
-
|
|
1686
|
+
* Create-time scaling for all value-shorthand `glaze.color()` inputs.
|
|
1687
|
+
* Light lightness is preserved (`lightLightness: false`); dark uses the
|
|
1688
|
+
* theme window from `globalConfig.darkLightness`, snapshotted at create
|
|
1689
|
+
* time so later `configure()` does not retroactively change tokens.
|
|
1690
|
+
*/
|
|
1691
|
+
function defaultValueShorthandScaling() {
|
|
1692
|
+
return {
|
|
1693
|
+
lightLightness: false,
|
|
1694
|
+
darkLightness: getConfig().darkLightness
|
|
1695
|
+
};
|
|
1696
|
+
}
|
|
1697
|
+
/**
|
|
1698
|
+
* Create-time scaling for structured `glaze.color({ hue, saturation,
|
|
1699
|
+
* lightness, ... })`. Both windows come from `globalConfig` so the
|
|
1700
|
+
* token behaves like an ordinary theme color on light and dark sides.
|
|
1701
|
+
*/
|
|
1702
|
+
function defaultStructuredScaling() {
|
|
1666
1703
|
const cfg = getConfig();
|
|
1667
|
-
if (isString) {
|
|
1668
|
-
const [darkLo] = cfg.darkLightness;
|
|
1669
|
-
return {
|
|
1670
|
-
lightLightness: false,
|
|
1671
|
-
darkLightness: [darkLo, 100]
|
|
1672
|
-
};
|
|
1673
|
-
}
|
|
1674
1704
|
return {
|
|
1675
1705
|
lightLightness: cfg.lightLightness,
|
|
1676
1706
|
darkLightness: cfg.darkLightness
|
|
@@ -1804,9 +1834,41 @@ function validateOkhslColor(value) {
|
|
|
1804
1834
|
if (!Number.isFinite(h) || !Number.isFinite(s) || !Number.isFinite(l)) throw new Error("glaze.color: OkhslColor h/s/l must be finite numbers.");
|
|
1805
1835
|
if (s > 1.5 || l > 1.5) throw new Error("glaze.color: OkhslColor s/l must be in 0–1 range. Did you mean the structured form { hue, saturation, lightness } (which uses 0–100)?");
|
|
1806
1836
|
}
|
|
1807
|
-
/** Validate a user-supplied `
|
|
1808
|
-
function
|
|
1809
|
-
for (const
|
|
1837
|
+
/** Validate a user-supplied `{ r, g, b }` object in 0–255. */
|
|
1838
|
+
function validateRgbColor(value) {
|
|
1839
|
+
for (const key of [
|
|
1840
|
+
"r",
|
|
1841
|
+
"g",
|
|
1842
|
+
"b"
|
|
1843
|
+
]) {
|
|
1844
|
+
const n = value[key];
|
|
1845
|
+
if (!Number.isFinite(n) || n < 0 || n > 255) throw new Error(`glaze.color: RgbColor ${key} must be a finite number in 0–255 (got ${n}).`);
|
|
1846
|
+
}
|
|
1847
|
+
}
|
|
1848
|
+
/** Validate a user-supplied `{ l, c, h }` OKLCh object. */
|
|
1849
|
+
function validateOklchColor(value) {
|
|
1850
|
+
const { l, c, h } = value;
|
|
1851
|
+
if (!Number.isFinite(l) || !Number.isFinite(c) || !Number.isFinite(h)) throw new Error("glaze.color: OklchColor l/c/h must be finite numbers.");
|
|
1852
|
+
if (l > 1.5 || c > 1.5) throw new Error("glaze.color: OklchColor l/c must be in 0–1 range (matching oklch() strings).");
|
|
1853
|
+
}
|
|
1854
|
+
function oklchComponentsToOkhsl(l, c, hDeg) {
|
|
1855
|
+
const hRad = hDeg * Math.PI / 180;
|
|
1856
|
+
const [h, s, outL] = oklabToOkhsl([
|
|
1857
|
+
l,
|
|
1858
|
+
c * Math.cos(hRad),
|
|
1859
|
+
c * Math.sin(hRad)
|
|
1860
|
+
]);
|
|
1861
|
+
return {
|
|
1862
|
+
h,
|
|
1863
|
+
s,
|
|
1864
|
+
l: outL
|
|
1865
|
+
};
|
|
1866
|
+
}
|
|
1867
|
+
function isRgbColorObject(value) {
|
|
1868
|
+
return "r" in value && "g" in value && "b" in value;
|
|
1869
|
+
}
|
|
1870
|
+
function isOklchColorObject(value) {
|
|
1871
|
+
return "c" in value && "l" in value && "h" in value;
|
|
1810
1872
|
}
|
|
1811
1873
|
/**
|
|
1812
1874
|
* Validate a user-supplied `opacity` override on `glaze.color()`.
|
|
@@ -1852,18 +1914,17 @@ function validateStandaloneName(name) {
|
|
|
1852
1914
|
/**
|
|
1853
1915
|
* Extract an OKHSL color from any `GlazeColorValue` form. Also used by
|
|
1854
1916
|
* `glaze.shadow()` so all shadow inputs (hex, color functions, OKHSL,
|
|
1855
|
-
*
|
|
1917
|
+
* literal objects) go through one parser.
|
|
1856
1918
|
*/
|
|
1857
1919
|
function extractOkhslFromValue(value) {
|
|
1858
1920
|
if (typeof value === "string") return parseColorString(value);
|
|
1859
|
-
if (Array.isArray(value)) {
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
const [r, g, b] = tuple;
|
|
1921
|
+
if (Array.isArray(value)) throw new Error("glaze.color: RGB tuple [r, g, b] is no longer supported — use { r, g, b } instead.");
|
|
1922
|
+
if (isRgbColorObject(value)) {
|
|
1923
|
+
validateRgbColor(value);
|
|
1863
1924
|
const [h, s, l] = srgbToOkhsl([
|
|
1864
|
-
r / 255,
|
|
1865
|
-
g / 255,
|
|
1866
|
-
b / 255
|
|
1925
|
+
value.r / 255,
|
|
1926
|
+
value.g / 255,
|
|
1927
|
+
value.b / 255
|
|
1867
1928
|
]);
|
|
1868
1929
|
return {
|
|
1869
1930
|
h,
|
|
@@ -1871,6 +1932,10 @@ function extractOkhslFromValue(value) {
|
|
|
1871
1932
|
l
|
|
1872
1933
|
};
|
|
1873
1934
|
}
|
|
1935
|
+
if (isOklchColorObject(value)) {
|
|
1936
|
+
validateOklchColor(value);
|
|
1937
|
+
return oklchComponentsToOkhsl(value.l, value.c, value.h);
|
|
1938
|
+
}
|
|
1874
1939
|
validateOkhslColor(value);
|
|
1875
1940
|
return value;
|
|
1876
1941
|
}
|
|
@@ -1878,11 +1943,9 @@ function extractOkhslFromValue(value) {
|
|
|
1878
1943
|
* Build the `ColorMap` for a value-shorthand `glaze.color()` call.
|
|
1879
1944
|
*
|
|
1880
1945
|
* The user-facing color (`STANDALONE_VALUE`) defaults to `mode: 'auto'`
|
|
1881
|
-
* across every value-shorthand form
|
|
1882
|
-
*
|
|
1883
|
-
*
|
|
1884
|
-
* snapshotted `globalConfig.lightLightness` / `globalConfig.darkLightness`
|
|
1885
|
-
* windows.
|
|
1946
|
+
* across every value-shorthand form, using the snapshotted
|
|
1947
|
+
* `globalConfig.darkLightness` window (light lightness preserved via
|
|
1948
|
+
* `lightLightness: false`).
|
|
1886
1949
|
*
|
|
1887
1950
|
* When the user requests `contrast` or relative `lightness`, a hidden
|
|
1888
1951
|
* `STANDALONE_SEED` def is synthesized at `mode: 'static'`. That keeps
|
|
@@ -1923,11 +1986,11 @@ function buildStandaloneValueDefs(main, options) {
|
|
|
1923
1986
|
primary
|
|
1924
1987
|
};
|
|
1925
1988
|
}
|
|
1926
|
-
function createColorTokenFromDefs(seedHue, seedSaturation, defs, primary, effectiveScaling, baseToken, exportData) {
|
|
1989
|
+
function createColorTokenFromDefs(seedHue, seedSaturation, defs, primary, effectiveScaling, baseToken, exportData, autoFlip) {
|
|
1927
1990
|
let cached;
|
|
1928
1991
|
const resolveOnce = () => {
|
|
1929
1992
|
if (cached) return cached;
|
|
1930
|
-
cached = resolveAllColors(seedHue, seedSaturation, defs, effectiveScaling, baseToken ? new Map([[STANDALONE_BASE, baseToken.resolve()]]) : void 0);
|
|
1993
|
+
cached = resolveAllColors(seedHue, seedSaturation, defs, effectiveScaling, baseToken ? new Map([[STANDALONE_BASE, baseToken.resolve()]]) : void 0, autoFlip);
|
|
1931
1994
|
return cached;
|
|
1932
1995
|
};
|
|
1933
1996
|
const resolveStates = (options) => {
|
|
@@ -1966,7 +2029,7 @@ function resolveBaseToken(base) {
|
|
|
1966
2029
|
if (isGlazeColorToken(base)) return base;
|
|
1967
2030
|
return createColorTokenFromValue(base, void 0, void 0);
|
|
1968
2031
|
}
|
|
1969
|
-
function createColorToken(input, scaling) {
|
|
2032
|
+
function createColorToken(input, scaling, overrideAutoFlip) {
|
|
1970
2033
|
validateStructuredInput(input);
|
|
1971
2034
|
const userName = input.name;
|
|
1972
2035
|
if (userName !== void 0) validateStandaloneName(userName);
|
|
@@ -1987,27 +2050,30 @@ function createColorToken(input, scaling) {
|
|
|
1987
2050
|
saturation: 1,
|
|
1988
2051
|
mode: "static"
|
|
1989
2052
|
};
|
|
1990
|
-
const effectiveScaling = scaling ??
|
|
2053
|
+
const effectiveScaling = scaling ?? defaultStructuredScaling();
|
|
2054
|
+
const autoFlip = overrideAutoFlip ?? getConfig().autoFlip;
|
|
1991
2055
|
const exportData = () => ({
|
|
1992
2056
|
form: "structured",
|
|
1993
2057
|
input: buildStructuredInputExport(input),
|
|
1994
|
-
scaling: effectiveScaling
|
|
2058
|
+
scaling: effectiveScaling,
|
|
2059
|
+
autoFlip
|
|
1995
2060
|
});
|
|
1996
|
-
return createColorTokenFromDefs(input.hue, input.saturation, defs, primary, effectiveScaling, baseToken, exportData);
|
|
2061
|
+
return createColorTokenFromDefs(input.hue, input.saturation, defs, primary, effectiveScaling, baseToken, exportData, autoFlip);
|
|
1997
2062
|
}
|
|
1998
|
-
function createColorTokenFromValue(value, options, scaling) {
|
|
1999
|
-
const inputIsString = typeof value === "string";
|
|
2063
|
+
function createColorTokenFromValue(value, options, scaling, overrideAutoFlip) {
|
|
2000
2064
|
const main = extractOkhslFromValue(value);
|
|
2001
2065
|
const baseToken = resolveBaseToken(options?.base);
|
|
2002
2066
|
const { seedHue, seedSaturation, defs, primary } = buildStandaloneValueDefs(main, options);
|
|
2003
|
-
const effectiveScaling = scaling ??
|
|
2067
|
+
const effectiveScaling = scaling ?? defaultValueShorthandScaling();
|
|
2068
|
+
const autoFlip = overrideAutoFlip ?? getConfig().autoFlip;
|
|
2004
2069
|
const exportData = () => ({
|
|
2005
2070
|
form: "value",
|
|
2006
2071
|
input: value,
|
|
2007
2072
|
...options !== void 0 ? { overrides: buildOverridesExport(options) } : {},
|
|
2008
|
-
scaling: effectiveScaling
|
|
2073
|
+
scaling: effectiveScaling,
|
|
2074
|
+
autoFlip
|
|
2009
2075
|
});
|
|
2010
|
-
return createColorTokenFromDefs(seedHue, seedSaturation, defs, primary, effectiveScaling, baseToken, exportData);
|
|
2076
|
+
return createColorTokenFromDefs(seedHue, seedSaturation, defs, primary, effectiveScaling, baseToken, exportData, autoFlip);
|
|
2011
2077
|
}
|
|
2012
2078
|
/**
|
|
2013
2079
|
* Build a JSON-safe snapshot of `GlazeColorOverrides`. `base` is
|
|
@@ -2086,9 +2152,15 @@ function colorFromExport(data) {
|
|
|
2086
2152
|
if (data.input === void 0) throw new Error(`glaze.colorFrom: missing "input" field — expected the original ${data.form === "value" ? "GlazeColorValue" : "GlazeColorInput"}.`);
|
|
2087
2153
|
if (data.form === "value") {
|
|
2088
2154
|
const value = data.input;
|
|
2089
|
-
|
|
2155
|
+
const overrides = data.overrides ? rehydrateOverrides(data.overrides) : void 0;
|
|
2156
|
+
const cfg = getConfig();
|
|
2157
|
+
const effectiveAutoFlip = data.autoFlip ?? cfg.autoFlip;
|
|
2158
|
+
return createColorTokenFromValue(value, overrides, data.scaling, effectiveAutoFlip);
|
|
2090
2159
|
}
|
|
2091
|
-
|
|
2160
|
+
const input = rehydrateStructuredInput(data.input);
|
|
2161
|
+
const cfg = getConfig();
|
|
2162
|
+
const effectiveAutoFlip = data.autoFlip ?? cfg.autoFlip;
|
|
2163
|
+
return createColorToken(input, data.scaling, effectiveAutoFlip);
|
|
2092
2164
|
}
|
|
2093
2165
|
|
|
2094
2166
|
//#endregion
|
|
@@ -2364,22 +2436,16 @@ glaze.from = function from(data) {
|
|
|
2364
2436
|
* lightness-window override.
|
|
2365
2437
|
* - `glaze.color(value, overrides?, scaling?)` — value-shorthand: a hex
|
|
2366
2438
|
* string (3/6/8 digits), one of the CSS color functions Glaze itself
|
|
2367
|
-
* emits (`rgb()`, `hsl()`, `okhsl()`, `oklch()`),
|
|
2368
|
-
*
|
|
2439
|
+
* emits (`rgb()`, `hsl()`, `okhsl()`, `oklch()`), or literal objects
|
|
2440
|
+
* `{ r, g, b }` (0–255), `{ h, s, l }` (OKHSL 0–1), `{ l, c, h }`
|
|
2441
|
+
* (OKLCh, matching `oklch()` strings).
|
|
2369
2442
|
*
|
|
2370
|
-
* Defaults: every input form defaults to `mode: 'auto'
|
|
2371
|
-
*
|
|
2372
|
-
*
|
|
2373
|
-
*
|
|
2374
|
-
*
|
|
2375
|
-
*
|
|
2376
|
-
* exactly; dark Möbius-inverts up to 100, so `glaze.color('#000')`
|
|
2377
|
-
* renders as `#fff` in dark mode (and `glaze.color('#fff')` falls to
|
|
2378
|
-
* the dark `lo` floor).
|
|
2379
|
-
* - `OkhslColor` object / RGB-tuple / structured value-shorthand:
|
|
2380
|
-
* `{ lightLightness: globalConfig.lightLightness, darkLightness:
|
|
2381
|
-
* globalConfig.darkLightness }` — both windows come straight from
|
|
2382
|
-
* `globalConfig`, so the resulting token behaves like a theme color.
|
|
2443
|
+
* Defaults: every input form defaults to `mode: 'auto'`. Value-shorthand
|
|
2444
|
+
* (strings and literal objects) snapshots `{ lightLightness: false,
|
|
2445
|
+
* darkLightness: globalConfig.darkLightness }` — light preserves the
|
|
2446
|
+
* input; dark uses the theme window. Structured `{ hue, saturation,
|
|
2447
|
+
* lightness, ... }` snapshots both `globalConfig` windows like a theme
|
|
2448
|
+
* color.
|
|
2383
2449
|
*
|
|
2384
2450
|
* Pass `{ mode: 'fixed' }` to opt back into the legacy linear, non-
|
|
2385
2451
|
* inverting mapping, or `{ mode: 'static' }` to pin the same lightness
|
|
@@ -2404,7 +2470,7 @@ glaze.color = function color(input, arg2, arg3) {
|
|
|
2404
2470
|
*
|
|
2405
2471
|
* Both `bg` and `fg` accept any `GlazeColorValue` form: hex (`#rgb` /
|
|
2406
2472
|
* `#rrggbb` / `#rrggbbaa`), `rgb()` / `hsl()` / `okhsl()` / `oklch()`
|
|
2407
|
-
* strings, `
|
|
2473
|
+
* strings, or `{ r, g, b }` / `{ h, s, l }` / `{ l, c, h }` objects.
|
|
2408
2474
|
*/
|
|
2409
2475
|
glaze.shadow = function shadow(input) {
|
|
2410
2476
|
const bg = extractOkhslFromValue(input.bg);
|