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