PluginProbe
Gutenberg / trunk
Gutenberg vtrunk
24.1.0 24.0.0 23.9.1 23.9.0 23.8.0 23.7.2 23.7.1 23.7.0 23.6.1 23.6.2 23.6.0 23.5.3 23.5.2 23.5.1 23.5.0 23.4.0 23.3.2 23.3.1 23.3.0 23.2.0 23.2.1 23.2.2 23.1.1 23.1.0 23.0.1 All 404 releases
← All changes | build/scripts/theme/index.js +141 -95 23.9.0 → trunk View file →
@@ -3344,25 +3344,20 @@
3344 3344 }
3345 3345 }
3346 3346 });
3347 3347
3348 + // packages/theme/node_modules/colorjs.io/src/equals.js
3349 + function equals(color1, color2) {
3350 + color1 = getColor(color1);
3351 + color2 = getColor(color2);
3352 + return color1.space === color2.space && color1.alpha === color2.alpha && color1.coords.every((c, i) => c === color2.coords[i]);
3353 + }
3354 +
3348 3355 // packages/theme/node_modules/colorjs.io/src/luminance.js
3349 3356 function getLuminance(color) {
3350 3357 return get(color, [xyz_d65_default, "y"]);
3351 3358 }
3352 3359
3353 - // packages/theme/node_modules/colorjs.io/src/contrast/WCAG21.js
3354 - function contrastWCAG21(color1, color2) {
3355 - color1 = getColor(color1);
3356 - color2 = getColor(color2);
3357 - let Y1 = Math.max(getLuminance(color1), 0);
3358 - let Y2 = Math.max(getLuminance(color2), 0);
3359 - if (Y2 > Y1) {
3360 - [Y1, Y2] = [Y2, Y1];
3361 - }
3362 - return (Y1 + 0.05) / (Y2 + 0.05);
3363 - }
3364 -
3365 3360 // packages/theme/node_modules/colorjs.io/src/spaces/hsl.js
3366 3361 var hsl_default = new ColorSpace({
3367 3362 id: "hsl",
3368 3363 name: "HSL",
@@ -3524,13 +3519,13 @@
3524 3519 "background-interactive-neutral-weak",
3525 3520 "background-interactive-neutral-weak-disabled"
3526 3521 ],
3527 3522 "primary-bgFill1": ["background-interactive-brand-strong"],
3523 + "primary-bgFill2": ["background-interactive-brand-strong-active"],
3528 3524 "primary-fgFill": [
3529 3525 "foreground-interactive-brand-strong",
3530 3526 "foreground-interactive-brand-strong-active"
3531 3527 ],
3532 - "primary-bgFill2": ["background-interactive-brand-strong-active"],
3533 3528 "primary-surface4": ["background-interactive-brand-weak-active"],
3534 3529 "primary-fgSurface4": ["foreground-interactive-brand-active"],
3535 3530 "primary-fgSurface3": ["foreground-interactive-brand"],
3536 3531 "primary-stroke3": [
@@ -3561,13 +3556,13 @@
3561 3556 "warning-fgSurface3": ["foreground-content-warning-weak"],
3562 3557 "warning-stroke3": ["stroke-surface-warning-strong"],
3563 3558 "warning-stroke1": ["stroke-surface-warning"],
3564 3559 "error-bgFill1": ["background-interactive-error-strong"],
3560 + "error-bgFill2": ["background-interactive-error-strong-active"],
3565 3561 "error-fgFill": [
3566 3562 "foreground-interactive-error-strong",
3567 3563 "foreground-interactive-error-strong-active"
3568 3564 ],
3569 - "error-bgFill2": ["background-interactive-error-strong-active"],
3570 3565 "error-surface2": [
3571 3566 "background-interactive-error-active",
3572 3567 "background-surface-error-weak"
3573 3568 ],
@@ -3596,9 +3591,14 @@
3596 3591 "background-interactive-error-strong-disabled",
3597 3592 "background-interactive-neutral-strong-disabled"
3598 3593 ],
3599 3594 "bg-surface4": ["background-interactive-neutral-weak-active"],
3600 - "bg-surface3": ["background-surface-neutral-strong"],
3595 + "bg-surface3": [
3596 + "background-interactive-neutral",
3597 + "background-interactive-neutral-active",
3598 + "background-interactive-neutral-disabled",
3599 + "background-surface-neutral-strong"
3600 + ],
3601 3601 "bg-fgSurface4": [
3602 3602 "foreground-content-neutral",
3603 3603 "foreground-interactive-neutral",
3604 3604 "foreground-interactive-neutral-active",
@@ -3656,8 +3656,9 @@
3656 3656 };
3657 3657
3658 3658 // packages/theme/build-module/color-ramps/lib/color-utils.mjs
3659 3659 var ALLOWED_SEED_COLOR_SPACES = [srgb_default];
3660 + var objectLuminanceCache = /* @__PURE__ */ new WeakMap();
3660 3661 function getColorString(color) {
3661 3662 ColorSpace.register(srgb_default);
3662 3663 const rgbRounded = serialize(to(color, srgb_default));
3663 3664 return serialize(rgbRounded, { format: "hex" });
@@ -3662,12 +3663,33 @@
3662 3663 const rgbRounded = serialize(to(color, srgb_default));
3663 3664 return serialize(rgbRounded, { format: "hex" });
3664 3665 }
3665 3666 function getContrast(colorA, colorB) {
3666 - ColorSpace.register(srgb_default);
3667 - return contrastWCAG21(colorA, colorB);
3667 + return getContrastFromLuminances(
3668 + getRelativeLuminance(colorA),
3669 + getRelativeLuminance(colorB)
3670 + );
3668 3671 }
3669 - function assertValidSeedColor(seed) {
3672 + function getRelativeLuminance(color) {
3673 + if (typeof color === "string") {
3674 + ColorSpace.register(srgb_default);
3675 + return Math.max(getLuminance(color), 0);
3676 + }
3677 + const cachedLuminance = objectLuminanceCache.get(color);
3678 + if (cachedLuminance && equals(cachedLuminance.color, color)) {
3679 + return cachedLuminance.luminance;
3680 + }
3681 + const luminance = Math.max(getLuminance(color), 0);
3682 + objectLuminanceCache.set(color, {
3683 + color: clone(color),
3684 + luminance
3685 + });
3686 + return luminance;
3687 + }
3688 + function getContrastFromLuminances(first, second) {
3689 + return first > second ? (first + 0.05) / (second + 0.05) : (second + 0.05) / (first + 0.05);
3690 + }
3691 + function parseSeedColor(seed) {
3670 3692 ALLOWED_SEED_COLOR_SPACES.forEach(
3671 3693 (space) => ColorSpace.register(space)
3672 3694 );
3673 3695 let parsedColor;
@@ -3688,11 +3710,11 @@
3688 3710 throw new Error(
3689 3711 `Unsupported seed color "${seed}": expected a fully opaque color.`
3690 3712 );
3691 3713 }
3714 + return parsedColor;
3692 3715 }
3693 3716 function clampToGamut(c) {
3694 - ColorSpace.register(srgb_default);
3695 3717 return to(toGamut(c, { space: srgb_default, method: "css" }), oklch_default);
3696 3718 }
3697 3719
3698 3720 // packages/theme/build-module/color-ramps/lib/constants.mjs
@@ -3736,11 +3758,16 @@
3736 3758 dependents.set(step, []);
3737 3759 });
3738 3760 Object.entries(config).forEach(([stepName, stepConfig]) => {
3739 3761 const step = stepName;
3740 - const reference = stepConfig.contrast.reference;
3741 - dependencies.get(step).push(reference);
3742 - dependents.get(reference).push(step);
3762 + const references = [
3763 + stepConfig.contrast.reference,
3764 + ...stepConfig.contrast.additionalReferences ?? []
3765 + ];
3766 + for (const reference of references) {
3767 + dependencies.get(step).push(reference);
3768 + dependents.get(reference).push(step);
3769 + }
3743 3770 if (stepConfig.sameAsIfPossible) {
3744 3771 dependencies.get(step).push(stepConfig.sameAsIfPossible);
3745 3772 dependents.get(stepConfig.sameAsIfPossible).push(step);
3746 3773 }
@@ -3787,8 +3814,9 @@
3787 3814 if (!stepConfig) {
3788 3815 return;
3789 3816 }
3790 3817 visit(stepConfig.contrast.reference);
3818 + stepConfig.contrast.additionalReferences?.forEach(visit);
3791 3819 if (stepConfig.sameAsIfPossible) {
3792 3820 visit(stepConfig.sameAsIfPossible);
3793 3821 }
3794 3822 result.add(step);
@@ -3795,11 +3823,20 @@
3795 3823 }
3796 3824 visit(stepName);
3797 3825 return Array.from(result);
3798 3826 }
3799 - function computeBetterFgColorDirection(seed, preferLighter) {
3800 - const contrastAgainstBlack = getContrast(seed, BLACK);
3801 - const contrastAgainstWhite = getContrast(seed, WHITE);
3827 + function computeBetterFgColorDirection(references, preferLighter) {
3828 + const referenceColors = Array.isArray(references) ? references : [references];
3829 + const contrastAgainstBlack = Math.min(
3830 + ...referenceColors.map(
3831 + (reference) => getContrast(reference, BLACK)
3832 + )
3833 + );
3834 + const contrastAgainstWhite = Math.min(
3835 + ...referenceColors.map(
3836 + (reference) => getContrast(reference, WHITE)
3837 + )
3838 + );
3802 3839 return contrastAgainstBlack > contrastAgainstWhite + (preferLighter ? WHITE_TEXT_CONTRAST_MARGIN : 0) ? { better: "darker", worse: "lighter" } : { better: "lighter", worse: "darker" };
3803 3840 }
3804 3841 function adjustContrastTarget(target) {
3805 3842 if (target === 1) {
@@ -3850,13 +3887,12 @@
3850 3887 return bestC;
3851 3888 }
3852 3889
3853 3890 // packages/theme/build-module/color-ramps/lib/taper-chroma.mjs
3854 - function taperChroma(seed, lTarget, options = {}) {
3891 + function createChromaTaper(seed, options = {}) {
3855 3892 const gamut = options.gamut ?? srgb_default;
3856 3893 const alpha = options.alpha ?? 0.65;
3857 3894 const carry = options.carry ?? 0.5;
3858 - const cUpperBound = options.cUpperBound ?? 0.45;
3859 3895 const radiusLight = options.radiusLight ?? 0.2;
3860 3896 const radiusDark = options.radiusDark ?? 0.2;
3861 3897 const kLight = options.kLight ?? 0.85;
3862 3898 const kDark = options.kDark ?? 0.85;
@@ -3868,37 +3904,34 @@
3868 3904 if (chromaIsTiny || hueIsInvalid) {
3869 3905 if (typeof options.hueFallback === "number") {
3870 3906 hSeed = normalizeHue(options.hueFallback);
3871 3907 } else {
3872 - return {
3908 + return (lTarget) => ({
3873 3909 space: oklch_default,
3874 3910 coords: [clamp01(lTarget), 0, 0],
3875 3911 alpha: 1
3876 - };
3912 + });
3877 3913 }
3878 3914 }
3879 3915 const lSeed = clamp01(get(seed, [oklch_default, "l"]));
3880 - const cmaxSeed = getCachedMaxChromaAtLH(lSeed, hSeed, gamut, cUpperBound);
3881 - const cmaxTarget = getCachedMaxChromaAtLH(
3882 - clamp01(lTarget),
3883 - hSeed,
3884 - gamut,
3885 - cUpperBound
3886 - );
3887 - let seedRelative = 0;
3916 + const cmaxSeed = getMaxChromaAtLH(lSeed, hSeed, gamut);
3888 3917 const denom = cmaxSeed > 0 ? cmaxSeed : 1e-6;
3889 - seedRelative = clamp01(cSeed / denom);
3890 - const cIntendedBase = alpha * cmaxTarget;
3891 - const cWithCarry = cIntendedBase * Math.pow(seedRelative, clamp01(carry));
3892 - const t = continuousTaper(lSeed, lTarget, {
3893 - radiusLight,
3894 - radiusDark,
3895 - kLight,
3896 - kDark
3897 - });
3898 - const cPlanned = cWithCarry * t;
3899 - const lOut = clamp01(lTarget);
3900 - return { l: lOut, c: cPlanned };
3918 + const seedRelative = clamp01(cSeed / denom);
3919 + const seedCarry = Math.pow(seedRelative, clamp01(carry));
3920 + return (lTarget) => {
3921 + const cmaxTarget = getMaxChromaAtLH(clamp01(lTarget), hSeed, gamut);
3922 + const cIntendedBase = alpha * cmaxTarget;
3923 + const cWithCarry = cIntendedBase * seedCarry;
3924 + const t = continuousTaper(lSeed, lTarget, {
3925 + radiusLight,
3926 + radiusDark,
3927 + kLight,
3928 + kDark
3929 + });
3930 + const cPlanned = cWithCarry * t;
3931 + const lOut = clamp01(lTarget);
3932 + return { l: lOut, c: cPlanned };
3933 + };
3901 3934 }
3902 3935 function clamp01(x) {
3903 3936 if (x < 0) {
3904 3937 return 0;
@@ -3929,34 +3962,13 @@
3929 3962 const u = opts.radiusDark > 0 ? Math.abs(d2) / opts.radiusDark : 1;
3930 3963 const w = raisedCosine(u > 1 ? 1 : u);
3931 3964 return 1 - (1 - opts.kDark) * w;
3932 3965 }
3933 - var maxChromaCache = /* @__PURE__ */ new Map();
3934 - function keyMax(l, h, gamut, cap) {
3935 - const lq = quantize(l, 0.05);
3936 - const hq = quantize(normalizeHue(h), 10);
3937 - const cq = quantize(cap, 0.05);
3938 - return `${gamut}|L:${lq}|H:${hq}|cap:${cq}`;
3939 - }
3940 - function quantize(x, step) {
3941 - const k = Math.round(x / step);
3942 - return k * step;
3943 - }
3944 - function getCachedMaxChromaAtLH(l, h, gamutSpace, cap) {
3945 - const gamut = gamutSpace.id;
3946 - const key = keyMax(l, h, gamut, cap);
3947 - const hit = maxChromaCache.get(key);
3948 - if (typeof hit === "number") {
3949 - return hit;
3950 - }
3951 - const computed = maxInGamutChromaAtLH(l, h, gamutSpace, cap);
3952 - maxChromaCache.set(key, computed);
3953 - return computed;
3954 - }
3955 - function maxInGamutChromaAtLH(l, h, gamutSpace, cap) {
3966 + var MAX_CHROMA = 0.45;
3967 + function getMaxChromaAtLH(l, h, gamutSpace) {
3956 3968 const probe = {
3957 3969 space: oklch_default,
3958 - coords: [l, cap, h],
3970 + coords: [clamp01(l), MAX_CHROMA, normalizeHue(h)],
3959 3971 alpha: 1
3960 3972 };
3961 3973 const clamped = toGamutCSS(probe, { space: gamutSpace });
3962 3974 return get(clamped, [oklch_default, "c"]);
@@ -3976,13 +3988,16 @@
3976 3988 reached: true,
3977 3989 achieved: 1
3978 3990 };
3979 3991 }
3992 + const seedChroma = get(seed, [oklch_default, "c"]);
3993 + const seedHue = get(seed, [oklch_default, "h"]);
3994 + const taperChromaAtLightness = taperChromaOptions ? createChromaTaper(seed, taperChromaOptions) : void 0;
3980 3995 function getColorForL(l) {
3981 3996 let newL = l;
3982 - let newC = get(seed, [oklch_default, "c"]);
3983 - if (taperChromaOptions) {
3984 - const tapered = taperChroma(seed, newL, taperChromaOptions);
3997 + let newC = seedChroma;
3998 + if (taperChromaAtLightness) {
3999 + const tapered = taperChromaAtLightness(newL);
3985 4000 if ("l" in tapered && "c" in tapered) {
3986 4001 newL = tapered.l;
3987 4002 newC = tapered.c;
3988 4003 } else {
@@ -3990,9 +4005,9 @@
3990 4005 }
3991 4006 }
3992 4007 return clampToGamut({
3993 4008 space: oklch_default,
3994 - coords: [newL, newC, get(seed, [oklch_default, "h"])],
4009 + coords: [newL, newC, seedHue],
3995 4010 alpha: seed.alpha
3996 4011 });
3997 4012 }
3998 4013 const mostContrastingL = direction === "lighter" ? 1 : 0;
@@ -4056,9 +4071,9 @@
4056 4071 let maxDeficitStep;
4057 4072 const calculatedColors = /* @__PURE__ */ new Map();
4058 4073 calculatedColors.set("seed", seed);
4059 4074 for (const stepName of sortedSteps) {
4060 - let computeDirection2 = function(color, followDirection) {
4075 + let computeDirection2 = function(colors, followDirection) {
4061 4076 if (followDirection === "main") {
4062 4077 return mainDir;
4063 4078 }
4064 4079 if (followDirection === "opposite") {
@@ -4065,9 +4080,9 @@
4065 4080 return oppDir;
4066 4081 }
4067 4082 if (followDirection === "best") {
4068 4083 return computeBetterFgColorDirection(
4069 - color,
4084 + colors,
4070 4085 contrast.preferLighter
4071 4086 ).better;
4072 4087 }
4073 4088 return followDirection;
@@ -4078,14 +4093,21 @@
4078 4093 lightness: stepLightnessConstraint,
4079 4094 taperChromaOptions,
4080 4095 sameAsIfPossible
4081 4096 } = config[stepName];
4082 - const referenceColor = calculatedColors.get(contrast.reference);
4083 - if (!referenceColor) {
4084 - throw new Error(
4085 - `Reference color for step ${stepName} not found: ${contrast.reference}`
4086 - );
4087 - }
4097 + const referenceNames = [
4098 + contrast.reference,
4099 + ...contrast.additionalReferences ?? []
4100 + ];
4101 + const referenceColors = referenceNames.map((referenceName) => {
4102 + const referenceColor2 = calculatedColors.get(referenceName);
4103 + if (!referenceColor2) {
4104 + throw new Error(
4105 + `Reference color for step ${stepName} not found: ${referenceName}`
4106 + );
4107 + }
4108 + return referenceColor2;
4109 + });
4088 4110 if (sameAsIfPossible) {
4089 4111 const candidateColor = calculatedColors.get(sameAsIfPossible);
4090 4112 if (!candidateColor) {
4091 4113 throw new Error(
@@ -4091,14 +4113,13 @@
4091 4113 throw new Error(
4092 4114 `Same-as color for step ${stepName} not found: ${sameAsIfPossible}`
4093 4115 );
4094 4116 }
4095 - const candidateContrast = getContrast(
4096 - referenceColor,
4097 - candidateColor
4117 + const adjustedTarget2 = adjustContrastTarget(contrast.target);
4118 + const candidateMeetsTarget = referenceColors.every(
4119 + (referenceColor2) => getContrast(referenceColor2, candidateColor) >= adjustedTarget2
4098 4120 );
4099 - const adjustedTarget2 = adjustContrastTarget(contrast.target);
4100 - if (candidateContrast >= adjustedTarget2) {
4121 + if (candidateMeetsTarget) {
4101 4122 calculatedColors.set(stepName, candidateColor);
4102 4123 rampResults[stepName] = getColorString(candidateColor);
4103 4124 continue;
4104 4125 }
@@ -4103,11 +4124,15 @@
4103 4124 continue;
4104 4125 }
4105 4126 }
4106 4127 const computedDir = computeDirection2(
4107 - referenceColor,
4128 + referenceColors,
4108 4129 contrast.followDirection
4109 4130 );
4131 + const endpoint = computedDir === "lighter" ? WHITE : BLACK;
4132 + const referenceColor = referenceColors.reduce(
4133 + (tightest, current) => getContrast(current, endpoint) < getContrast(tightest, endpoint) ? current : tightest
4134 + );
4110 4135 const adjustedTarget = adjustContrastTarget(contrast.target);
4111 4136 let lightnessConstraint;
4112 4137 if (pinLightness?.stepName === stepName) {
4113 4138 lightnessConstraint = {
@@ -4154,12 +4179,12 @@
4154 4179 mainDirection,
4155 4180 pinLightness,
4156 4181 rescaleToFitContrastTargets = true
4157 4182 } = {}) {
4158 - assertValidSeedColor(seedArg);
4183 + const parsedSeed = parseSeedColor(seedArg);
4159 4184 let seed;
4160 4185 try {
4161 - seed = clampToGamut(seedArg);
4186 + seed = clampToGamut(parsedSeed);
4162 4187 } catch (error) {
4163 4188 throw new Error(
4164 4189 `Invalid seed color "${seedArg}": ${error instanceof Error ? error.message : "Unknown error"}`
4165 4190 );
@@ -4427,11 +4452,12 @@
4427 4452 // fgFill
4428 4453 fgFill: {
4429 4454 contrast: {
4430 4455 reference: "bgFill1",
4456 + additionalReferences: ["bgFill2"],
4431 4457 followDirection: "best",
4432 - target: 4.5,
4433 - preferLighter: true
4458 + // Preserve the 4.5:1 WCAG floor after 8-bit sRGB serialization.
4459 + target: 4.55
4434 4460 },
4435 4461 lightness: lightnessConstraintForegroundHighContrast,
4436 4462 taperChromaOptions: FG_TAPER_CHROMA
4437 4463 },
@@ -4559,8 +4585,28 @@
4559 4585
4560 4586 // packages/theme/build-module/semantic-color-contrast-pairs.mjs
4561 4587 var MINIMUM_TEXT_CONTRAST = 4.5;
4562 4588 var SEMANTIC_COLOR_CONTRAST_PAIRS = [
4589 + {
4590 + background: "background.interactive.neutral",
4591 + foreground: "foreground.interactive.neutral"
4592 + },
4593 + {
4594 + background: "background.interactive.neutral",
4595 + foreground: "foreground.interactive.neutral-weak"
4596 + },
4597 + {
4598 + background: "background.interactive.neutral-active",
4599 + foreground: "foreground.interactive.neutral"
4600 + },
4601 + {
4602 + background: "background.interactive.neutral-active",
4603 + foreground: "foreground.interactive.neutral-weak"
4604 + },
4605 + {
4606 + background: "background.interactive.neutral-weak-active",
4607 + foreground: "foreground.interactive.neutral-active"
4608 + },
4563 4609 {
4564 4610 background: "background.surface.neutral",
4565 4611 foreground: "foreground.content.neutral"
4566 4612 },