@tenphi/glaze 1.4.0 → 1.5.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/README.md +1 -1
- package/dist/index.cjs +55 -29
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +18 -15
- package/dist/index.d.mts +18 -15
- package/dist/index.mjs +55 -29
- package/dist/index.mjs.map +1 -1
- package/docs/api.md +58 -35
- package/docs/methodology.md +15 -13
- package/docs/okhst.md +4 -0
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -234,9 +234,10 @@ interface GlazeOutputModes {
|
|
|
234
234
|
/**
|
|
235
235
|
* Include high-contrast variants (both light-HC and dark-HC). Default: false.
|
|
236
236
|
*
|
|
237
|
-
*
|
|
238
|
-
* high-contrast
|
|
239
|
-
*
|
|
237
|
+
* Independent of `contrastLevel` — the level positions the normal variants
|
|
238
|
+
* while these stay the true high-contrast resolution, so the two compose. The
|
|
239
|
+
* exception is a global level of 100, where the normal variants already *are*
|
|
240
|
+
* the high-contrast ones: the tier would duplicate them and is dropped.
|
|
240
241
|
* @see GlazeConfig.contrastLevel
|
|
241
242
|
*/
|
|
242
243
|
highContrast?: boolean;
|
|
@@ -572,15 +573,15 @@ interface GlazeConfig {
|
|
|
572
573
|
shadowTuning?: ShadowTuning;
|
|
573
574
|
/**
|
|
574
575
|
* Manual contrast level — a 0–100 slider from normal contrast (`0`) to high
|
|
575
|
-
* contrast (`100`), or `'auto'` (the default) to
|
|
576
|
-
*
|
|
576
|
+
* contrast (`100`), or `'auto'` (the default) to take the authored normal
|
|
577
|
+
* entries as-is.
|
|
577
578
|
*
|
|
578
|
-
* With a number, Glaze resolves the `light` / `dark` variants *at* that
|
|
579
|
-
*
|
|
580
|
-
*
|
|
581
|
-
*
|
|
582
|
-
*
|
|
583
|
-
*
|
|
579
|
+
* With a number, Glaze resolves the `light` / `dark` variants *at* that level.
|
|
580
|
+
* That is all it does: `lightContrast` / `darkContrast` stay the true
|
|
581
|
+
* high-contrast resolution at every level, and `modes.highContrast` alone
|
|
582
|
+
* decides whether they are emitted. Level `0` therefore reproduces `'auto'`
|
|
583
|
+
* bit for bit. At `100` the normal variants *are* the high-contrast ones, so a
|
|
584
|
+
* separate tier would duplicate them and a global level of 100 drops it.
|
|
584
585
|
*
|
|
585
586
|
* The level interpolates all three things that make high contrast differ:
|
|
586
587
|
* authored `[normal, highContrast]` pairs, the tone-window widening, and
|
|
@@ -630,8 +631,8 @@ interface GlazeConfigResolved {
|
|
|
630
631
|
shadowTuning?: ShadowTuning;
|
|
631
632
|
autoFlip: boolean;
|
|
632
633
|
/**
|
|
633
|
-
* Manual contrast level (0–100), or `'auto'`
|
|
634
|
-
* normal
|
|
634
|
+
* Manual contrast level (0–100) for the normal variants, or `'auto'` to take
|
|
635
|
+
* the authored normal entries as-is.
|
|
635
636
|
* @see GlazeConfig.contrastLevel
|
|
636
637
|
*/
|
|
637
638
|
contrastLevel: number | 'auto';
|
|
@@ -661,8 +662,10 @@ interface GlazeConfigOverride {
|
|
|
661
662
|
/** Whether to auto-flip tone when contrast can't be met. */
|
|
662
663
|
autoFlip?: boolean;
|
|
663
664
|
/**
|
|
664
|
-
* Manual contrast level (0–100) for this instance, or `'auto'` to opt out
|
|
665
|
-
*
|
|
665
|
+
* Manual contrast level (0–100) for this instance, or `'auto'` to opt out of a
|
|
666
|
+
* global level and take the authored normal entries as-is. Either way the
|
|
667
|
+
* instance keeps its high-contrast tier; a per-instance level never changes
|
|
668
|
+
* which modes are emitted.
|
|
666
669
|
*
|
|
667
670
|
* Only an instance-authored level is frozen into `.export()` snapshots — a
|
|
668
671
|
* level inherited from the global config is treated as a live preference and
|
package/dist/index.d.mts
CHANGED
|
@@ -234,9 +234,10 @@ interface GlazeOutputModes {
|
|
|
234
234
|
/**
|
|
235
235
|
* Include high-contrast variants (both light-HC and dark-HC). Default: false.
|
|
236
236
|
*
|
|
237
|
-
*
|
|
238
|
-
* high-contrast
|
|
239
|
-
*
|
|
237
|
+
* Independent of `contrastLevel` — the level positions the normal variants
|
|
238
|
+
* while these stay the true high-contrast resolution, so the two compose. The
|
|
239
|
+
* exception is a global level of 100, where the normal variants already *are*
|
|
240
|
+
* the high-contrast ones: the tier would duplicate them and is dropped.
|
|
240
241
|
* @see GlazeConfig.contrastLevel
|
|
241
242
|
*/
|
|
242
243
|
highContrast?: boolean;
|
|
@@ -572,15 +573,15 @@ interface GlazeConfig {
|
|
|
572
573
|
shadowTuning?: ShadowTuning;
|
|
573
574
|
/**
|
|
574
575
|
* Manual contrast level — a 0–100 slider from normal contrast (`0`) to high
|
|
575
|
-
* contrast (`100`), or `'auto'` (the default) to
|
|
576
|
-
*
|
|
576
|
+
* contrast (`100`), or `'auto'` (the default) to take the authored normal
|
|
577
|
+
* entries as-is.
|
|
577
578
|
*
|
|
578
|
-
* With a number, Glaze resolves the `light` / `dark` variants *at* that
|
|
579
|
-
*
|
|
580
|
-
*
|
|
581
|
-
*
|
|
582
|
-
*
|
|
583
|
-
*
|
|
579
|
+
* With a number, Glaze resolves the `light` / `dark` variants *at* that level.
|
|
580
|
+
* That is all it does: `lightContrast` / `darkContrast` stay the true
|
|
581
|
+
* high-contrast resolution at every level, and `modes.highContrast` alone
|
|
582
|
+
* decides whether they are emitted. Level `0` therefore reproduces `'auto'`
|
|
583
|
+
* bit for bit. At `100` the normal variants *are* the high-contrast ones, so a
|
|
584
|
+
* separate tier would duplicate them and a global level of 100 drops it.
|
|
584
585
|
*
|
|
585
586
|
* The level interpolates all three things that make high contrast differ:
|
|
586
587
|
* authored `[normal, highContrast]` pairs, the tone-window widening, and
|
|
@@ -630,8 +631,8 @@ interface GlazeConfigResolved {
|
|
|
630
631
|
shadowTuning?: ShadowTuning;
|
|
631
632
|
autoFlip: boolean;
|
|
632
633
|
/**
|
|
633
|
-
* Manual contrast level (0–100), or `'auto'`
|
|
634
|
-
* normal
|
|
634
|
+
* Manual contrast level (0–100) for the normal variants, or `'auto'` to take
|
|
635
|
+
* the authored normal entries as-is.
|
|
635
636
|
* @see GlazeConfig.contrastLevel
|
|
636
637
|
*/
|
|
637
638
|
contrastLevel: number | 'auto';
|
|
@@ -661,8 +662,10 @@ interface GlazeConfigOverride {
|
|
|
661
662
|
/** Whether to auto-flip tone when contrast can't be met. */
|
|
662
663
|
autoFlip?: boolean;
|
|
663
664
|
/**
|
|
664
|
-
* Manual contrast level (0–100) for this instance, or `'auto'` to opt out
|
|
665
|
-
*
|
|
665
|
+
* Manual contrast level (0–100) for this instance, or `'auto'` to opt out of a
|
|
666
|
+
* global level and take the authored normal entries as-is. Either way the
|
|
667
|
+
* instance keeps its high-contrast tier; a per-instance level never changes
|
|
668
|
+
* which modes are emitted.
|
|
666
669
|
*
|
|
667
670
|
* Only an instance-authored level is frozen into `.export()` snapshots — a
|
|
668
671
|
* level inherited from the global config is treated as a live preference and
|
package/dist/index.mjs
CHANGED
|
@@ -904,10 +904,14 @@ function levelFraction(level) {
|
|
|
904
904
|
}
|
|
905
905
|
/**
|
|
906
906
|
* Manual-contrast blend fraction (0–1) for a resolved config, or `undefined`
|
|
907
|
-
* in `'auto'` mode
|
|
907
|
+
* in `'auto'` mode, where the normal variants take the authored normal entries
|
|
908
|
+
* as-is.
|
|
908
909
|
*
|
|
909
|
-
* Note `contrastLevel: 0` yields `0`, not `undefined
|
|
910
|
-
*
|
|
910
|
+
* Note `contrastLevel: 0` yields `0`, not `undefined`. The two select different
|
|
911
|
+
* branches, and an authored `0` is a slider position that `.export()` freezes
|
|
912
|
+
* where an absent level is not — but they are not an *output* distinction: at
|
|
913
|
+
* fraction 0 every interpolation returns its normal entry verbatim, so level 0
|
|
914
|
+
* reproduces `'auto'` bit for bit.
|
|
911
915
|
*/
|
|
912
916
|
function contrastFraction(config) {
|
|
913
917
|
const level = config.contrastLevel;
|
|
@@ -915,6 +919,20 @@ function contrastFraction(config) {
|
|
|
915
919
|
return levelFraction(level);
|
|
916
920
|
}
|
|
917
921
|
/**
|
|
922
|
+
* Whether the high-contrast variants are exact duplicates of the normal ones —
|
|
923
|
+
* true only at `contrastLevel: 100`, where the normal passes already resolve at
|
|
924
|
+
* full contrast.
|
|
925
|
+
*
|
|
926
|
+
* The level otherwise says nothing about high contrast: it positions the normal
|
|
927
|
+
* variants, while `lightContrast` / `darkContrast` stay the true high-contrast
|
|
928
|
+
* resolution at every level. This predicate marks the one level where that tier
|
|
929
|
+
* carries no new information, so callers can skip the two high-contrast passes,
|
|
930
|
+
* their drift checks, and the redundant output tier.
|
|
931
|
+
*/
|
|
932
|
+
function hcMirrorsNormal(config) {
|
|
933
|
+
return contrastFraction(config) === 1;
|
|
934
|
+
}
|
|
935
|
+
/**
|
|
918
936
|
* A numeric HC pair at a blend fraction (shadow `intensity`, mix `value`).
|
|
919
937
|
*
|
|
920
938
|
* The endpoints return the authored entry *by identity* rather than through
|
|
@@ -2274,12 +2292,14 @@ function warnContrastDrift(name, isDark, isHighContrast, contrast, yColor, yBase
|
|
|
2274
2292
|
* Color resolution engine.
|
|
2275
2293
|
*
|
|
2276
2294
|
* Runs the four-pass solver (light → light-HC → dark → dark-HC) that
|
|
2277
|
-
* turns a `ColorMap` into a fully resolved `ResolvedColor` per name
|
|
2278
|
-
*
|
|
2279
|
-
*
|
|
2280
|
-
*
|
|
2281
|
-
*
|
|
2282
|
-
*
|
|
2295
|
+
* turns a `ColorMap` into a fully resolved `ResolvedColor` per name. A manual
|
|
2296
|
+
* `contrastLevel` interpolates the authored HC pairs, tone window, and contrast
|
|
2297
|
+
* targets at that level for the two *normal* passes only; the high-contrast
|
|
2298
|
+
* passes bypass the level entirely, so the high-contrast slots are the same
|
|
2299
|
+
* values `'auto'` resolves. At level 100 the normal passes already are the
|
|
2300
|
+
* high-contrast ones, so the solver drops to two passes (light → dark) and
|
|
2301
|
+
* mirrors the slots. Owns the per-scheme resolve helpers for regular, shadow,
|
|
2302
|
+
* and mix color defs.
|
|
2283
2303
|
*
|
|
2284
2304
|
* Variants are stored in OKHST: `h` / `s` are OKHSL hue/saturation and
|
|
2285
2305
|
* `t` is the canonical contrast-uniform tone (0–1, reference eps). The
|
|
@@ -2536,7 +2556,7 @@ function resolveDependentColor(name, def, ctx, isHighContrast, isDark, channels,
|
|
|
2536
2556
|
flip,
|
|
2537
2557
|
pastel
|
|
2538
2558
|
};
|
|
2539
|
-
const level = contrastFraction(
|
|
2559
|
+
const level = isHighContrast ? void 0 : contrastFraction(config);
|
|
2540
2560
|
let preferInitial = false;
|
|
2541
2561
|
if (level !== void 0 && level > 0 && level < 1) {
|
|
2542
2562
|
initialDirection = findToneForContrast({
|
|
@@ -2725,10 +2745,11 @@ function seedField(order, ctx, field, source) {
|
|
|
2725
2745
|
* resolved with a `base` + `contrast` may land slightly under the contrast
|
|
2726
2746
|
* its tone implies because chromatic luminance drifts from the gray tone.
|
|
2727
2747
|
*
|
|
2728
|
-
* Under a manual `contrastLevel`
|
|
2729
|
-
*
|
|
2730
|
-
*
|
|
2731
|
-
*
|
|
2748
|
+
* Under a manual `contrastLevel` the spec is resolved at the level, so the check
|
|
2749
|
+
* measures each normal variant against the target it was actually solved for. At
|
|
2750
|
+
* level 100 the high-contrast slots are mirrors and are skipped: the normal
|
|
2751
|
+
* schemes already carry that check, and repeating it would emit a byte-identical
|
|
2752
|
+
* advisory under a different scheme label, which dedupes as a separate key.
|
|
2732
2753
|
*/
|
|
2733
2754
|
function verifyContrastDrift(order, defs, result, config) {
|
|
2734
2755
|
const roles = /* @__PURE__ */ new Map();
|
|
@@ -2763,9 +2784,9 @@ function verifyContrastDrift(order, defs, result, config) {
|
|
|
2763
2784
|
field: "darkContrast"
|
|
2764
2785
|
}
|
|
2765
2786
|
];
|
|
2766
|
-
const
|
|
2787
|
+
const mirrored = hcMirrorsNormal(config);
|
|
2767
2788
|
for (const s of schemes) {
|
|
2768
|
-
if (
|
|
2789
|
+
if (mirrored && s.isHighContrast) continue;
|
|
2769
2790
|
const spec = resolveContrastSpec(regDef.contrast, s.isHighContrast, config, polarity);
|
|
2770
2791
|
const cVariant = color[s.field];
|
|
2771
2792
|
const bVariant = base[s.field];
|
|
@@ -2790,10 +2811,10 @@ function resolveAllColors(seed, defs, config, externalBases) {
|
|
|
2790
2811
|
roles: /* @__PURE__ */ new Map()
|
|
2791
2812
|
};
|
|
2792
2813
|
if (externalBases) for (const [name, color] of externalBases) ctx.resolved.set(name, color);
|
|
2793
|
-
const
|
|
2814
|
+
const mirrored = hcMirrorsNormal(config);
|
|
2794
2815
|
const lightMap = runPass(order, defs, ctx, false, false, "light");
|
|
2795
2816
|
let lightHCMap = lightMap;
|
|
2796
|
-
if (!
|
|
2817
|
+
if (!mirrored) {
|
|
2797
2818
|
seedField(order, ctx, "lightContrast", lightMap);
|
|
2798
2819
|
lightHCMap = runPass(order, defs, ctx, false, true, "lightContrast");
|
|
2799
2820
|
}
|
|
@@ -2801,7 +2822,7 @@ function resolveAllColors(seed, defs, config, externalBases) {
|
|
|
2801
2822
|
seedField(order, ctx, "darkContrast", lightHCMap);
|
|
2802
2823
|
const darkMap = runPass(order, defs, ctx, true, false, "dark");
|
|
2803
2824
|
let darkHCMap = darkMap;
|
|
2804
|
-
if (!
|
|
2825
|
+
if (!mirrored) {
|
|
2805
2826
|
seedField(order, ctx, "darkContrast", darkMap);
|
|
2806
2827
|
darkHCMap = runPass(order, defs, ctx, true, true, "darkContrast");
|
|
2807
2828
|
}
|
|
@@ -3035,23 +3056,28 @@ function formatColorValue(v, format, pastel, huePlan) {
|
|
|
3035
3056
|
/**
|
|
3036
3057
|
* Resolve the effective output modes for an export.
|
|
3037
3058
|
*
|
|
3038
|
-
*
|
|
3039
|
-
*
|
|
3040
|
-
*
|
|
3041
|
-
*
|
|
3042
|
-
*
|
|
3043
|
-
*
|
|
3059
|
+
* `modes.highContrast` and `contrastLevel` are independent: the level positions
|
|
3060
|
+
* the normal variants, the high-contrast tier stays the true high-contrast
|
|
3061
|
+
* resolution, and the two compose — a slider raises the baseline while a
|
|
3062
|
+
* `prefers-contrast: more` block still escalates on top of it.
|
|
3063
|
+
*
|
|
3064
|
+
* The one exception is a **global** level of 100, where the normal variants
|
|
3065
|
+
* already *are* the high-contrast ones. The tier would be an exact duplicate, so
|
|
3066
|
+
* it is dropped and a single light/dark set is emitted — including against an
|
|
3067
|
+
* explicit `highContrast: true` override, which has nothing different left to
|
|
3068
|
+
* ask for.
|
|
3044
3069
|
*
|
|
3045
3070
|
* A level set on a single theme or token does not change which modes are
|
|
3046
|
-
* emitted:
|
|
3047
|
-
*
|
|
3048
|
-
* the
|
|
3071
|
+
* emitted: `modes` is global-only, and a palette must not have one sibling
|
|
3072
|
+
* collapse the shared token structure. A per-instance level of 100 therefore
|
|
3073
|
+
* still reports into the tier, with values equal to its own normal ones.
|
|
3049
3074
|
*/
|
|
3050
3075
|
function resolveModes(override) {
|
|
3051
3076
|
const cfg = getConfig();
|
|
3077
|
+
const highContrast = override?.highContrast ?? cfg.modes.highContrast;
|
|
3052
3078
|
return {
|
|
3053
3079
|
dark: override?.dark ?? cfg.modes.dark,
|
|
3054
|
-
highContrast:
|
|
3080
|
+
highContrast: highContrast && !hcMirrorsNormal(cfg)
|
|
3055
3081
|
};
|
|
3056
3082
|
}
|
|
3057
3083
|
function buildTokenMap(resolved, prefix, states, modes, format = "oklch", pastel = false, channelCtx) {
|