diff --git a/packages/css-color-parser/CHANGELOG.md b/packages/css-color-parser/CHANGELOG.md index 393fee7d10..e68f404654 100644 --- a/packages/css-color-parser/CHANGELOG.md +++ b/packages/css-color-parser/CHANGELOG.md @@ -1,5 +1,9 @@ # Changes to CSS Color Parser +### Unreleased (patch) + +- Remove special handling of all zero percentages in `color-mix()` + ### 4.1.1 _May 13, 2026_ diff --git a/packages/css-color-parser/dist/index.mjs b/packages/css-color-parser/dist/index.mjs index 3756a4c75a..e81cf98866 100644 --- a/packages/css-color-parser/dist/index.mjs +++ b/packages/css-color-parser/dist/index.mjs @@ -1 +1 @@ -import{TokenType as e,NumberType as a,isTokenIdent as n,isTokenPercentage as r,isTokenNumber as o,isTokenDelim as l,isTokenNumeric as t,isTokenComma as s,isTokenDimension as u,isTokenHash as i}from"@csstools/css-tokenizer";import{XYZ_D50_to_XYZ_D65 as c,XYZ_D50_to_XYZ_D50 as h,XYZ_D50_to_OKLab as m,XYZ_D50_to_OKLCH as p,XYZ_D50_to_LCH as N,XYZ_D50_to_Lab as b,XYZ_D50_to_HWB as g,XYZ_D50_to_HSL as v,XYZ_D50_to_a98_RGB as f,XYZ_D50_to_ProPhoto as y,XYZ_D50_to_rec_2020 as d,XYZ_D50_to_lin_P3 as _,XYZ_D50_to_P3 as C,XYZ_D50_to_lin_sRGB as w,XYZ_D50_to_sRGB as x,XYZ_D65_to_XYZ_D50 as L,OKLCH_to_XYZ_D50 as H,LCH_to_XYZ_D50 as k,OKLab_to_XYZ_D50 as S,Lab_to_XYZ_D50 as P,HWB_to_XYZ_D50 as M,HSL_to_XYZ_D50 as D,ProPhoto_RGB_to_XYZ_D50 as F,a98_RGB_to_XYZ_D50 as z,rec_2020_to_XYZ_D50 as Z,lin_P3_to_XYZ_D50 as R,P3_to_XYZ_D50 as B,lin_sRGB_to_XYZ_D50 as V,sRGB_to_XYZ_D50 as T,namedColors as G,inGamut as A,clip as X,gam_sRGB as K,mapGamutRayTrace as Y,OKLCH_to_OKLab as I,OKLab_to_XYZ as O,XYZ_to_lin_sRGB as W,lin_sRGB_to_XYZ as E,XYZ_to_OKLab as U,OKLab_to_OKLCH as $,contrast_ratio_wcag_2_1 as j,gam_P3 as q,XYZ_to_lin_P3 as J,lin_P3_to_XYZ as Q}from"@csstools/color-helpers";import{isWhitespaceNode as ee,isCommentNode as ae,isTokenNode as ne,isFunctionNode as re,TokenNode as oe,isWhiteSpaceOrCommentNode as le,replaceComponentValues as te,FunctionNode as se,WhitespaceNode as ue}from"@csstools/css-parser-algorithms";import{mathFunctionNames as ie,calcFromComponentValues as ce}from"@csstools/css-calc";var he,me;function convertNaNToZero(e){return[Number.isNaN(e[0])?0:e[0],Number.isNaN(e[1])?0:e[1],Number.isNaN(e[2])?0:e[2]]}function colorData_to_XYZ_D50(e){switch(e.colorNotation){case he.HEX:case he.RGB:case he.sRGB:return{...e,colorNotation:he.XYZ_D50,channels:T(convertNaNToZero(e.channels))};case he.Linear_sRGB:return{...e,colorNotation:he.XYZ_D50,channels:V(convertNaNToZero(e.channels))};case he.Display_P3:return{...e,colorNotation:he.XYZ_D50,channels:B(convertNaNToZero(e.channels))};case he.Linear_Display_P3:return{...e,colorNotation:he.XYZ_D50,channels:R(convertNaNToZero(e.channels))};case he.Rec2020:return{...e,colorNotation:he.XYZ_D50,channels:Z(convertNaNToZero(e.channels))};case he.A98_RGB:return{...e,colorNotation:he.XYZ_D50,channels:z(convertNaNToZero(e.channels))};case he.ProPhoto_RGB:return{...e,colorNotation:he.XYZ_D50,channels:F(convertNaNToZero(e.channels))};case he.HSL:return{...e,colorNotation:he.XYZ_D50,channels:D(convertNaNToZero(e.channels))};case he.HWB:return{...e,colorNotation:he.XYZ_D50,channels:M(convertNaNToZero(e.channels))};case he.Lab:return{...e,colorNotation:he.XYZ_D50,channels:P(convertNaNToZero(e.channels))};case he.OKLab:return{...e,colorNotation:he.XYZ_D50,channels:S(convertNaNToZero(e.channels))};case he.LCH:return{...e,colorNotation:he.XYZ_D50,channels:k(convertNaNToZero(e.channels))};case he.OKLCH:return{...e,colorNotation:he.XYZ_D50,channels:H(convertNaNToZero(e.channels))};case he.XYZ_D50:return{...e,colorNotation:he.XYZ_D50,channels:h(convertNaNToZero(e.channels))};case he.XYZ_D65:return{...e,colorNotation:he.XYZ_D50,channels:L(convertNaNToZero(e.channels))};default:throw new Error("Unsupported color notation")}}!function(e){e.A98_RGB="a98-rgb",e.Display_P3="display-p3",e.Linear_Display_P3="display-p3-linear",e.HEX="hex",e.HSL="hsl",e.HWB="hwb",e.LCH="lch",e.Lab="lab",e.Linear_sRGB="srgb-linear",e.OKLCH="oklch",e.OKLab="oklab",e.ProPhoto_RGB="prophoto-rgb",e.RGB="rgb",e.sRGB="srgb",e.Rec2020="rec2020",e.XYZ_D50="xyz-d50",e.XYZ_D65="xyz-d65"}(he||(he={})),function(e){e.ColorKeyword="color-keyword",e.HasAlpha="has-alpha",e.HasDimensionValues="has-dimension-values",e.HasNoneKeywords="has-none-keywords",e.HasNumberValues="has-number-values",e.HasPercentageAlpha="has-percentage-alpha",e.HasPercentageValues="has-percentage-values",e.HasVariableAlpha="has-variable-alpha",e.Hex="hex",e.LegacyHSL="legacy-hsl",e.LegacyRGB="legacy-rgb",e.NamedColor="named-color",e.RelativeColorSyntax="relative-color-syntax",e.ColorMix="color-mix",e.ColorMixVariadic="color-mix-variadic",e.ContrastColor="contrast-color",e.RelativeAlphaSyntax="relative-alpha-syntax",e.Experimental="experimental"}(me||(me={}));const pe=new Set([he.A98_RGB,he.Display_P3,he.Linear_Display_P3,he.HEX,he.Linear_sRGB,he.ProPhoto_RGB,he.RGB,he.sRGB,he.Rec2020,he.XYZ_D50,he.XYZ_D65]);function colorDataTo(e,a){const n={...e};if(e.colorNotation!==a){const e=colorData_to_XYZ_D50(n);switch(a){case he.HEX:case he.RGB:n.colorNotation=he.RGB,n.channels=x(e.channels);break;case he.sRGB:n.colorNotation=he.sRGB,n.channels=x(e.channels);break;case he.Linear_sRGB:n.colorNotation=he.Linear_sRGB,n.channels=w(e.channels);break;case he.Display_P3:n.colorNotation=he.Display_P3,n.channels=C(e.channels);break;case he.Linear_Display_P3:n.colorNotation=he.Linear_Display_P3,n.channels=_(e.channels);break;case he.Rec2020:n.colorNotation=he.Rec2020,n.channels=d(e.channels);break;case he.ProPhoto_RGB:n.colorNotation=he.ProPhoto_RGB,n.channels=y(e.channels);break;case he.A98_RGB:n.colorNotation=he.A98_RGB,n.channels=f(e.channels);break;case he.HSL:n.colorNotation=he.HSL,n.channels=v(e.channels);break;case he.HWB:n.colorNotation=he.HWB,n.channels=g(e.channels);break;case he.Lab:n.colorNotation=he.Lab,n.channels=b(e.channels);break;case he.LCH:n.colorNotation=he.LCH,n.channels=N(e.channels);break;case he.OKLCH:n.colorNotation=he.OKLCH,n.channels=p(e.channels);break;case he.OKLab:n.colorNotation=he.OKLab,n.channels=m(e.channels);break;case he.XYZ_D50:n.colorNotation=he.XYZ_D50,n.channels=h(e.channels);break;case he.XYZ_D65:n.colorNotation=he.XYZ_D65,n.channels=c(e.channels);break;default:throw new Error("Unsupported color notation")}}else n.channels=convertNaNToZero(e.channels);if(a===e.colorNotation)n.channels=carryForwardMissingComponents(e.channels,[0,1,2],[],n.channels,[0,1,2],[]);else if(pe.has(a)&&pe.has(e.colorNotation))n.channels=carryForwardMissingComponents(e.channels,[0,1,2],[],n.channels,[0,1,2],[]);else switch(a){case he.HSL:switch(e.colorNotation){case he.HWB:n.channels=carryForwardMissingComponents(e.channels,[0],[1,2],n.channels,[0],[1,2]);break;case he.Lab:case he.OKLab:n.channels=carryForwardMissingComponents(e.channels,[0],[1,2],n.channels,[2],[0,1]);break;case he.LCH:case he.OKLCH:n.channels=carryForwardMissingComponents(e.channels,[0,1,2],[],n.channels,[2,1,0],[]);break;default:n.channels=carryForwardMissingComponents(e.channels,[],[],n.channels,[],[])}break;case he.HWB:switch(e.colorNotation){case he.HSL:n.channels=carryForwardMissingComponents(e.channels,[0],[1,2],n.channels,[0],[1,2]);break;case he.LCH:case he.OKLCH:n.channels=carryForwardMissingComponents(e.channels,[2],[0,1],n.channels,[0],[1,2]);break;default:n.channels=carryForwardMissingComponents(e.channels,[],[],n.channels,[],[])}break;case he.Lab:case he.OKLab:switch(e.colorNotation){case he.HSL:n.channels=carryForwardMissingComponents(e.channels,[2],[0,1],n.channels,[0],[1,2]);break;case he.Lab:case he.OKLab:n.channels=carryForwardMissingComponents(e.channels,[0,1,2],[],n.channels,[0,1,2],[]);break;case he.LCH:case he.OKLCH:n.channels=carryForwardMissingComponents(e.channels,[0],[1,2],n.channels,[0],[1,2]);break;default:n.channels=carryForwardMissingComponents(e.channels,[],[],n.channels,[],[])}break;case he.LCH:case he.OKLCH:switch(e.colorNotation){case he.HSL:n.channels=carryForwardMissingComponents(e.channels,[0,1,2],[],n.channels,[2,1,0],[]);break;case he.HWB:n.channels=carryForwardMissingComponents(e.channels,[0],[1,2],n.channels,[2],[0,1]);break;case he.Lab:case he.OKLab:n.channels=carryForwardMissingComponents(e.channels,[0],[1,2],n.channels,[0],[1,2]);break;case he.LCH:case he.OKLCH:n.channels=carryForwardMissingComponents(e.channels,[0,1,2],[],n.channels,[0,1,2],[]);break;default:n.channels=carryForwardMissingComponents(e.channels,[],[],n.channels,[],[])}break;default:n.channels=carryForwardMissingComponents(e.channels,[],[],n.channels,[],[])}return n.channels=convertPowerlessComponentsToMissingComponents(n.channels,a),n}function convertPowerlessComponentsToMissingComponents(e,a){const n=[...e];switch(a){case he.HSL:!Number.isNaN(n[1])&&n[1]<=.001&&(n[0]=Number.NaN);break;case he.HWB:!Number.isNaN(n[1])&&!Number.isNaN(n[2])&&Math.max(0,n[1])+Math.max(0,n[2])>=99.999&&(n[0]=Number.NaN);break;case he.LCH:!Number.isNaN(n[1])&&n[1]<=.0015&&(n[2]=Number.NaN);break;case he.OKLCH:!Number.isNaN(n[1])&&n[1]<=4e-6&&(n[2]=Number.NaN)}return n}function convertPowerlessComponentsToZeroValuesForDisplay(e,a){const n=[...e];switch(a){case he.HSL:(reducePrecision(n[2])<=0||reducePrecision(n[2])>=100)&&(n[0]=Number.NaN,n[1]=Number.NaN),!Number.isNaN(n[1])&&n[1]<=.001&&(n[0]=Number.NaN);break;case he.HWB:!Number.isNaN(n[1])&&!Number.isNaN(n[2])&&Math.max(0,n[1])+Math.max(0,n[2])>=99.999&&(n[0]=Number.NaN);break;case he.Lab:(reducePrecision(n[0])<=0||reducePrecision(n[0])>=100)&&(n[1]=Number.NaN,n[2]=Number.NaN);break;case he.LCH:!Number.isNaN(n[1])&&n[1]<=.0015&&(n[2]=Number.NaN),(reducePrecision(n[0])<=0||reducePrecision(n[0])>=100)&&(n[1]=Number.NaN,n[2]=Number.NaN);break;case he.OKLab:(reducePrecision(n[0])<=0||reducePrecision(n[0])>=1)&&(n[1]=Number.NaN,n[2]=Number.NaN);break;case he.OKLCH:!Number.isNaN(n[1])&&n[1]<=4e-6&&(n[2]=Number.NaN),(reducePrecision(n[0])<=0||reducePrecision(n[0])>=1)&&(n[1]=Number.NaN,n[2]=Number.NaN)}return n}function carryForwardMissingComponents(e,a,n,r,o,l){if(a.length<3&&e.every(Number.isNaN))return[Number.NaN,Number.NaN,Number.NaN];const t=[...r];for(let n=0;nNumber.isNaN(e[a])))for(let e=0;ee<-1e-5||e>1.00001)}function colorDataFitsDisplayP3_Gamut(e){const a={...e,channels:[...e.channels]};a.channels=convertPowerlessComponentsToZeroValuesForDisplay(a.channels,a.colorNotation);return!colorDataTo(a,he.Display_P3).channels.find(e=>e<-1e-5||e>1.00001)}function normalize(e,a,n,r){return Math.min(Math.max(e/a,n),r)}const Ne=/[A-Z]/g;function toLowerCaseAZ(e){return e.replace(Ne,e=>String.fromCharCode(e.charCodeAt(0)+32))}function normalize_Color_ChannelValues(l,t,s){if(n(l)&&"none"===toLowerCaseAZ(l[4].value))return s.syntaxFlags.add(me.HasNoneKeywords),[e.Number,"none",l[2],l[3],{value:Number.NaN,type:a.Number}];if(r(l)){3!==t&&s.syntaxFlags.add(me.HasPercentageValues);let n=normalize(l[4].value,100,-2147483647,2147483647);return 3===t&&(n=normalize(l[4].value,100,0,1)),[e.Number,n.toString(),l[2],l[3],{value:n,type:a.Number}]}if(o(l)){3!==t&&s.syntaxFlags.add(me.HasNumberValues);let n=normalize(l[4].value,1,-2147483647,2147483647);return 3===t&&(n=normalize(l[4].value,1,0,1)),[e.Number,n.toString(),l[2],l[3],{value:n,type:a.Number}]}return!1}const be=new Set(["srgb","srgb-linear","display-p3","display-p3-linear","a98-rgb","prophoto-rgb","rec2020","xyz","xyz-d50","xyz-d65"]);function color$1(e,a){const r=[],s=[],u=[],i=[];let c,h,m=!1,p=!1;const N={colorNotation:he.sRGB,channels:[0,0,0],alpha:1,syntaxFlags:new Set([])};let b=r;for(let o=0;o=0){u=i.value[4].value;continue}}return!1}if(!l)return!1;n.push({color:l,percentage:u}),l=!1,u=!1}}if(!l)return!1;n.push({color:l,percentage:u});let i=0,c=0;for(let e=0;e100)return!1;i+=a}else c++}const h=Math.max(0,100-i);i=0;for(let e=0;e100)for(let e=0;e=2;){const e=n.pop(),a=n.pop();if(!e||!a)return!1;const o=colorMixRectangularPair(r,e.color,e.percentage,a.color,a.percentage);if(!o)return!1;n.push({color:o,percentage:e.percentage+a.percentage})}const o=n[0]?.color;return!!o&&(a.colors.some(e=>e.color.syntaxFlags.has(me.Experimental))&&o.syntaxFlags.add(me.Experimental),"number"==typeof o.alpha&&(o.alpha=o.alpha*a.alphaMultiplier,2!==a.colors.length&&o.syntaxFlags.add(me.ColorMixVariadic),o))}function colorMixRectangularPair(e,a,n,r,o){const l=n/(n+o);let t=a.alpha;if("number"!=typeof t)return!1;let s=r.alpha;if("number"!=typeof s)return!1;t=Number.isNaN(t)?s:t,s=Number.isNaN(s)?t:s;const u=colorDataTo(a,e).channels,i=colorDataTo(r,e).channels;u[0]=fillInMissingComponent(u[0],i[0]),i[0]=fillInMissingComponent(i[0],u[0]),u[1]=fillInMissingComponent(u[1],i[1]),i[1]=fillInMissingComponent(i[1],u[1]),u[2]=fillInMissingComponent(u[2],i[2]),i[2]=fillInMissingComponent(i[2],u[2]),u[0]=premultiply(u[0],t),u[1]=premultiply(u[1],t),u[2]=premultiply(u[2],t),i[0]=premultiply(i[0],s),i[1]=premultiply(i[1],s),i[2]=premultiply(i[2],s);const c=interpolate(t,s,l);return{colorNotation:e,channels:[un_premultiply(interpolate(u[0],i[0],l),c),un_premultiply(interpolate(u[1],i[1],l),c),un_premultiply(interpolate(u[2],i[2],l),c)],alpha:c,syntaxFlags:new Set([me.ColorMix])}}function colorMixPolar(e,a,n){if(!n||!n.colors.length)return!1;const r=n.colors.slice();let o;switch(r.reverse(),e){case"hsl":o=he.HSL;break;case"hwb":o=he.HWB;break;case"lch":o=he.LCH;break;case"oklch":o=he.OKLCH;break;default:return!1}if(1===r.length){const e=colorDataTo(r[0].color,o);return e.colorNotation=o,e.syntaxFlags.add(me.ColorMixVariadic),"number"!=typeof e.alpha?!1:(e.alpha=e.alpha*n.alphaMultiplier,e)}for(;r.length>=2;){const e=r.pop(),n=r.pop();if(!e||!n)return!1;const l=colorMixPolarPair(o,a,e.color,e.percentage,n.color,n.percentage);if(!l)return!1;r.push({color:l,percentage:e.percentage+n.percentage})}const l=r[0]?.color;return!!l&&(n.colors.some(e=>e.color.syntaxFlags.has(me.Experimental))&&l.syntaxFlags.add(me.Experimental),"number"==typeof l.alpha&&(l.alpha=l.alpha*n.alphaMultiplier,2!==n.colors.length&&l.syntaxFlags.add(me.ColorMixVariadic),l))}function colorMixPolarPair(e,a,n,r,o,l){const t=r/(r+l);let s=0,u=0,i=0,c=0,h=0,m=0,p=n.alpha;if("number"!=typeof p)return!1;let N=o.alpha;if("number"!=typeof N)return!1;p=Number.isNaN(p)?N:p,N=Number.isNaN(N)?p:N;const b=colorDataTo(n,e).channels,g=colorDataTo(o,e).channels;switch(e){case he.HSL:case he.HWB:s=b[0],u=g[0],i=b[1],c=g[1],h=b[2],m=g[2];break;case he.LCH:case he.OKLCH:i=b[0],c=g[0],h=b[1],m=g[1],s=b[2],u=g[2]}s=fillInMissingComponent(s,u),Number.isNaN(s)&&(s=0),u=fillInMissingComponent(u,s),Number.isNaN(u)&&(u=0),i=fillInMissingComponent(i,c),c=fillInMissingComponent(c,i),h=fillInMissingComponent(h,m),m=fillInMissingComponent(m,h);const v=u-s;switch(a){case"shorter":v>180?s+=360:v<-180&&(u+=360);break;case"longer":-1800?s+=360:u+=360);break;case"increasing":v<0&&(u+=360);break;case"decreasing":v>0&&(s+=360);break;default:throw new Error("Unknown hue interpolation method")}i=premultiply(i,p),h=premultiply(h,p),c=premultiply(c,N),m=premultiply(m,N);let f=[0,0,0];const y=interpolate(p,N,t);switch(e){case he.HSL:case he.HWB:f=[interpolate(s,u,t),un_premultiply(interpolate(i,c,t),y),un_premultiply(interpolate(h,m,t),y)];break;case he.LCH:case he.OKLCH:f=[un_premultiply(interpolate(i,c,t),y),un_premultiply(interpolate(h,m,t),y),interpolate(s,u,t)]}return{colorNotation:e,channels:f,alpha:y,syntaxFlags:new Set([me.ColorMix])}}function fillInMissingComponent(e,a){return Number.isNaN(e)?a:e}function interpolate(e,a,n){return e*n+a*(1-n)}function premultiply(e,a){return Number.isNaN(a)?e:Number.isNaN(e)?Number.NaN:e*a}function un_premultiply(e,a){return 0===a||Number.isNaN(a)?e:Number.isNaN(e)?Number.NaN:e/a}function hex(e){const a=toLowerCaseAZ(e[4].value);if(a.match(/[^a-f0-9]/))return!1;const n={colorNotation:he.HEX,channels:[0,0,0],alpha:1,syntaxFlags:new Set([me.Hex])},r=a.length;if(3===r){const e=a[0],r=a[1],o=a[2];return n.channels=[parseInt(e+e,16)/255,parseInt(r+r,16)/255,parseInt(o+o,16)/255],n}if(6===r){const e=a[0]+a[1],r=a[2]+a[3],o=a[4]+a[5];return n.channels=[parseInt(e,16)/255,parseInt(r,16)/255,parseInt(o,16)/255],n}if(4===r){const e=a[0],r=a[1],o=a[2],l=a[3];return n.channels=[parseInt(e+e,16)/255,parseInt(r+r,16)/255,parseInt(o+o,16)/255],n.alpha=parseInt(l+l,16)/255,n.syntaxFlags.add(me.HasAlpha),n}if(8===r){const e=a[0]+a[1],r=a[2]+a[3],o=a[4]+a[5],l=a[6]+a[7];return n.channels=[parseInt(e,16)/255,parseInt(r,16)/255,parseInt(o,16)/255],n.alpha=parseInt(l,16)/255,n.syntaxFlags.add(me.HasAlpha),n}return!1}function normalizeHue(n){if(o(n))return n[4].value=n[4].value%360,n[1]=n[4].value.toString(),n;if(u(n)){let r=n[4].value;switch(toLowerCaseAZ(n[4].unit)){case"deg":break;case"rad":r=180*n[4].value/Math.PI;break;case"grad":r=.9*n[4].value;break;case"turn":r=360*n[4].value;break;default:return!1}return r%=360,[e.Number,r.toString(),n[2],n[3],{value:r,type:a.Number}]}return!1}function normalize_legacy_HSL_ChannelValues(n,l,t){if(0===l){const e=normalizeHue(n);return!1!==e&&(u(n)&&t.syntaxFlags.add(me.HasDimensionValues),e)}if(r(n)){3===l?t.syntaxFlags.add(me.HasPercentageAlpha):t.syntaxFlags.add(me.HasPercentageValues);let r=normalize(n[4].value,1,0,100);return 3===l&&(r=normalize(n[4].value,100,0,1)),[e.Number,r.toString(),n[2],n[3],{value:r,type:a.Number}]}if(o(n)){if(3!==l)return!1;let r=normalize(n[4].value,1,0,100);return 3===l&&(r=normalize(n[4].value,1,0,1)),[e.Number,r.toString(),n[2],n[3],{value:r,type:a.Number}]}return!1}function normalize_modern_HSL_ChannelValues(l,t,s){if(n(l)&&"none"===toLowerCaseAZ(l[4].value))return s.syntaxFlags.add(me.HasNoneKeywords),[e.Number,"none",l[2],l[3],{value:Number.NaN,type:a.Number}];if(0===t){const e=normalizeHue(l);return!1!==e&&(u(l)&&s.syntaxFlags.add(me.HasDimensionValues),e)}if(r(l)){3===t?s.syntaxFlags.add(me.HasPercentageAlpha):s.syntaxFlags.add(me.HasPercentageValues);let n=l[4].value;return 3===t?n=normalize(l[4].value,100,0,1):1===t&&(n=normalize(l[4].value,1,0,2147483647)),[e.Number,n.toString(),l[2],l[3],{value:n,type:a.Number}]}if(o(l)){3!==t&&s.syntaxFlags.add(me.HasNumberValues);let n=l[4].value;return 3===t?n=normalize(l[4].value,1,0,1):1===t&&(n=normalize(l[4].value,1,0,2147483647)),[e.Number,n.toString(),l[2],l[3],{value:n,type:a.Number}]}return!1}function threeChannelLegacySyntax(e,a,n,r){const l=[],u=[],i=[],c=[],h={colorNotation:n,channels:[0,0,0],alpha:1,syntaxFlags:new Set(r)};let m=l;for(let a=0;ane(e)&&s(e.value))){const a=hslCommaSeparated(e);if(!1!==a)return a}{const n=hslSpaceSeparated(e,a);if(!1!==n)return n}return!1}function hslCommaSeparated(e){return threeChannelLegacySyntax(e,normalize_legacy_HSL_ChannelValues,he.HSL,[me.LegacyHSL])}function hslSpaceSeparated(e,a){return threeChannelSpaceSeparated(e,normalize_modern_HSL_ChannelValues,he.HSL,[],a)}function normalize_HWB_ChannelValues(l,t,s){if(n(l)&&"none"===toLowerCaseAZ(l[4].value))return s.syntaxFlags.add(me.HasNoneKeywords),[e.Number,"none",l[2],l[3],{value:Number.NaN,type:a.Number}];if(0===t){const e=normalizeHue(l);return!1!==e&&(u(l)&&s.syntaxFlags.add(me.HasDimensionValues),e)}if(r(l)){3===t?s.syntaxFlags.add(me.HasPercentageAlpha):s.syntaxFlags.add(me.HasPercentageValues);let n=l[4].value;return 3===t&&(n=normalize(l[4].value,100,0,1)),[e.Number,n.toString(),l[2],l[3],{value:n,type:a.Number}]}if(o(l)){3!==t&&s.syntaxFlags.add(me.HasNumberValues);let n=l[4].value;return 3===t&&(n=normalize(l[4].value,1,0,1)),[e.Number,n.toString(),l[2],l[3],{value:n,type:a.Number}]}return!1}function normalize_Lab_ChannelValues(l,t,s){if(n(l)&&"none"===toLowerCaseAZ(l[4].value))return s.syntaxFlags.add(me.HasNoneKeywords),[e.Number,"none",l[2],l[3],{value:Number.NaN,type:a.Number}];if(r(l)){3!==t&&s.syntaxFlags.add(me.HasPercentageValues);let n=normalize(l[4].value,1,0,100);return 1===t||2===t?n=normalize(l[4].value,.8,-2147483647,2147483647):3===t&&(n=normalize(l[4].value,100,0,1)),[e.Number,n.toString(),l[2],l[3],{value:n,type:a.Number}]}if(o(l)){3!==t&&s.syntaxFlags.add(me.HasNumberValues);let n=normalize(l[4].value,1,0,100);return 1===t||2===t?n=normalize(l[4].value,1,-2147483647,2147483647):3===t&&(n=normalize(l[4].value,1,0,1)),[e.Number,n.toString(),l[2],l[3],{value:n,type:a.Number}]}return!1}function lab(e,a){return threeChannelSpaceSeparated(e,normalize_Lab_ChannelValues,he.Lab,[],a)}function normalize_LCH_ChannelValues(l,t,s){if(n(l)&&"none"===toLowerCaseAZ(l[4].value))return s.syntaxFlags.add(me.HasNoneKeywords),[e.Number,"none",l[2],l[3],{value:Number.NaN,type:a.Number}];if(2===t){const e=normalizeHue(l);return!1!==e&&(u(l)&&s.syntaxFlags.add(me.HasDimensionValues),e)}if(r(l)){3!==t&&s.syntaxFlags.add(me.HasPercentageValues);let n=normalize(l[4].value,1,0,100);return 1===t?n=normalize(l[4].value,100/150,0,2147483647):3===t&&(n=normalize(l[4].value,100,0,1)),[e.Number,n.toString(),l[2],l[3],{value:n,type:a.Number}]}if(o(l)){3!==t&&s.syntaxFlags.add(me.HasNumberValues);let n=normalize(l[4].value,1,0,100);return 1===t?n=normalize(l[4].value,1,0,2147483647):3===t&&(n=normalize(l[4].value,1,0,1)),[e.Number,n.toString(),l[2],l[3],{value:n,type:a.Number}]}return!1}function lch(e,a){return threeChannelSpaceSeparated(e,normalize_LCH_ChannelValues,he.LCH,[],a)}const ye=new Map;for(const[e,a]of Object.entries(G))ye.set(e,a);function namedColor(e){const a=ye.get(toLowerCaseAZ(e));return!!a&&{colorNotation:he.RGB,channels:[a[0]/255,a[1]/255,a[2]/255],alpha:1,syntaxFlags:new Set([me.ColorKeyword,me.NamedColor])}}function normalize_OKLab_ChannelValues(l,t,s){if(n(l)&&"none"===toLowerCaseAZ(l[4].value))return s.syntaxFlags.add(me.HasNoneKeywords),[e.Number,"none",l[2],l[3],{value:Number.NaN,type:a.Number}];if(r(l)){3!==t&&s.syntaxFlags.add(me.HasPercentageValues);let n=normalize(l[4].value,100,0,1);return 1===t||2===t?n=normalize(l[4].value,250,-2147483647,2147483647):3===t&&(n=normalize(l[4].value,100,0,1)),[e.Number,n.toString(),l[2],l[3],{value:n,type:a.Number}]}if(o(l)){3!==t&&s.syntaxFlags.add(me.HasNumberValues);let n=normalize(l[4].value,1,0,1);return 1===t||2===t?n=normalize(l[4].value,1,-2147483647,2147483647):3===t&&(n=normalize(l[4].value,1,0,1)),[e.Number,n.toString(),l[2],l[3],{value:n,type:a.Number}]}return!1}function oklab(e,a){return threeChannelSpaceSeparated(e,normalize_OKLab_ChannelValues,he.OKLab,[],a)}function normalize_OKLCH_ChannelValues(l,t,s){if(n(l)&&"none"===toLowerCaseAZ(l[4].value))return s.syntaxFlags.add(me.HasNoneKeywords),[e.Number,"none",l[2],l[3],{value:Number.NaN,type:a.Number}];if(2===t){const e=normalizeHue(l);return!1!==e&&(u(l)&&s.syntaxFlags.add(me.HasDimensionValues),e)}if(r(l)){3!==t&&s.syntaxFlags.add(me.HasPercentageValues);let n=normalize(l[4].value,100,0,1);return 1===t?n=normalize(l[4].value,250,0,2147483647):3===t&&(n=normalize(l[4].value,100,0,1)),[e.Number,n.toString(),l[2],l[3],{value:n,type:a.Number}]}if(o(l)){3!==t&&s.syntaxFlags.add(me.HasNumberValues);let n=normalize(l[4].value,1,0,1);return 1===t?n=normalize(l[4].value,1,0,2147483647):3===t&&(n=normalize(l[4].value,1,0,1)),[e.Number,n.toString(),l[2],l[3],{value:n,type:a.Number}]}return!1}function oklch(e,a){return threeChannelSpaceSeparated(e,normalize_OKLCH_ChannelValues,he.OKLCH,[],a)}function normalize_legacy_sRGB_ChannelValues(n,l,t){if(r(n)){3===l?t.syntaxFlags.add(me.HasPercentageAlpha):t.syntaxFlags.add(me.HasPercentageValues);const r=normalize(n[4].value,100,0,1);return[e.Number,r.toString(),n[2],n[3],{value:r,type:a.Number}]}if(o(n)){3!==l&&t.syntaxFlags.add(me.HasNumberValues);let r=normalize(n[4].value,255,0,1);return 3===l&&(r=normalize(n[4].value,1,0,1)),[e.Number,r.toString(),n[2],n[3],{value:r,type:a.Number}]}return!1}function normalize_modern_sRGB_ChannelValues(l,t,s){if(n(l)&&"none"===l[4].value.toLowerCase())return s.syntaxFlags.add(me.HasNoneKeywords),[e.Number,"none",l[2],l[3],{value:Number.NaN,type:a.Number}];if(r(l)){3!==t&&s.syntaxFlags.add(me.HasPercentageValues);let n=normalize(l[4].value,100,-2147483647,2147483647);return 3===t&&(n=normalize(l[4].value,100,0,1)),[e.Number,n.toString(),l[2],l[3],{value:n,type:a.Number}]}if(o(l)){3!==t&&s.syntaxFlags.add(me.HasNumberValues);let n=normalize(l[4].value,255,-2147483647,2147483647);return 3===t&&(n=normalize(l[4].value,1,0,1)),[e.Number,n.toString(),l[2],l[3],{value:n,type:a.Number}]}return!1}function rgb(e,a){if(e.value.some(e=>ne(e)&&s(e.value))){const a=rgbCommaSeparated(e);if(!1!==a)return(!a.syntaxFlags.has(me.HasNumberValues)||!a.syntaxFlags.has(me.HasPercentageValues))&&a}else{const n=rgbSpaceSeparated(e,a);if(!1!==n)return n}return!1}function rgbCommaSeparated(e){return threeChannelLegacySyntax(e,normalize_legacy_sRGB_ChannelValues,he.RGB,[me.LegacyRGB])}function rgbSpaceSeparated(e,a){return threeChannelSpaceSeparated(e,normalize_modern_sRGB_ChannelValues,he.RGB,[],a)}function XYZ_D50_to_sRGB_Gamut(e){const a=x(e);if(A(a))return X(a);let n=e;return n=p(n),n[0]<1e-6&&(n=[0,0,0]),n[0]>.999999&&(n=[1,0,0]),K(Y(n,oklch_to_lin_srgb,lin_srgb_to_oklch))}function oklch_to_lin_srgb(e){return e=I(e),e=O(e),W(e)}function lin_srgb_to_oklch(e){return e=E(e),e=U(e),$(e)}function contrastColor(e,a){let n=!1;for(let r=0;rl?[1,1,1]:[0,0,0],r}function alpha(e,a){let r,s,u=!1,i=!1,c=!1;const h={colorNotation:he.sRGB,channels:[0,0,0],alpha:1,syntaxFlags:new Set([])};for(let m=0;m{if(ne(e)&&n(e.value)&&"alpha"===toLowerCaseAZ(e.value[4].value)&&r&&r.has("alpha"))return new oe(r.get("alpha"))});h.alpha=e[0][0],i=!0;continue}return!1}if(c)return!1;for(;ee(e.value[m+1])||ae(e.value[m+1]);)m++;if(m++,p=e.value[m],c=a(p),!1===c)return!1;r=normalizeRelativeColorDataChannels(c),s=noneToZeroInRelativeColorDataChannels(r),h.syntaxFlags=new Set(c.syntaxFlags),h.syntaxFlags.add(me.RelativeAlphaSyntax),h.channels=[...c.channels],h.colorNotation=c.colorNotation,h.alpha=c.alpha}}return!!r&&h}function XYZ_D50_to_P3_Gamut(e){const a=C(e);if(A(a))return X(a);let n=e;return n=p(n),n[0]<1e-6&&(n=[0,0,0]),n[0]>.999999&&(n=[1,0,0]),q(Y(n,oklch_to_lin_p3,lin_p3_to_oklch))}function oklch_to_lin_p3(e){return e=I(e),e=O(e),J(e)}function lin_p3_to_oklch(e){return e=Q(e),e=U(e),$(e)}function toPrecision(e,a=7){e=+e,a=+a;const n=(Math.floor(Math.abs(e))+"").length;if(a>n)return+e.toFixed(a-n);{const r=10**(n-a);return Math.round(e/r)*r}}function serializeWithAlpha(n,r,o,l){const t=[e.CloseParen,")",-1,-1,void 0];if("number"==typeof n.alpha){const s=Math.min(1,Math.max(0,toPrecision(Number.isNaN(n.alpha)?0:n.alpha)));return 1===toPrecision(s,4)?new se(r,t,l):new se(r,t,[...l,new ue([o]),new oe([e.Delim,"/",-1,-1,{value:"/"}]),new ue([o]),new oe([e.Number,toPrecision(s,4).toString(),-1,-1,{value:n.alpha,type:a.Integer}])])}return new se(r,t,[...l,new ue([o]),new oe([e.Delim,"/",-1,-1,{value:"/"}]),new ue([o]),n.alpha])}function serializeP3(n,r=!0){n.channels=convertPowerlessComponentsToZeroValuesForDisplay(n.channels,n.colorNotation);let o=n.channels.map(e=>Number.isNaN(e)?0:e);r?o=XYZ_D50_to_P3_Gamut(colorData_to_XYZ_D50(n).channels):n.colorNotation!==he.Display_P3&&(o=C(colorData_to_XYZ_D50(n).channels));const l=r?Math.min(1,Math.max(0,toPrecision(o[0],6))):toPrecision(o[0],6),t=r?Math.min(1,Math.max(0,toPrecision(o[1],6))):toPrecision(o[1],6),s=r?Math.min(1,Math.max(0,toPrecision(o[2],6))):toPrecision(o[2],6),u=[e.Function,"color(",-1,-1,{value:"color"}],i=[e.Whitespace," ",-1,-1,void 0];return serializeWithAlpha(n,u,i,[new oe([e.Ident,"display-p3",-1,-1,{value:"display-p3"}]),new ue([i]),new oe([e.Number,l.toString(),-1,-1,{value:o[0],type:a.Number}]),new ue([i]),new oe([e.Number,t.toString(),-1,-1,{value:o[1],type:a.Number}]),new ue([i]),new oe([e.Number,s.toString(),-1,-1,{value:o[2],type:a.Number}])])}function serializeRGB(n,r=!0){let o;n.channels=convertPowerlessComponentsToZeroValuesForDisplay(n.channels,n.colorNotation),o=r?XYZ_D50_to_sRGB_Gamut(colorData_to_XYZ_D50(n).channels):x(colorData_to_XYZ_D50(n).channels);const l=Math.min(255,Math.max(0,Math.round(255*toPrecision(o[0])))),t=Math.min(255,Math.max(0,Math.round(255*toPrecision(o[1])))),s=Math.min(255,Math.max(0,Math.round(255*toPrecision(o[2])))),u=[e.CloseParen,")",-1,-1,void 0],i=[e.Whitespace," ",-1,-1,void 0],c=[e.Comma,",",-1,-1,void 0],h=[new oe([e.Number,l.toString(),-1,-1,{value:Math.min(255,255*Math.max(0,o[0])),type:a.Integer}]),new oe(c),new ue([i]),new oe([e.Number,t.toString(),-1,-1,{value:Math.min(255,255*Math.max(0,o[1])),type:a.Integer}]),new oe(c),new ue([i]),new oe([e.Number,s.toString(),-1,-1,{value:Math.min(255,255*Math.max(0,o[2])),type:a.Integer}])];if("number"==typeof n.alpha){const r=Math.min(1,Math.max(0,toPrecision(Number.isNaN(n.alpha)?0:n.alpha)));return 1===toPrecision(r,4)?new se([e.Function,"rgb(",-1,-1,{value:"rgb"}],u,h):new se([e.Function,"rgba(",-1,-1,{value:"rgba"}],u,[...h,new oe(c),new ue([i]),new oe([e.Number,toPrecision(r,4).toString(),-1,-1,{value:n.alpha,type:a.Number}])])}return new se([e.Function,"rgba(",-1,-1,{value:"rgba"}],u,[...h,new oe(c),new ue([i]),n.alpha])}function serializeHSL(n,r=!0){let o;n.channels=convertPowerlessComponentsToZeroValuesForDisplay(n.channels,n.colorNotation),o=v(r?T(XYZ_D50_to_sRGB_Gamut(colorData_to_XYZ_D50(n).channels)):colorData_to_XYZ_D50(n).channels),o=o.map(e=>Number.isNaN(e)?0:e);const l=Math.min(360,Math.max(0,Math.round(toPrecision(o[0])))),t=Math.min(100,Math.max(0,Math.round(toPrecision(o[1])))),s=Math.min(100,Math.max(0,Math.round(toPrecision(o[2])))),u=[e.CloseParen,")",-1,-1,void 0],i=[e.Whitespace," ",-1,-1,void 0],c=[e.Comma,",",-1,-1,void 0],h=[new oe([e.Number,l.toString(),-1,-1,{value:o[0],type:a.Integer}]),new oe(c),new ue([i]),new oe([e.Percentage,t.toString()+"%",-1,-1,{value:o[1]}]),new oe(c),new ue([i]),new oe([e.Percentage,s.toString()+"%",-1,-1,{value:o[2]}])];if("number"==typeof n.alpha){const r=Math.min(1,Math.max(0,toPrecision(Number.isNaN(n.alpha)?0:n.alpha)));return 1===toPrecision(r,4)?new se([e.Function,"hsl(",-1,-1,{value:"hsl"}],u,h):new se([e.Function,"hsla(",-1,-1,{value:"hsla"}],u,[...h,new oe(c),new ue([i]),new oe([e.Number,toPrecision(r,4).toString(),-1,-1,{value:n.alpha,type:a.Number}])])}return new se([e.Function,"hsla(",-1,-1,{value:"hsla"}],u,[...h,new oe(c),new ue([i]),n.alpha])}function serializeOKLCH(n){n.channels=convertPowerlessComponentsToZeroValuesForDisplay(n.channels,n.colorNotation);let r=n.channels.map(e=>Number.isNaN(e)?0:e);n.colorNotation!==he.OKLCH&&(r=p(colorData_to_XYZ_D50(n).channels));const o=toPrecision(r[0],6),l=toPrecision(r[1],6),t=toPrecision(r[2],6),s=[e.Function,"oklch(",-1,-1,{value:"oklch"}],u=[e.Whitespace," ",-1,-1,void 0];return serializeWithAlpha(n,s,u,[new oe([e.Number,o.toString(),-1,-1,{value:r[0],type:a.Number}]),new ue([u]),new oe([e.Number,l.toString(),-1,-1,{value:r[1],type:a.Number}]),new ue([u]),new oe([e.Number,t.toString(),-1,-1,{value:r[2],type:a.Number}])])}function color(e){if(re(e)){switch(toLowerCaseAZ(e.getName())){case"rgb":case"rgba":return rgb(e,color);case"hsl":case"hsla":return hsl(e,color);case"hwb":return a=color,threeChannelSpaceSeparated(e,normalize_HWB_ChannelValues,he.HWB,[],a);case"lab":return lab(e,color);case"lch":return lch(e,color);case"oklab":return oklab(e,color);case"oklch":return oklch(e,color);case"color":return color$1(e,color);case"color-mix":return colorMix(e,color);case"contrast-color":return contrastColor(e,color);case"alpha":return alpha(e,color)}}var a;if(ne(e)){if(i(e.value))return hex(e.value);if(n(e.value)){const a=namedColor(e.value[4].value);return!1!==a?a:"transparent"===toLowerCaseAZ(e.value[4].value)&&{colorNotation:he.RGB,channels:[0,0,0],alpha:0,syntaxFlags:new Set([me.ColorKeyword])}}}return!1}export{he as ColorNotation,me as SyntaxFlag,color,colorDataFitsDisplayP3_Gamut,colorDataFitsRGB_Gamut,serializeHSL,serializeOKLCH,serializeP3,serializeRGB}; +import{TokenType as e,NumberType as a,isTokenIdent as n,isTokenPercentage as r,isTokenNumber as o,isTokenDelim as t,isTokenNumeric as l,isTokenComma as s,isTokenDimension as u,isTokenHash as i}from"@csstools/css-tokenizer";import{XYZ_D50_to_XYZ_D65 as c,XYZ_D50_to_XYZ_D50 as h,XYZ_D50_to_OKLab as m,XYZ_D50_to_OKLCH as p,XYZ_D50_to_LCH as N,XYZ_D50_to_Lab as b,XYZ_D50_to_HWB as f,XYZ_D50_to_HSL as v,XYZ_D50_to_a98_RGB as g,XYZ_D50_to_ProPhoto as y,XYZ_D50_to_rec_2020 as d,XYZ_D50_to_lin_P3 as _,XYZ_D50_to_P3 as C,XYZ_D50_to_lin_sRGB as w,XYZ_D50_to_sRGB as x,XYZ_D65_to_XYZ_D50 as L,OKLCH_to_XYZ_D50 as H,LCH_to_XYZ_D50 as k,OKLab_to_XYZ_D50 as P,Lab_to_XYZ_D50 as S,HWB_to_XYZ_D50 as D,HSL_to_XYZ_D50 as M,ProPhoto_RGB_to_XYZ_D50 as F,a98_RGB_to_XYZ_D50 as z,rec_2020_to_XYZ_D50 as Z,lin_P3_to_XYZ_D50 as R,P3_to_XYZ_D50 as B,lin_sRGB_to_XYZ_D50 as V,sRGB_to_XYZ_D50 as T,namedColors as G,inGamut as A,clip as X,gam_sRGB as K,mapGamutRayTrace as Y,OKLCH_to_OKLab as I,OKLab_to_XYZ as O,XYZ_to_lin_sRGB as W,lin_sRGB_to_XYZ as E,XYZ_to_OKLab as U,OKLab_to_OKLCH as $,contrast_ratio_wcag_2_1 as j,gam_P3 as q,XYZ_to_lin_P3 as J,lin_P3_to_XYZ as Q}from"@csstools/color-helpers";import{isWhitespaceNode as ee,isCommentNode as ae,isTokenNode as ne,isFunctionNode as re,TokenNode as oe,isWhiteSpaceOrCommentNode as te,replaceComponentValues as le,FunctionNode as se,WhitespaceNode as ue}from"@csstools/css-parser-algorithms";import{mathFunctionNames as ie,calcFromComponentValues as ce}from"@csstools/css-calc";var he,me;function convertNaNToZero(e){return[Number.isNaN(e[0])?0:e[0],Number.isNaN(e[1])?0:e[1],Number.isNaN(e[2])?0:e[2]]}function colorData_to_XYZ_D50(e){switch(e.colorNotation){case he.HEX:case he.RGB:case he.sRGB:return{...e,colorNotation:he.XYZ_D50,channels:T(convertNaNToZero(e.channels))};case he.Linear_sRGB:return{...e,colorNotation:he.XYZ_D50,channels:V(convertNaNToZero(e.channels))};case he.Display_P3:return{...e,colorNotation:he.XYZ_D50,channels:B(convertNaNToZero(e.channels))};case he.Linear_Display_P3:return{...e,colorNotation:he.XYZ_D50,channels:R(convertNaNToZero(e.channels))};case he.Rec2020:return{...e,colorNotation:he.XYZ_D50,channels:Z(convertNaNToZero(e.channels))};case he.A98_RGB:return{...e,colorNotation:he.XYZ_D50,channels:z(convertNaNToZero(e.channels))};case he.ProPhoto_RGB:return{...e,colorNotation:he.XYZ_D50,channels:F(convertNaNToZero(e.channels))};case he.HSL:return{...e,colorNotation:he.XYZ_D50,channels:M(convertNaNToZero(e.channels))};case he.HWB:return{...e,colorNotation:he.XYZ_D50,channels:D(convertNaNToZero(e.channels))};case he.Lab:return{...e,colorNotation:he.XYZ_D50,channels:S(convertNaNToZero(e.channels))};case he.OKLab:return{...e,colorNotation:he.XYZ_D50,channels:P(convertNaNToZero(e.channels))};case he.LCH:return{...e,colorNotation:he.XYZ_D50,channels:k(convertNaNToZero(e.channels))};case he.OKLCH:return{...e,colorNotation:he.XYZ_D50,channels:H(convertNaNToZero(e.channels))};case he.XYZ_D50:return{...e,colorNotation:he.XYZ_D50,channels:h(convertNaNToZero(e.channels))};case he.XYZ_D65:return{...e,colorNotation:he.XYZ_D50,channels:L(convertNaNToZero(e.channels))};default:throw new Error("Unsupported color notation")}}!function(e){e.A98_RGB="a98-rgb",e.Display_P3="display-p3",e.Linear_Display_P3="display-p3-linear",e.HEX="hex",e.HSL="hsl",e.HWB="hwb",e.LCH="lch",e.Lab="lab",e.Linear_sRGB="srgb-linear",e.OKLCH="oklch",e.OKLab="oklab",e.ProPhoto_RGB="prophoto-rgb",e.RGB="rgb",e.sRGB="srgb",e.Rec2020="rec2020",e.XYZ_D50="xyz-d50",e.XYZ_D65="xyz-d65"}(he||(he={})),function(e){e.ColorKeyword="color-keyword",e.HasAlpha="has-alpha",e.HasDimensionValues="has-dimension-values",e.HasNoneKeywords="has-none-keywords",e.HasNumberValues="has-number-values",e.HasPercentageAlpha="has-percentage-alpha",e.HasPercentageValues="has-percentage-values",e.HasVariableAlpha="has-variable-alpha",e.Hex="hex",e.LegacyHSL="legacy-hsl",e.LegacyRGB="legacy-rgb",e.NamedColor="named-color",e.RelativeColorSyntax="relative-color-syntax",e.ColorMix="color-mix",e.ColorMixVariadic="color-mix-variadic",e.ContrastColor="contrast-color",e.RelativeAlphaSyntax="relative-alpha-syntax",e.Experimental="experimental"}(me||(me={}));const pe=new Set([he.A98_RGB,he.Display_P3,he.Linear_Display_P3,he.HEX,he.Linear_sRGB,he.ProPhoto_RGB,he.RGB,he.sRGB,he.Rec2020,he.XYZ_D50,he.XYZ_D65]);function colorDataTo(e,a){const n={...e};if(e.colorNotation!==a){const e=colorData_to_XYZ_D50(n);switch(a){case he.HEX:case he.RGB:n.colorNotation=he.RGB,n.channels=x(e.channels);break;case he.sRGB:n.colorNotation=he.sRGB,n.channels=x(e.channels);break;case he.Linear_sRGB:n.colorNotation=he.Linear_sRGB,n.channels=w(e.channels);break;case he.Display_P3:n.colorNotation=he.Display_P3,n.channels=C(e.channels);break;case he.Linear_Display_P3:n.colorNotation=he.Linear_Display_P3,n.channels=_(e.channels);break;case he.Rec2020:n.colorNotation=he.Rec2020,n.channels=d(e.channels);break;case he.ProPhoto_RGB:n.colorNotation=he.ProPhoto_RGB,n.channels=y(e.channels);break;case he.A98_RGB:n.colorNotation=he.A98_RGB,n.channels=g(e.channels);break;case he.HSL:n.colorNotation=he.HSL,n.channels=v(e.channels);break;case he.HWB:n.colorNotation=he.HWB,n.channels=f(e.channels);break;case he.Lab:n.colorNotation=he.Lab,n.channels=b(e.channels);break;case he.LCH:n.colorNotation=he.LCH,n.channels=N(e.channels);break;case he.OKLCH:n.colorNotation=he.OKLCH,n.channels=p(e.channels);break;case he.OKLab:n.colorNotation=he.OKLab,n.channels=m(e.channels);break;case he.XYZ_D50:n.colorNotation=he.XYZ_D50,n.channels=h(e.channels);break;case he.XYZ_D65:n.colorNotation=he.XYZ_D65,n.channels=c(e.channels);break;default:throw new Error("Unsupported color notation")}}else n.channels=convertNaNToZero(e.channels);if(a===e.colorNotation)n.channels=carryForwardMissingComponents(e.channels,[0,1,2],[],n.channels,[0,1,2],[]);else if(pe.has(a)&&pe.has(e.colorNotation))n.channels=carryForwardMissingComponents(e.channels,[0,1,2],[],n.channels,[0,1,2],[]);else switch(a){case he.HSL:switch(e.colorNotation){case he.HWB:n.channels=carryForwardMissingComponents(e.channels,[0],[1,2],n.channels,[0],[1,2]);break;case he.Lab:case he.OKLab:n.channels=carryForwardMissingComponents(e.channels,[0],[1,2],n.channels,[2],[0,1]);break;case he.LCH:case he.OKLCH:n.channels=carryForwardMissingComponents(e.channels,[0,1,2],[],n.channels,[2,1,0],[]);break;default:n.channels=carryForwardMissingComponents(e.channels,[],[],n.channels,[],[])}break;case he.HWB:switch(e.colorNotation){case he.HSL:n.channels=carryForwardMissingComponents(e.channels,[0],[1,2],n.channels,[0],[1,2]);break;case he.LCH:case he.OKLCH:n.channels=carryForwardMissingComponents(e.channels,[2],[0,1],n.channels,[0],[1,2]);break;default:n.channels=carryForwardMissingComponents(e.channels,[],[],n.channels,[],[])}break;case he.Lab:case he.OKLab:switch(e.colorNotation){case he.HSL:n.channels=carryForwardMissingComponents(e.channels,[2],[0,1],n.channels,[0],[1,2]);break;case he.Lab:case he.OKLab:n.channels=carryForwardMissingComponents(e.channels,[0,1,2],[],n.channels,[0,1,2],[]);break;case he.LCH:case he.OKLCH:n.channels=carryForwardMissingComponents(e.channels,[0],[1,2],n.channels,[0],[1,2]);break;default:n.channels=carryForwardMissingComponents(e.channels,[],[],n.channels,[],[])}break;case he.LCH:case he.OKLCH:switch(e.colorNotation){case he.HSL:n.channels=carryForwardMissingComponents(e.channels,[0,1,2],[],n.channels,[2,1,0],[]);break;case he.HWB:n.channels=carryForwardMissingComponents(e.channels,[0],[1,2],n.channels,[2],[0,1]);break;case he.Lab:case he.OKLab:n.channels=carryForwardMissingComponents(e.channels,[0],[1,2],n.channels,[0],[1,2]);break;case he.LCH:case he.OKLCH:n.channels=carryForwardMissingComponents(e.channels,[0,1,2],[],n.channels,[0,1,2],[]);break;default:n.channels=carryForwardMissingComponents(e.channels,[],[],n.channels,[],[])}break;default:n.channels=carryForwardMissingComponents(e.channels,[],[],n.channels,[],[])}return n.channels=convertPowerlessComponentsToMissingComponents(n.channels,a),n}function convertPowerlessComponentsToMissingComponents(e,a){const n=[...e];switch(a){case he.HSL:!Number.isNaN(n[1])&&n[1]<=.001&&(n[0]=Number.NaN);break;case he.HWB:!Number.isNaN(n[1])&&!Number.isNaN(n[2])&&Math.max(0,n[1])+Math.max(0,n[2])>=99.999&&(n[0]=Number.NaN);break;case he.LCH:!Number.isNaN(n[1])&&n[1]<=.0015&&(n[2]=Number.NaN);break;case he.OKLCH:!Number.isNaN(n[1])&&n[1]<=4e-6&&(n[2]=Number.NaN)}return n}function convertPowerlessComponentsToZeroValuesForDisplay(e,a){const n=[...e];switch(a){case he.HSL:(reducePrecision(n[2])<=0||reducePrecision(n[2])>=100)&&(n[0]=Number.NaN,n[1]=Number.NaN),!Number.isNaN(n[1])&&n[1]<=.001&&(n[0]=Number.NaN);break;case he.HWB:!Number.isNaN(n[1])&&!Number.isNaN(n[2])&&Math.max(0,n[1])+Math.max(0,n[2])>=99.999&&(n[0]=Number.NaN);break;case he.Lab:(reducePrecision(n[0])<=0||reducePrecision(n[0])>=100)&&(n[1]=Number.NaN,n[2]=Number.NaN);break;case he.LCH:!Number.isNaN(n[1])&&n[1]<=.0015&&(n[2]=Number.NaN),(reducePrecision(n[0])<=0||reducePrecision(n[0])>=100)&&(n[1]=Number.NaN,n[2]=Number.NaN);break;case he.OKLab:(reducePrecision(n[0])<=0||reducePrecision(n[0])>=1)&&(n[1]=Number.NaN,n[2]=Number.NaN);break;case he.OKLCH:!Number.isNaN(n[1])&&n[1]<=4e-6&&(n[2]=Number.NaN),(reducePrecision(n[0])<=0||reducePrecision(n[0])>=1)&&(n[1]=Number.NaN,n[2]=Number.NaN)}return n}function carryForwardMissingComponents(e,a,n,r,o,t){if(a.length<3&&e.every(Number.isNaN))return[Number.NaN,Number.NaN,Number.NaN];const l=[...r];for(let n=0;nNumber.isNaN(e[a])))for(let e=0;ee<-1e-5||e>1.00001)}function colorDataFitsDisplayP3_Gamut(e){const a={...e,channels:[...e.channels]};a.channels=convertPowerlessComponentsToZeroValuesForDisplay(a.channels,a.colorNotation);return!colorDataTo(a,he.Display_P3).channels.find(e=>e<-1e-5||e>1.00001)}function normalize(e,a,n,r){return Math.min(Math.max(e/a,n),r)}const Ne=/[A-Z]/g;function toLowerCaseAZ(e){return e.replace(Ne,e=>String.fromCharCode(e.charCodeAt(0)+32))}function normalize_Color_ChannelValues(t,l,s){if(n(t)&&"none"===toLowerCaseAZ(t[4].value))return s.syntaxFlags.add(me.HasNoneKeywords),[e.Number,"none",t[2],t[3],{value:Number.NaN,type:a.Number}];if(r(t)){3!==l&&s.syntaxFlags.add(me.HasPercentageValues);let n=normalize(t[4].value,100,-2147483647,2147483647);return 3===l&&(n=normalize(t[4].value,100,0,1)),[e.Number,n.toString(),t[2],t[3],{value:n,type:a.Number}]}if(o(t)){3!==l&&s.syntaxFlags.add(me.HasNumberValues);let n=normalize(t[4].value,1,-2147483647,2147483647);return 3===l&&(n=normalize(t[4].value,1,0,1)),[e.Number,n.toString(),t[2],t[3],{value:n,type:a.Number}]}return!1}const be=new Set(["srgb","srgb-linear","display-p3","display-p3-linear","a98-rgb","prophoto-rgb","rec2020","xyz","xyz-d50","xyz-d65"]);function color$1(e,a){const r=[],s=[],u=[],i=[];let c,h,m=!1,p=!1;const N={colorNotation:he.sRGB,channels:[0,0,0],alpha:1,syntaxFlags:new Set([])};let b=r;for(let o=0;o=0){t=i.value[4].value;continue}}return!1}if(!o)return!1;n.push({color:o,percentage:t}),o=!1,t=!1}}return!!o&&(n.push({color:o,percentage:t}),n)}function colorMixRectangular(e,a){if(!a||!a.length)return!1;for(const e of a)if(e.percentage&&(e.percentage<0||e.percentage>100))return!1;const{items:n,leftover:r}=normalizeMixPercentages(a,!0),o=1-r/100;let t;switch(e){case"srgb":t=he.RGB;break;case"srgb-linear":t=he.Linear_sRGB;break;case"display-p3":t=he.Display_P3;break;case"display-p3-linear":t=he.Linear_Display_P3;break;case"a98-rgb":t=he.A98_RGB;break;case"prophoto-rgb":t=he.ProPhoto_RGB;break;case"rec2020":t=he.Rec2020;break;case"lab":t=he.Lab;break;case"oklab":t=he.OKLab;break;case"xyz-d50":t=he.XYZ_D50;break;case"xyz":case"xyz-d65":t=he.XYZ_D65;break;default:return!1}if(1===n.length){const e=colorDataTo(n[0].color,t);return e.colorNotation=t,e.syntaxFlags.add(me.ColorMixVariadic),"number"!=typeof e.alpha?!1:(e.alpha=e.alpha*o,e)}for(n.reverse();n.length>=2;){const e=n.pop(),a=n.pop();if(!e||!a)return!1;const r=e.percentage+a.percentage,o=r>0?a.percentage/r:.5,l=colorMixRectangularPair(t,e.color,a.color,o);if(!l)return!1;n.push({color:l,percentage:r})}const l=n[0]?.color;return!!l&&("number"==typeof l.alpha&&(l.alpha=l.alpha*o,a.some(e=>e.color.syntaxFlags.has(me.Experimental))&&l.syntaxFlags.add(me.Experimental),2!==a.length&&l.syntaxFlags.add(me.ColorMixVariadic),l))}function colorMixRectangularPair(e,a,n,r){let o=a.alpha;if("number"!=typeof o)return!1;let t=n.alpha;if("number"!=typeof t)return!1;o=Number.isNaN(o)?t:o,t=Number.isNaN(t)?o:t;const l=colorDataTo(a,e).channels,s=colorDataTo(n,e).channels;l[0]=fillInMissingComponent(l[0],s[0]),s[0]=fillInMissingComponent(s[0],l[0]),l[1]=fillInMissingComponent(l[1],s[1]),s[1]=fillInMissingComponent(s[1],l[1]),l[2]=fillInMissingComponent(l[2],s[2]),s[2]=fillInMissingComponent(s[2],l[2]),l[0]=premultiply(l[0],o),l[1]=premultiply(l[1],o),l[2]=premultiply(l[2],o),s[0]=premultiply(s[0],t),s[1]=premultiply(s[1],t),s[2]=premultiply(s[2],t);const u=interpolate(o,t,r);return{colorNotation:e,channels:[un_premultiply(interpolate(l[0],s[0],r),u),un_premultiply(interpolate(l[1],s[1],r),u),un_premultiply(interpolate(l[2],s[2],r),u)],alpha:u,syntaxFlags:new Set([me.ColorMix])}}function colorMixPolar(e,a,n){if(!n||!n.length)return!1;for(const e of n)if(e.percentage&&(e.percentage<0||e.percentage>100))return!1;const{items:r,leftover:o}=normalizeMixPercentages(n,!0),t=1-o/100;let l;switch(e){case"hsl":l=he.HSL;break;case"hwb":l=he.HWB;break;case"lch":l=he.LCH;break;case"oklch":l=he.OKLCH;break;default:return!1}if(1===r.length){const e=colorDataTo(r[0].color,l);return e.colorNotation=l,e.syntaxFlags.add(me.ColorMixVariadic),"number"!=typeof e.alpha?!1:(e.alpha=e.alpha*t,e)}for(r.reverse();r.length>=2;){const e=r.pop(),n=r.pop();if(!e||!n)return!1;const o=e.percentage+n.percentage,t=o>0?n.percentage/o:.5,s=colorMixPolarPair(l,a,e.color,n.color,t);if(!s)return!1;r.push({color:s,percentage:o})}const s=r[0]?.color;return!!s&&("number"==typeof s.alpha&&(s.alpha=s.alpha*t,n.some(e=>e.color.syntaxFlags.has(me.Experimental))&&s.syntaxFlags.add(me.Experimental),2!==n.length&&s.syntaxFlags.add(me.ColorMixVariadic),s))}function colorMixPolarPair(e,a,n,r,o){let t=0,l=0,s=0,u=0,i=0,c=0,h=n.alpha;if("number"!=typeof h)return!1;let m=r.alpha;if("number"!=typeof m)return!1;h=Number.isNaN(h)?m:h,m=Number.isNaN(m)?h:m;const p=colorDataTo(n,e).channels,N=colorDataTo(r,e).channels;switch(e){case he.HSL:case he.HWB:t=p[0],l=N[0],s=p[1],u=N[1],i=p[2],c=N[2];break;case he.LCH:case he.OKLCH:s=p[0],u=N[0],i=p[1],c=N[1],t=p[2],l=N[2]}t=fillInMissingComponent(t,l),Number.isNaN(t)&&(t=0),l=fillInMissingComponent(l,t),Number.isNaN(l)&&(l=0),s=fillInMissingComponent(s,u),u=fillInMissingComponent(u,s),i=fillInMissingComponent(i,c),c=fillInMissingComponent(c,i);const b=l-t;switch(a){case"shorter":b>180?t+=360:b<-180&&(l+=360);break;case"longer":-1800?t+=360:l+=360);break;case"increasing":b<0&&(l+=360);break;case"decreasing":b>0&&(t+=360);break;default:throw new Error("Unknown hue interpolation method")}s=premultiply(s,h),i=premultiply(i,h),u=premultiply(u,m),c=premultiply(c,m);let f=[0,0,0];const v=interpolate(h,m,o);switch(e){case he.HSL:case he.HWB:f=[interpolate(t,l,o),un_premultiply(interpolate(s,u,o),v),un_premultiply(interpolate(i,c,o),v)];break;case he.LCH:case he.OKLCH:f=[un_premultiply(interpolate(s,u,o),v),un_premultiply(interpolate(i,c,o),v),interpolate(t,l,o)]}return{colorNotation:e,channels:f,alpha:v,syntaxFlags:new Set([me.ColorMix])}}function fillInMissingComponent(e,a){return Number.isNaN(e)?a:e}function interpolate(e,a,n){return e*(1-n)+a*n}function premultiply(e,a){return Number.isNaN(a)?e:Number.isNaN(e)?Number.NaN:e*a}function un_premultiply(e,a){return 0===a||Number.isNaN(a)?e:Number.isNaN(e)?Number.NaN:e/a}function normalizeMixPercentages(e,a=!1){let n=0,r=0;for(const a of e)a.percentage&&(n+=a.percentage),!1===a.percentage&&r++;n=Math.min(100,n);for(const a of e)!1===a.percentage&&(a.percentage=(100-n)/r);const o=e.slice();let t=0;for(const e of o)t+=e.percentage;if(t>100||t>0&&a)for(const e of o)e.percentage=e.percentage*(100/t);let l=0;return t<100&&(l=100-t),{items:o,leftover:l}}function hex(e){const a=toLowerCaseAZ(e[4].value);if(a.match(/[^a-f0-9]/))return!1;const n={colorNotation:he.HEX,channels:[0,0,0],alpha:1,syntaxFlags:new Set([me.Hex])},r=a.length;if(3===r){const e=a[0],r=a[1],o=a[2];return n.channels=[parseInt(e+e,16)/255,parseInt(r+r,16)/255,parseInt(o+o,16)/255],n}if(6===r){const e=a[0]+a[1],r=a[2]+a[3],o=a[4]+a[5];return n.channels=[parseInt(e,16)/255,parseInt(r,16)/255,parseInt(o,16)/255],n}if(4===r){const e=a[0],r=a[1],o=a[2],t=a[3];return n.channels=[parseInt(e+e,16)/255,parseInt(r+r,16)/255,parseInt(o+o,16)/255],n.alpha=parseInt(t+t,16)/255,n.syntaxFlags.add(me.HasAlpha),n}if(8===r){const e=a[0]+a[1],r=a[2]+a[3],o=a[4]+a[5],t=a[6]+a[7];return n.channels=[parseInt(e,16)/255,parseInt(r,16)/255,parseInt(o,16)/255],n.alpha=parseInt(t,16)/255,n.syntaxFlags.add(me.HasAlpha),n}return!1}function normalizeHue(n){if(o(n))return n[4].value=n[4].value%360,n[1]=n[4].value.toString(),n;if(u(n)){let r=n[4].value;switch(toLowerCaseAZ(n[4].unit)){case"deg":break;case"rad":r=180*n[4].value/Math.PI;break;case"grad":r=.9*n[4].value;break;case"turn":r=360*n[4].value;break;default:return!1}return r%=360,[e.Number,r.toString(),n[2],n[3],{value:r,type:a.Number}]}return!1}function normalize_legacy_HSL_ChannelValues(n,t,l){if(0===t){const e=normalizeHue(n);return!1!==e&&(u(n)&&l.syntaxFlags.add(me.HasDimensionValues),e)}if(r(n)){3===t?l.syntaxFlags.add(me.HasPercentageAlpha):l.syntaxFlags.add(me.HasPercentageValues);let r=normalize(n[4].value,1,0,100);return 3===t&&(r=normalize(n[4].value,100,0,1)),[e.Number,r.toString(),n[2],n[3],{value:r,type:a.Number}]}if(o(n)){if(3!==t)return!1;let r=normalize(n[4].value,1,0,100);return 3===t&&(r=normalize(n[4].value,1,0,1)),[e.Number,r.toString(),n[2],n[3],{value:r,type:a.Number}]}return!1}function normalize_modern_HSL_ChannelValues(t,l,s){if(n(t)&&"none"===toLowerCaseAZ(t[4].value))return s.syntaxFlags.add(me.HasNoneKeywords),[e.Number,"none",t[2],t[3],{value:Number.NaN,type:a.Number}];if(0===l){const e=normalizeHue(t);return!1!==e&&(u(t)&&s.syntaxFlags.add(me.HasDimensionValues),e)}if(r(t)){3===l?s.syntaxFlags.add(me.HasPercentageAlpha):s.syntaxFlags.add(me.HasPercentageValues);let n=t[4].value;return 3===l?n=normalize(t[4].value,100,0,1):1===l&&(n=normalize(t[4].value,1,0,2147483647)),[e.Number,n.toString(),t[2],t[3],{value:n,type:a.Number}]}if(o(t)){3!==l&&s.syntaxFlags.add(me.HasNumberValues);let n=t[4].value;return 3===l?n=normalize(t[4].value,1,0,1):1===l&&(n=normalize(t[4].value,1,0,2147483647)),[e.Number,n.toString(),t[2],t[3],{value:n,type:a.Number}]}return!1}function threeChannelLegacySyntax(e,a,n,r){const t=[],u=[],i=[],c=[],h={colorNotation:n,channels:[0,0,0],alpha:1,syntaxFlags:new Set(r)};let m=t;for(let a=0;ane(e)&&s(e.value))){const a=hslCommaSeparated(e);if(!1!==a)return a}{const n=hslSpaceSeparated(e,a);if(!1!==n)return n}return!1}function hslCommaSeparated(e){return threeChannelLegacySyntax(e,normalize_legacy_HSL_ChannelValues,he.HSL,[me.LegacyHSL])}function hslSpaceSeparated(e,a){return threeChannelSpaceSeparated(e,normalize_modern_HSL_ChannelValues,he.HSL,[],a)}function normalize_HWB_ChannelValues(t,l,s){if(n(t)&&"none"===toLowerCaseAZ(t[4].value))return s.syntaxFlags.add(me.HasNoneKeywords),[e.Number,"none",t[2],t[3],{value:Number.NaN,type:a.Number}];if(0===l){const e=normalizeHue(t);return!1!==e&&(u(t)&&s.syntaxFlags.add(me.HasDimensionValues),e)}if(r(t)){3===l?s.syntaxFlags.add(me.HasPercentageAlpha):s.syntaxFlags.add(me.HasPercentageValues);let n=t[4].value;return 3===l&&(n=normalize(t[4].value,100,0,1)),[e.Number,n.toString(),t[2],t[3],{value:n,type:a.Number}]}if(o(t)){3!==l&&s.syntaxFlags.add(me.HasNumberValues);let n=t[4].value;return 3===l&&(n=normalize(t[4].value,1,0,1)),[e.Number,n.toString(),t[2],t[3],{value:n,type:a.Number}]}return!1}function normalize_Lab_ChannelValues(t,l,s){if(n(t)&&"none"===toLowerCaseAZ(t[4].value))return s.syntaxFlags.add(me.HasNoneKeywords),[e.Number,"none",t[2],t[3],{value:Number.NaN,type:a.Number}];if(r(t)){3!==l&&s.syntaxFlags.add(me.HasPercentageValues);let n=normalize(t[4].value,1,0,100);return 1===l||2===l?n=normalize(t[4].value,.8,-2147483647,2147483647):3===l&&(n=normalize(t[4].value,100,0,1)),[e.Number,n.toString(),t[2],t[3],{value:n,type:a.Number}]}if(o(t)){3!==l&&s.syntaxFlags.add(me.HasNumberValues);let n=normalize(t[4].value,1,0,100);return 1===l||2===l?n=normalize(t[4].value,1,-2147483647,2147483647):3===l&&(n=normalize(t[4].value,1,0,1)),[e.Number,n.toString(),t[2],t[3],{value:n,type:a.Number}]}return!1}function lab(e,a){return threeChannelSpaceSeparated(e,normalize_Lab_ChannelValues,he.Lab,[],a)}function normalize_LCH_ChannelValues(t,l,s){if(n(t)&&"none"===toLowerCaseAZ(t[4].value))return s.syntaxFlags.add(me.HasNoneKeywords),[e.Number,"none",t[2],t[3],{value:Number.NaN,type:a.Number}];if(2===l){const e=normalizeHue(t);return!1!==e&&(u(t)&&s.syntaxFlags.add(me.HasDimensionValues),e)}if(r(t)){3!==l&&s.syntaxFlags.add(me.HasPercentageValues);let n=normalize(t[4].value,1,0,100);return 1===l?n=normalize(t[4].value,100/150,0,2147483647):3===l&&(n=normalize(t[4].value,100,0,1)),[e.Number,n.toString(),t[2],t[3],{value:n,type:a.Number}]}if(o(t)){3!==l&&s.syntaxFlags.add(me.HasNumberValues);let n=normalize(t[4].value,1,0,100);return 1===l?n=normalize(t[4].value,1,0,2147483647):3===l&&(n=normalize(t[4].value,1,0,1)),[e.Number,n.toString(),t[2],t[3],{value:n,type:a.Number}]}return!1}function lch(e,a){return threeChannelSpaceSeparated(e,normalize_LCH_ChannelValues,he.LCH,[],a)}const ye=new Map;for(const[e,a]of Object.entries(G))ye.set(e,a);function namedColor(e){const a=ye.get(toLowerCaseAZ(e));return!!a&&{colorNotation:he.RGB,channels:[a[0]/255,a[1]/255,a[2]/255],alpha:1,syntaxFlags:new Set([me.ColorKeyword,me.NamedColor])}}function normalize_OKLab_ChannelValues(t,l,s){if(n(t)&&"none"===toLowerCaseAZ(t[4].value))return s.syntaxFlags.add(me.HasNoneKeywords),[e.Number,"none",t[2],t[3],{value:Number.NaN,type:a.Number}];if(r(t)){3!==l&&s.syntaxFlags.add(me.HasPercentageValues);let n=normalize(t[4].value,100,0,1);return 1===l||2===l?n=normalize(t[4].value,250,-2147483647,2147483647):3===l&&(n=normalize(t[4].value,100,0,1)),[e.Number,n.toString(),t[2],t[3],{value:n,type:a.Number}]}if(o(t)){3!==l&&s.syntaxFlags.add(me.HasNumberValues);let n=normalize(t[4].value,1,0,1);return 1===l||2===l?n=normalize(t[4].value,1,-2147483647,2147483647):3===l&&(n=normalize(t[4].value,1,0,1)),[e.Number,n.toString(),t[2],t[3],{value:n,type:a.Number}]}return!1}function oklab(e,a){return threeChannelSpaceSeparated(e,normalize_OKLab_ChannelValues,he.OKLab,[],a)}function normalize_OKLCH_ChannelValues(t,l,s){if(n(t)&&"none"===toLowerCaseAZ(t[4].value))return s.syntaxFlags.add(me.HasNoneKeywords),[e.Number,"none",t[2],t[3],{value:Number.NaN,type:a.Number}];if(2===l){const e=normalizeHue(t);return!1!==e&&(u(t)&&s.syntaxFlags.add(me.HasDimensionValues),e)}if(r(t)){3!==l&&s.syntaxFlags.add(me.HasPercentageValues);let n=normalize(t[4].value,100,0,1);return 1===l?n=normalize(t[4].value,250,0,2147483647):3===l&&(n=normalize(t[4].value,100,0,1)),[e.Number,n.toString(),t[2],t[3],{value:n,type:a.Number}]}if(o(t)){3!==l&&s.syntaxFlags.add(me.HasNumberValues);let n=normalize(t[4].value,1,0,1);return 1===l?n=normalize(t[4].value,1,0,2147483647):3===l&&(n=normalize(t[4].value,1,0,1)),[e.Number,n.toString(),t[2],t[3],{value:n,type:a.Number}]}return!1}function oklch(e,a){return threeChannelSpaceSeparated(e,normalize_OKLCH_ChannelValues,he.OKLCH,[],a)}function normalize_legacy_sRGB_ChannelValues(n,t,l){if(r(n)){3===t?l.syntaxFlags.add(me.HasPercentageAlpha):l.syntaxFlags.add(me.HasPercentageValues);const r=normalize(n[4].value,100,0,1);return[e.Number,r.toString(),n[2],n[3],{value:r,type:a.Number}]}if(o(n)){3!==t&&l.syntaxFlags.add(me.HasNumberValues);let r=normalize(n[4].value,255,0,1);return 3===t&&(r=normalize(n[4].value,1,0,1)),[e.Number,r.toString(),n[2],n[3],{value:r,type:a.Number}]}return!1}function normalize_modern_sRGB_ChannelValues(t,l,s){if(n(t)&&"none"===t[4].value.toLowerCase())return s.syntaxFlags.add(me.HasNoneKeywords),[e.Number,"none",t[2],t[3],{value:Number.NaN,type:a.Number}];if(r(t)){3!==l&&s.syntaxFlags.add(me.HasPercentageValues);let n=normalize(t[4].value,100,-2147483647,2147483647);return 3===l&&(n=normalize(t[4].value,100,0,1)),[e.Number,n.toString(),t[2],t[3],{value:n,type:a.Number}]}if(o(t)){3!==l&&s.syntaxFlags.add(me.HasNumberValues);let n=normalize(t[4].value,255,-2147483647,2147483647);return 3===l&&(n=normalize(t[4].value,1,0,1)),[e.Number,n.toString(),t[2],t[3],{value:n,type:a.Number}]}return!1}function rgb(e,a){if(e.value.some(e=>ne(e)&&s(e.value))){const a=rgbCommaSeparated(e);if(!1!==a)return(!a.syntaxFlags.has(me.HasNumberValues)||!a.syntaxFlags.has(me.HasPercentageValues))&&a}else{const n=rgbSpaceSeparated(e,a);if(!1!==n)return n}return!1}function rgbCommaSeparated(e){return threeChannelLegacySyntax(e,normalize_legacy_sRGB_ChannelValues,he.RGB,[me.LegacyRGB])}function rgbSpaceSeparated(e,a){return threeChannelSpaceSeparated(e,normalize_modern_sRGB_ChannelValues,he.RGB,[],a)}function XYZ_D50_to_sRGB_Gamut(e){const a=x(e);if(A(a))return X(a);let n=e;return n=p(n),n[0]<1e-6&&(n=[0,0,0]),n[0]>.999999&&(n=[1,0,0]),K(Y(n,oklch_to_lin_srgb,lin_srgb_to_oklch))}function oklch_to_lin_srgb(e){return e=I(e),e=O(e),W(e)}function lin_srgb_to_oklch(e){return e=E(e),e=U(e),$(e)}function contrastColor(e,a){let n=!1;for(let r=0;rt?[1,1,1]:[0,0,0],r}function alpha(e,a){let r,s,u=!1,i=!1,c=!1;const h={colorNotation:he.sRGB,channels:[0,0,0],alpha:1,syntaxFlags:new Set([])};for(let m=0;m{if(ne(e)&&n(e.value)&&"alpha"===toLowerCaseAZ(e.value[4].value)&&r&&r.has("alpha"))return new oe(r.get("alpha"))});h.alpha=e[0][0],i=!0;continue}return!1}if(c)return!1;for(;ee(e.value[m+1])||ae(e.value[m+1]);)m++;if(m++,p=e.value[m],c=a(p),!1===c)return!1;r=normalizeRelativeColorDataChannels(c),s=noneToZeroInRelativeColorDataChannels(r),h.syntaxFlags=new Set(c.syntaxFlags),h.syntaxFlags.add(me.RelativeAlphaSyntax),h.channels=[...c.channels],h.colorNotation=c.colorNotation,h.alpha=c.alpha}}return!!r&&h}function XYZ_D50_to_P3_Gamut(e){const a=C(e);if(A(a))return X(a);let n=e;return n=p(n),n[0]<1e-6&&(n=[0,0,0]),n[0]>.999999&&(n=[1,0,0]),q(Y(n,oklch_to_lin_p3,lin_p3_to_oklch))}function oklch_to_lin_p3(e){return e=I(e),e=O(e),J(e)}function lin_p3_to_oklch(e){return e=Q(e),e=U(e),$(e)}function toPrecision(e,a=7){e=+e,a=+a;const n=(Math.floor(Math.abs(e))+"").length;if(a>n)return+e.toFixed(a-n);{const r=10**(n-a);return Math.round(e/r)*r}}function serializeWithAlpha(n,r,o,t){const l=[e.CloseParen,")",-1,-1,void 0];if("number"==typeof n.alpha){const s=Math.min(1,Math.max(0,toPrecision(Number.isNaN(n.alpha)?0:n.alpha)));return 1===toPrecision(s,4)?new se(r,l,t):new se(r,l,[...t,new ue([o]),new oe([e.Delim,"/",-1,-1,{value:"/"}]),new ue([o]),new oe([e.Number,toPrecision(s,4).toString(),-1,-1,{value:n.alpha,type:a.Integer}])])}return new se(r,l,[...t,new ue([o]),new oe([e.Delim,"/",-1,-1,{value:"/"}]),new ue([o]),n.alpha])}function serializeP3(n,r=!0){n.channels=convertPowerlessComponentsToZeroValuesForDisplay(n.channels,n.colorNotation);let o=n.channels.map(e=>Number.isNaN(e)?0:e);r?o=XYZ_D50_to_P3_Gamut(colorData_to_XYZ_D50(n).channels):n.colorNotation!==he.Display_P3&&(o=C(colorData_to_XYZ_D50(n).channels));const t=r?Math.min(1,Math.max(0,toPrecision(o[0],6))):toPrecision(o[0],6),l=r?Math.min(1,Math.max(0,toPrecision(o[1],6))):toPrecision(o[1],6),s=r?Math.min(1,Math.max(0,toPrecision(o[2],6))):toPrecision(o[2],6),u=[e.Function,"color(",-1,-1,{value:"color"}],i=[e.Whitespace," ",-1,-1,void 0];return serializeWithAlpha(n,u,i,[new oe([e.Ident,"display-p3",-1,-1,{value:"display-p3"}]),new ue([i]),new oe([e.Number,t.toString(),-1,-1,{value:o[0],type:a.Number}]),new ue([i]),new oe([e.Number,l.toString(),-1,-1,{value:o[1],type:a.Number}]),new ue([i]),new oe([e.Number,s.toString(),-1,-1,{value:o[2],type:a.Number}])])}function serializeRGB(n,r=!0){let o;n.channels=convertPowerlessComponentsToZeroValuesForDisplay(n.channels,n.colorNotation),o=r?XYZ_D50_to_sRGB_Gamut(colorData_to_XYZ_D50(n).channels):x(colorData_to_XYZ_D50(n).channels);const t=Math.min(255,Math.max(0,Math.round(255*toPrecision(o[0])))),l=Math.min(255,Math.max(0,Math.round(255*toPrecision(o[1])))),s=Math.min(255,Math.max(0,Math.round(255*toPrecision(o[2])))),u=[e.CloseParen,")",-1,-1,void 0],i=[e.Whitespace," ",-1,-1,void 0],c=[e.Comma,",",-1,-1,void 0],h=[new oe([e.Number,t.toString(),-1,-1,{value:Math.min(255,255*Math.max(0,o[0])),type:a.Integer}]),new oe(c),new ue([i]),new oe([e.Number,l.toString(),-1,-1,{value:Math.min(255,255*Math.max(0,o[1])),type:a.Integer}]),new oe(c),new ue([i]),new oe([e.Number,s.toString(),-1,-1,{value:Math.min(255,255*Math.max(0,o[2])),type:a.Integer}])];if("number"==typeof n.alpha){const r=Math.min(1,Math.max(0,toPrecision(Number.isNaN(n.alpha)?0:n.alpha)));return 1===toPrecision(r,4)?new se([e.Function,"rgb(",-1,-1,{value:"rgb"}],u,h):new se([e.Function,"rgba(",-1,-1,{value:"rgba"}],u,[...h,new oe(c),new ue([i]),new oe([e.Number,toPrecision(r,4).toString(),-1,-1,{value:n.alpha,type:a.Number}])])}return new se([e.Function,"rgba(",-1,-1,{value:"rgba"}],u,[...h,new oe(c),new ue([i]),n.alpha])}function serializeHSL(n,r=!0){let o;n.channels=convertPowerlessComponentsToZeroValuesForDisplay(n.channels,n.colorNotation),o=v(r?T(XYZ_D50_to_sRGB_Gamut(colorData_to_XYZ_D50(n).channels)):colorData_to_XYZ_D50(n).channels),o=o.map(e=>Number.isNaN(e)?0:e);const t=Math.min(360,Math.max(0,Math.round(toPrecision(o[0])))),l=Math.min(100,Math.max(0,Math.round(toPrecision(o[1])))),s=Math.min(100,Math.max(0,Math.round(toPrecision(o[2])))),u=[e.CloseParen,")",-1,-1,void 0],i=[e.Whitespace," ",-1,-1,void 0],c=[e.Comma,",",-1,-1,void 0],h=[new oe([e.Number,t.toString(),-1,-1,{value:o[0],type:a.Integer}]),new oe(c),new ue([i]),new oe([e.Percentage,l.toString()+"%",-1,-1,{value:o[1]}]),new oe(c),new ue([i]),new oe([e.Percentage,s.toString()+"%",-1,-1,{value:o[2]}])];if("number"==typeof n.alpha){const r=Math.min(1,Math.max(0,toPrecision(Number.isNaN(n.alpha)?0:n.alpha)));return 1===toPrecision(r,4)?new se([e.Function,"hsl(",-1,-1,{value:"hsl"}],u,h):new se([e.Function,"hsla(",-1,-1,{value:"hsla"}],u,[...h,new oe(c),new ue([i]),new oe([e.Number,toPrecision(r,4).toString(),-1,-1,{value:n.alpha,type:a.Number}])])}return new se([e.Function,"hsla(",-1,-1,{value:"hsla"}],u,[...h,new oe(c),new ue([i]),n.alpha])}function serializeOKLCH(n){n.channels=convertPowerlessComponentsToZeroValuesForDisplay(n.channels,n.colorNotation);let r=n.channels.map(e=>Number.isNaN(e)?0:e);n.colorNotation!==he.OKLCH&&(r=p(colorData_to_XYZ_D50(n).channels));const o=toPrecision(r[0],6),t=toPrecision(r[1],6),l=toPrecision(r[2],6),s=[e.Function,"oklch(",-1,-1,{value:"oklch"}],u=[e.Whitespace," ",-1,-1,void 0];return serializeWithAlpha(n,s,u,[new oe([e.Number,o.toString(),-1,-1,{value:r[0],type:a.Number}]),new ue([u]),new oe([e.Number,t.toString(),-1,-1,{value:r[1],type:a.Number}]),new ue([u]),new oe([e.Number,l.toString(),-1,-1,{value:r[2],type:a.Number}])])}function color(e){if(re(e)){switch(toLowerCaseAZ(e.getName())){case"rgb":case"rgba":return rgb(e,color);case"hsl":case"hsla":return hsl(e,color);case"hwb":return a=color,threeChannelSpaceSeparated(e,normalize_HWB_ChannelValues,he.HWB,[],a);case"lab":return lab(e,color);case"lch":return lch(e,color);case"oklab":return oklab(e,color);case"oklch":return oklch(e,color);case"color":return color$1(e,color);case"color-mix":return colorMix(e,color);case"contrast-color":return contrastColor(e,color);case"alpha":return alpha(e,color)}}var a;if(ne(e)){if(i(e.value))return hex(e.value);if(n(e.value)){const a=namedColor(e.value[4].value);return!1!==a?a:"transparent"===toLowerCaseAZ(e.value[4].value)&&{colorNotation:he.RGB,channels:[0,0,0],alpha:0,syntaxFlags:new Set([me.ColorKeyword])}}}return!1}export{he as ColorNotation,me as SyntaxFlag,color,colorDataFitsDisplayP3_Gamut,colorDataFitsRGB_Gamut,serializeHSL,serializeOKLCH,serializeP3,serializeRGB}; diff --git a/packages/css-color-parser/src/functions/color-mix.ts b/packages/css-color-parser/src/functions/color-mix.ts index e54ef676eb..f91fc957a6 100644 --- a/packages/css-color-parser/src/functions/color-mix.ts +++ b/packages/css-color-parser/src/functions/color-mix.ts @@ -125,19 +125,12 @@ export function colorMix(colorMixNode: FunctionNode, colorParser: ColorParser): type ColorMixEntry = { color: ColorData, - percentage: number + percentage: number | false }; -type ColorMixItems = { - colors: Array, - alphaMultiplier: number, -}; - -function colorMixComponents(componentValues: Array, colorParser: ColorParser): ColorMixItems | false { +function colorMixComponents(componentValues: Array, colorParser: ColorParser): Array | false { const colors: Array<{ color: ColorData, percentage: number | false }> = []; - let alphaMultiplier = 1; - let color: ColorData | false = false; let percentage: number | false = false; @@ -219,83 +212,30 @@ function colorMixComponents(componentValues: Array, colorParser: return false; } - let pSum = 0; - let pOmitted = 0; - for (let i = 0; i < colors.length; i++) { - const p = colors[i].percentage; - if (p === false) { - pOmitted++; - continue; - } - - if (p < 0 || p > 100) { - return false; - } - - pSum += p; - } - - const pRemainder = Math.max(0, 100 - pSum); - - pSum = 0; - for (let i = 0; i < colors.length; i++) { - if (colors[i].percentage === false) { - colors[i].percentage = pRemainder / pOmitted; - } - - pSum += colors[i].percentage as number; - } + return colors; +} - if (pSum === 0) { // The sum of explicitly provided mix percentages is `0` - return { - colors: [ - { - color: { - channels: [0, 0, 0], - colorNotation: ColorNotation.sRGB, - alpha: 0, - syntaxFlags: new Set(), - }, - percentage: 0 - } - ], - alphaMultiplier: 0, - }; +function colorMixRectangular(colorSpace: string, items: Array | false): ColorData | false { + if (!items || !items.length) { + return false; } - if (pSum > 100) { - for (let i = 0; i < colors.length; i++) { - let p = colors[i].percentage as number; // already handled all `false` cases - - p = (p / pSum) * 100; - colors[i].percentage = p; + for (const item of items) { + if (!item.percentage) { + continue; } - } - - if (pSum < 100) { - alphaMultiplier = pSum / 100; - - for (let i = 0; i < colors.length; i++) { - let p = colors[i].percentage as number; // already handled all `false` cases - p = (p / pSum) * 100; - colors[i].percentage = p; + if (item.percentage < 0 || item.percentage > 100) { + return false; } } - return { - colors: colors as ColorMixItems['colors'], // already handled all percentage `false` cases - alphaMultiplier: alphaMultiplier, - }; -} - -function colorMixRectangular(colorSpace: string, items: ColorMixItems | false): ColorData | false { - if (!items || !items.colors.length) { - return false; - } + // https://drafts.csswg.org/css-color-5/#color-mix + // 1. Normalize mix percentages from the list of mix items passed to the function, with the "forced normalization" flag set to true, letting items and leftover be the result. + const { items: normalizedItems, leftover } = normalizeMixPercentages(items, true); - const colors = items.colors.slice(); - colors.reverse(); + // 2. Let alpha mult be 1 - leftover, interpreting leftover as a number between 0 and 1. + const alphaMultiplier = 1 - (leftover / 100); let outputColorNotation: ColorNotation; switch (colorSpace) { @@ -337,13 +277,15 @@ function colorMixRectangular(colorSpace: string, items: ColorMixItems | false): return false; } - if (colors.length === 1) { - const color = colorDataTo(colors[0].color, outputColorNotation); + // 3. If items is length 1, set color to the color of that sole item, converted to the specified interpolation . + if (normalizedItems.length === 1) { + const color = colorDataTo(normalizedItems[0].color, outputColorNotation); color.colorNotation = outputColorNotation; color.syntaxFlags.add(SyntaxFlag.ColorMixVariadic); if (typeof color.alpha === 'number') { - color.alpha = color.alpha * items.alphaMultiplier; + // 4. Multiply the alpha component of color by alpha mult. + color.alpha = color.alpha * alphaMultiplier; } else { return false; } @@ -351,51 +293,68 @@ function colorMixRectangular(colorSpace: string, items: ColorMixItems | false): return color; } - while (colors.length >= 2) { - // Pop from item stack twice, letting a and b be the two results in order. - const a_color = colors.pop(); - const b_color = colors.pop(); + // 3.1. Let item stack be a stack made by reversing items. (Thus, with the first item at the top of the stack.) + normalizedItems.reverse(); + // 3.2. While item stack has length 2 or greater: + while (normalizedItems.length >= 2) { + // 3.2.1. Pop from item stack twice, letting a and b be the two results in order. + const a_color = normalizedItems.pop(); + const b_color = normalizedItems.pop(); if (!a_color || !b_color) { return false; } - const mixed_color = colorMixRectangularPair(outputColorNotation, a_color.color, a_color.percentage, b_color.color, b_color.percentage); + // 3.2.1. Let combined percentage be the sum of a and b’s percentages. + const combined_percentage = a_color.percentage + b_color.percentage; + + // 3.2.2. with a progress percentage equal to (b’s percentage) / combined percentage), if combined percentage is greater than 0, and 0.5 otherwise + const progress = combined_percentage > 0 ? b_color.percentage / combined_percentage : 0.5; + + // 3.2.2. Interpolate a and b’s colors as described in CSS Color 4 § 13. Color Interpolation, + // with a progress percentage equal to (b’s percentage) / combined percentage), + // if combined percentage is greater than 0, and 0.5 otherwise. + // If the specified color space is a cylindrical polar color space, + // then the controls the interpolation of hue, + // as described in CSS Color 4 § 13.4 Hue Interpolation. + // If no is specified, assume shorter. + const mixed_color = colorMixRectangularPair(outputColorNotation, a_color.color, b_color.color, progress); if (!mixed_color) { return false; } - colors.push({ + // 3.2.3. Create a new mix item with the resulting color and a percentage of combined percentage, and push it onto item stack. + normalizedItems.push({ color: mixed_color, - percentage: a_color.percentage + b_color.percentage + percentage: combined_percentage }); } - const colorData = colors[0]?.color; + // 3.3. Set color to the color of the sole remaining item in item stack. + const colorData = normalizedItems[0]?.color; if (!colorData) { return false; } - if (items.colors.some((x) => x.color.syntaxFlags.has(SyntaxFlag.Experimental))) { - colorData.syntaxFlags.add(SyntaxFlag.Experimental); - } - + // 4. Multiply the alpha component of color by alpha mult. if (typeof colorData.alpha === 'number') { - colorData.alpha = colorData.alpha * items.alphaMultiplier; + colorData.alpha = colorData.alpha * alphaMultiplier; } else { return false; } - if (items.colors.length !== 2) { + if (items.some((x) => x.color.syntaxFlags.has(SyntaxFlag.Experimental))) { + colorData.syntaxFlags.add(SyntaxFlag.Experimental); + } + + if (items.length !== 2) { colorData.syntaxFlags.add(SyntaxFlag.ColorMixVariadic); } return colorData; } -function colorMixRectangularPair(colorNotation: ColorNotation, a_color: ColorData, a_percentage: number, b_color: ColorData, b_percentage: number): ColorData | false { - const ratio = a_percentage / (a_percentage + b_percentage); - +function colorMixRectangularPair(colorNotation: ColorNotation, a_color: ColorData, b_color: ColorData, progress: number): ColorData | false { let a_alpha = a_color.alpha; if (typeof a_alpha !== 'number') { return false; @@ -429,11 +388,11 @@ function colorMixRectangularPair(colorNotation: ColorNotation, a_color: ColorDat b_channels[1] = premultiply(b_channels[1], b_alpha); b_channels[2] = premultiply(b_channels[2], b_alpha); - const alpha = interpolate(a_alpha, b_alpha, ratio); + const alpha = interpolate(a_alpha, b_alpha, progress); const outputChannels: Color = [ - un_premultiply(interpolate(a_channels[0], b_channels[0], ratio), alpha), - un_premultiply(interpolate(a_channels[1], b_channels[1], ratio), alpha), - un_premultiply(interpolate(a_channels[2], b_channels[2], ratio), alpha), + un_premultiply(interpolate(a_channels[0], b_channels[0], progress), alpha), + un_premultiply(interpolate(a_channels[1], b_channels[1], progress), alpha), + un_premultiply(interpolate(a_channels[2], b_channels[2], progress), alpha), ]; const colorData: ColorData = { @@ -446,13 +405,27 @@ function colorMixRectangularPair(colorNotation: ColorNotation, a_color: ColorDat return colorData; } -function colorMixPolar(colorSpace: string, hueInterpolationMethod: string, items: ColorMixItems | false): ColorData | false { - if (!items || !items.colors.length) { +function colorMixPolar(colorSpace: string, hueInterpolationMethod: string, items: Array | false): ColorData | false { + if (!items || !items.length) { return false; } - const colors = items.colors.slice(); - colors.reverse(); + for (const item of items) { + if (!item.percentage) { + continue; + } + + if (item.percentage < 0 || item.percentage > 100) { + return false; + } + } + + // https://drafts.csswg.org/css-color-5/#color-mix + // 1. Normalize mix percentages from the list of mix items passed to the function, with the "forced normalization" flag set to true, letting items and leftover be the result. + const { items: normalizedItems, leftover } = normalizeMixPercentages(items, true); + + // 2. Let alpha mult be 1 - leftover, interpreting leftover as a number between 0 and 1. + const alphaMultiplier = 1 - (leftover / 100); let outputColorNotation: ColorNotation; switch (colorSpace) { @@ -472,13 +445,15 @@ function colorMixPolar(colorSpace: string, hueInterpolationMethod: string, items return false; } - if (colors.length === 1) { - const color = colorDataTo(colors[0].color, outputColorNotation); + // 3. If items is length 1, set color to the color of that sole item, converted to the specified interpolation . + if (normalizedItems.length === 1) { + const color = colorDataTo(normalizedItems[0].color, outputColorNotation); color.colorNotation = outputColorNotation; color.syntaxFlags.add(SyntaxFlag.ColorMixVariadic); if (typeof color.alpha === 'number') { - color.alpha = color.alpha * items.alphaMultiplier; + // 4. Multiply the alpha component of color by alpha mult. + color.alpha = color.alpha * alphaMultiplier; } else { return false; } @@ -486,51 +461,68 @@ function colorMixPolar(colorSpace: string, hueInterpolationMethod: string, items return color; } - while (colors.length >= 2) { - // Pop from item stack twice, letting a and b be the two results in order. - const a_color = colors.pop(); - const b_color = colors.pop(); + // 3.1. Let item stack be a stack made by reversing items. (Thus, with the first item at the top of the stack.) + normalizedItems.reverse(); + // 3.2. While item stack has length 2 or greater: + while (normalizedItems.length >= 2) { + // 3.2.1. Pop from item stack twice, letting a and b be the two results in order. + const a_color = normalizedItems.pop(); + const b_color = normalizedItems.pop(); if (!a_color || !b_color) { return false; } - const mixed_color = colorMixPolarPair(outputColorNotation, hueInterpolationMethod, a_color.color, a_color.percentage, b_color.color, b_color.percentage); + // 3.2.1. Let combined percentage be the sum of a and b’s percentages. + const combined_percentage = a_color.percentage + b_color.percentage; + + // 3.2.2. with a progress percentage equal to (b’s percentage) / combined percentage), if combined percentage is greater than 0, and 0.5 otherwise + const progress = combined_percentage > 0 ? b_color.percentage / combined_percentage : 0.5; + + // 3.2.2. Interpolate a and b’s colors as described in CSS Color 4 § 13. Color Interpolation, + // with a progress percentage equal to (b’s percentage) / combined percentage), + // if combined percentage is greater than 0, and 0.5 otherwise. + // If the specified color space is a cylindrical polar color space, + // then the controls the interpolation of hue, + // as described in CSS Color 4 § 13.4 Hue Interpolation. + // If no is specified, assume shorter. + const mixed_color = colorMixPolarPair(outputColorNotation, hueInterpolationMethod, a_color.color, b_color.color, progress); if (!mixed_color) { return false; } - colors.push({ + // 3.2.3. Create a new mix item with the resulting color and a percentage of combined percentage, and push it onto item stack. + normalizedItems.push({ color: mixed_color, - percentage: a_color.percentage + b_color.percentage + percentage: combined_percentage }); } - const colorData = colors[0]?.color; + // 3.3. Set color to the color of the sole remaining item in item stack. + const colorData = normalizedItems[0]?.color; if (!colorData) { return false; } - if (items.colors.some((x) => x.color.syntaxFlags.has(SyntaxFlag.Experimental))) { - colorData.syntaxFlags.add(SyntaxFlag.Experimental); - } - + // 4. Multiply the alpha component of color by alpha mult. if (typeof colorData.alpha === 'number') { - colorData.alpha = colorData.alpha * items.alphaMultiplier; + colorData.alpha = colorData.alpha * alphaMultiplier; } else { return false; } - if (items.colors.length !== 2) { + if (items.some((x) => x.color.syntaxFlags.has(SyntaxFlag.Experimental))) { + colorData.syntaxFlags.add(SyntaxFlag.Experimental); + } + + if (items.length !== 2) { colorData.syntaxFlags.add(SyntaxFlag.ColorMixVariadic); } return colorData; } -function colorMixPolarPair(colorNotation: ColorNotation, hueInterpolationMethod: string, a_color: ColorData, a_percentage: number, b_color: ColorData, b_percentage: number): ColorData | false { - const ratio = a_percentage / (a_percentage + b_percentage); - +function colorMixPolarPair(colorNotation: ColorNotation, hueInterpolationMethod: string, a_color: ColorData, b_color: ColorData, progress: number): ColorData | false { let a_hue = 0; let b_hue = 0; @@ -644,24 +636,24 @@ function colorMixPolarPair(colorNotation: ColorNotation, hueInterpolationMethod: b_second = premultiply(b_second, b_alpha); let outputChannels: Color = [0, 0, 0]; - const alpha = interpolate(a_alpha, b_alpha, ratio); + const alpha = interpolate(a_alpha, b_alpha, progress); switch (colorNotation) { case ColorNotation.HSL: case ColorNotation.HWB: outputChannels = [ - interpolate(a_hue, b_hue, ratio), - un_premultiply(interpolate(a_first, b_first, ratio), alpha), - un_premultiply(interpolate(a_second, b_second, ratio), alpha), + interpolate(a_hue, b_hue, progress), + un_premultiply(interpolate(a_first, b_first, progress), alpha), + un_premultiply(interpolate(a_second, b_second, progress), alpha), ]; break; case ColorNotation.LCH: case ColorNotation.OKLCH: outputChannels = [ - un_premultiply(interpolate(a_first, b_first, ratio), alpha), - un_premultiply(interpolate(a_second, b_second, ratio), alpha), - interpolate(a_hue, b_hue, ratio), + un_premultiply(interpolate(a_first, b_first, progress), alpha), + un_premultiply(interpolate(a_second, b_second, progress), alpha), + interpolate(a_hue, b_hue, progress), ]; break; @@ -688,7 +680,7 @@ function fillInMissingComponent(a: number, b: number): number { } function interpolate(start: number, end: number, p: number): number { - return (start * p) + end * (1 - p); + return (start * (1 - p)) + end * p; } function premultiply(x: number, alpha: number): number { @@ -718,3 +710,53 @@ function un_premultiply(x: number, alpha: number): number { return x / alpha; } + +// https://drafts.csswg.org/css-values-5/#normalize-mix-percentages +function normalizeMixPercentages(mix_items: Array<{ color: T, percentage: number | false }>, force_normalization: boolean = false): { items: Array<{ color: T, percentage: number }>, leftover: number } { + // 1. Let specified sum be the sum of the percentages specified in items (clamped to 100%), or 0% if the percentages are omitted for all items. + let specified_sum = 0; + let number_of_omitted_percentages = 0; + for (const item of mix_items) { + if (item.percentage) { + specified_sum += item.percentage; + } + + if (item.percentage === false) { + number_of_omitted_percentages++; + } + } + + specified_sum = Math.min(100, specified_sum); + + // 2. For each omitted percentage in items, set it to (100% - specified sum) / (number of omitted percentages). + for (const item of mix_items) { + if (item.percentage === false) { + item.percentage = (100 - specified_sum) / (number_of_omitted_percentages); + } + } + + const mix_items_with_percentages = (mix_items as Array<{ color: T, percentage: number }>).slice(); + + // 3. Let total be the sum of the percentages of all the items. + let total = 0; + for (const item of mix_items_with_percentages) { + total += item.percentage; + } + + // 4. If total is greater than 100%, or if total is greater than 0% and the force normalization flag is true, multiply every percentage in items by (100% / total). + if (total > 100 || (total > 0 && force_normalization)) { + for (const item of mix_items_with_percentages) { + item.percentage = item.percentage * (100 / total); + } + } + + let leftover = 0; + if (total < 100) { + leftover = 100 - total; + } + + return { + items: mix_items_with_percentages, + leftover: leftover, + }; +} diff --git a/packages/css-color-parser/test/basic/color-mix-function.mjs b/packages/css-color-parser/test/basic/color-mix-function.mjs index 01bddb6244..51776b594e 100644 --- a/packages/css-color-parser/test/basic/color-mix-function.mjs +++ b/packages/css-color-parser/test/basic/color-mix-function.mjs @@ -104,9 +104,9 @@ const tests = [ ['color-mix(in hsl, hsl(calc(none) 30% 40%), hsl(20deg 30% 40%))', ''], // 0 percentage sum - ['color-mix(in hsl, hsl(120deg 10% 20%) 0%, hsl(30deg 30% 40%) 0%)', 'rgba(0, 0, 0, 0)'], // sum of percentages is 0 - ['color-mix(in hwb, hwb(40deg 30% 40%) 0%, hwb(40deg 30% 40%) 0%)', canonicalize('hwb(0 0 100% / 0)')], // sum of percentages is 0 - ['color-mix(in lch, lch(10 20 30deg) 0%, lch(10 20 30deg) 0%)', canonicalize('lch(0 0 0 / 0)')], // sum of percentages is 0 + ['color-mix(in hsl, hsl(120deg 10% 20%) 0%, hsl(30deg 30% 40%) 0%)', canonicalize('hsl(75.48deg 20.26% 30% / 0)')], // sum of percentages is 0 + ['color-mix(in hwb, hwb(40deg 30% 40%) 0%, hwb(40deg 30% 40%) 0%)', canonicalize('hwb(40.26deg 30.2% 40% / 0%)')], // sum of percentages is 0 + ['color-mix(in lch, lch(10 20 30deg) 0%, lch(10 20 30deg) 0%)', canonicalize('lch(10 19.96 29.26 / 0)')], // sum of percentages is 0 ['color-mix(in hsl, hsl(120deg 10% 20%))', canonicalize('hsl(120deg 10% 20%)')], // single arg ['color-mix(in hsl, hsl(120deg 10% 20%) 100%)', canonicalize('hsl(120deg 10% 20%)')], // single arg @@ -144,6 +144,9 @@ const tests = [ // non-unity alpha ['color-mix(in srgb, rgb(100% 0% 0% / 0.7) 25%, rgb(0% 100% 0% / 0.2))', canonicalize('color(srgb 0.53846 0.46154 0 / 0.325)')], ['color-mix(in srgb, rgb(100% 0% 0% / 0.7) 20%, rgb(0% 100% 0% / 0.2) 60%)', canonicalize('color(srgb 0.53846 0.46154 0 / 0.260)')], + + ['color-mix(in srgb, red 0%, blue 0%, green 100%)', 'rgb(0, 128, 0)'], + ['color-mix(in srgb, green 100%, blue 0%, red 0%)', 'rgb(0, 128, 0)'], ]; for (const test of tests) { @@ -153,3 +156,34 @@ for (const test of tests) { `"${test[0]}" : ${test[1]}`, ); } + + +assert.deepStrictEqual( + color(parse('color-mix(in hsl, red 0%, blue 0%)')), + { + colorNotation: 'hsl', + channels: [300.00000000000006, 100, 49.99999999999999], + alpha: 0, + syntaxFlags: new Set(['color-mix']), + }, +); + +assert.deepStrictEqual( + color(parse('color-mix(in hsl, red 0%)')), + { + colorNotation: 'hsl', + channels: [9.186375618530642e-14, 100, 49.99999999999999], + alpha: 0, + syntaxFlags: new Set(['color-keyword', 'named-color', 'color-mix-variadic']), + }, +); + +assert.deepStrictEqual( + color(parse('color-mix(in hsl, red 0%, red 0%, red 0%)')), + { + colorNotation: 'hsl', + channels: [9.186375618530642e-14, 100, 49.99999999999999], + alpha: 0, + syntaxFlags: new Set(['color-mix', 'color-mix-variadic']), + }, +); diff --git a/plugins/postcss-color-mix-function/test/basic.expect.css b/plugins/postcss-color-mix-function/test/basic.expect.css index c55809a3e8..d0e9dd1ee9 100644 --- a/plugins/postcss-color-mix-function/test/basic.expect.css +++ b/plugins/postcss-color-mix-function/test/basic.expect.css @@ -175,7 +175,7 @@ } .ignore-zero-percentage { - color: color-mix(in srgb, blue 0%, yellow 0%); + color: rgba(128, 128, 128, 0); } .display-p3-linear { diff --git a/plugins/postcss-color-mix-function/test/basic.preserve-true.expect.css b/plugins/postcss-color-mix-function/test/basic.preserve-true.expect.css index 0664938687..4e7751a7ee 100644 --- a/plugins/postcss-color-mix-function/test/basic.preserve-true.expect.css +++ b/plugins/postcss-color-mix-function/test/basic.preserve-true.expect.css @@ -250,6 +250,7 @@ } .ignore-zero-percentage { + color: rgba(128, 128, 128, 0); color: color-mix(in srgb, blue 0%, yellow 0%); } diff --git a/plugins/postcss-color-mix-function/test/basic.with-cloned-rules.expect.css b/plugins/postcss-color-mix-function/test/basic.with-cloned-rules.expect.css index 0664938687..4e7751a7ee 100644 --- a/plugins/postcss-color-mix-function/test/basic.with-cloned-rules.expect.css +++ b/plugins/postcss-color-mix-function/test/basic.with-cloned-rules.expect.css @@ -250,6 +250,7 @@ } .ignore-zero-percentage { + color: rgba(128, 128, 128, 0); color: color-mix(in srgb, blue 0%, yellow 0%); } diff --git a/plugins/postcss-color-mix-variadic-function-arguments/test/basic.expect.css b/plugins/postcss-color-mix-variadic-function-arguments/test/basic.expect.css index 07539747b6..29a6137cd6 100644 --- a/plugins/postcss-color-mix-variadic-function-arguments/test/basic.expect.css +++ b/plugins/postcss-color-mix-variadic-function-arguments/test/basic.expect.css @@ -21,5 +21,5 @@ } .zero-percentage { - color: rgba(0, 0, 0, 0); + color: color-mix(in srgb, blue 0%, yellow 0%); } diff --git a/plugins/postcss-color-mix-variadic-function-arguments/test/basic.preserve-true.expect.css b/plugins/postcss-color-mix-variadic-function-arguments/test/basic.preserve-true.expect.css index f9642f1195..8053aea300 100644 --- a/plugins/postcss-color-mix-variadic-function-arguments/test/basic.preserve-true.expect.css +++ b/plugins/postcss-color-mix-variadic-function-arguments/test/basic.preserve-true.expect.css @@ -26,6 +26,5 @@ } .zero-percentage { - color: rgba(0, 0, 0, 0); color: color-mix(in srgb, blue 0%, yellow 0%); } diff --git a/plugins/postcss-color-mix-variadic-function-arguments/test/basic.with-cloned-rules.expect.css b/plugins/postcss-color-mix-variadic-function-arguments/test/basic.with-cloned-rules.expect.css index f9642f1195..8053aea300 100644 --- a/plugins/postcss-color-mix-variadic-function-arguments/test/basic.with-cloned-rules.expect.css +++ b/plugins/postcss-color-mix-variadic-function-arguments/test/basic.with-cloned-rules.expect.css @@ -26,6 +26,5 @@ } .zero-percentage { - color: rgba(0, 0, 0, 0); color: color-mix(in srgb, blue 0%, yellow 0%); }