From 0b65485c484c664784b17a4fcd9554b429204942 Mon Sep 17 00:00:00 2001 From: Romain Menke Date: Mon, 26 May 2025 17:18:15 +0200 Subject: [PATCH 1/5] color-mix-variadic-function-arguments --- packages/css-color-parser/dist/index.cjs | 2 +- packages/css-color-parser/dist/index.d.ts | 2 + packages/css-color-parser/dist/index.mjs | 2 +- .../docs/css-color-parser.api.json | 21 ++ packages/css-color-parser/src/color-data.ts | 2 + .../src/functions/color-mix.ts | 357 +++++++++++------- .../test/basic/color-mix-function.mjs | 25 ++ .../wpt/color-invalid-color-mix-function.mjs | 10 - .../postcss-color-mix-function/dist/index.cjs | 2 +- .../postcss-color-mix-function/dist/index.mjs | 2 +- .../postcss-color-mix-function/src/index.ts | 4 + .../postcss-color-mix-function/test/_tape.mjs | 12 + .../postcss-color-mix-function/test/basic.css | 4 + .../test/basic.expect.css | 4 + .../test/basic.preserve-true.expect.css | 4 + .../test/basic.with-cloned-rules.expect.css | 4 + .../test/variadic.css | 11 + .../test/variadic.expect.css | 11 + .../.gitignore | 6 + .../.nvmrc | 1 + .../CHANGELOG.md | 5 + .../INSTALL.md | 235 ++++++++++++ .../LICENSE.md | 18 + .../README.md | 104 +++++ .../api-extractor.json | 4 + .../dist/index.cjs | 1 + .../dist/index.d.ts | 19 + .../dist/index.mjs | 1 + .../docs/README.md | 57 +++ .../package.json | 102 +++++ .../src/index.ts | 151 ++++++++ .../test/_import.mjs | 6 + .../test/_require.cjs | 6 + .../test/_tape.mjs | 41 ++ .../test/basic.css | 15 + .../test/basic.expect.css | 15 + .../test/basic.preserve-true.expect.css | 19 + .../test/basic.with-cloned-rules.expect.css | 19 + .../test/examples/example.css | 7 + .../test/examples/example.expect.css | 7 + .../examples/example.preserve-true.expect.css | 9 + .../test/variables.css | 7 + .../test/variables.expect.css | 7 + .../test/variables.preserve-true.expect.css | 9 + .../tsconfig.json | 10 + 45 files changed, 1220 insertions(+), 140 deletions(-) create mode 100644 plugins/postcss-color-mix-function/test/variadic.css create mode 100644 plugins/postcss-color-mix-function/test/variadic.expect.css create mode 100644 plugins/postcss-color-mix-variadic-function-arguments/.gitignore create mode 100644 plugins/postcss-color-mix-variadic-function-arguments/.nvmrc create mode 100644 plugins/postcss-color-mix-variadic-function-arguments/CHANGELOG.md create mode 100644 plugins/postcss-color-mix-variadic-function-arguments/INSTALL.md create mode 100644 plugins/postcss-color-mix-variadic-function-arguments/LICENSE.md create mode 100644 plugins/postcss-color-mix-variadic-function-arguments/README.md create mode 100644 plugins/postcss-color-mix-variadic-function-arguments/api-extractor.json create mode 100644 plugins/postcss-color-mix-variadic-function-arguments/dist/index.cjs create mode 100644 plugins/postcss-color-mix-variadic-function-arguments/dist/index.d.ts create mode 100644 plugins/postcss-color-mix-variadic-function-arguments/dist/index.mjs create mode 100644 plugins/postcss-color-mix-variadic-function-arguments/docs/README.md create mode 100644 plugins/postcss-color-mix-variadic-function-arguments/package.json create mode 100644 plugins/postcss-color-mix-variadic-function-arguments/src/index.ts create mode 100644 plugins/postcss-color-mix-variadic-function-arguments/test/_import.mjs create mode 100644 plugins/postcss-color-mix-variadic-function-arguments/test/_require.cjs create mode 100644 plugins/postcss-color-mix-variadic-function-arguments/test/_tape.mjs create mode 100644 plugins/postcss-color-mix-variadic-function-arguments/test/basic.css create mode 100644 plugins/postcss-color-mix-variadic-function-arguments/test/basic.expect.css create mode 100644 plugins/postcss-color-mix-variadic-function-arguments/test/basic.preserve-true.expect.css create mode 100644 plugins/postcss-color-mix-variadic-function-arguments/test/basic.with-cloned-rules.expect.css create mode 100644 plugins/postcss-color-mix-variadic-function-arguments/test/examples/example.css create mode 100644 plugins/postcss-color-mix-variadic-function-arguments/test/examples/example.expect.css create mode 100644 plugins/postcss-color-mix-variadic-function-arguments/test/examples/example.preserve-true.expect.css create mode 100644 plugins/postcss-color-mix-variadic-function-arguments/test/variables.css create mode 100644 plugins/postcss-color-mix-variadic-function-arguments/test/variables.expect.css create mode 100644 plugins/postcss-color-mix-variadic-function-arguments/test/variables.preserve-true.expect.css create mode 100644 plugins/postcss-color-mix-variadic-function-arguments/tsconfig.json diff --git a/packages/css-color-parser/dist/index.cjs b/packages/css-color-parser/dist/index.cjs index d528785a85..6e11238e81 100644 --- a/packages/css-color-parser/dist/index.cjs +++ b/packages/css-color-parser/dist/index.cjs @@ -1 +1 @@ -"use strict";var e,o,a=require("@csstools/css-tokenizer"),n=require("@csstools/color-helpers"),t=require("@csstools/css-parser-algorithms"),r=require("@csstools/css-calc");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 exports.ColorNotation.HEX:case exports.ColorNotation.RGB:case exports.ColorNotation.sRGB:return{...e,colorNotation:exports.ColorNotation.XYZ_D50,channels:n.sRGB_to_XYZ_D50(convertNaNToZero(e.channels))};case exports.ColorNotation.Linear_sRGB:return{...e,colorNotation:exports.ColorNotation.XYZ_D50,channels:n.lin_sRGB_to_XYZ_D50(convertNaNToZero(e.channels))};case exports.ColorNotation.Display_P3:return{...e,colorNotation:exports.ColorNotation.XYZ_D50,channels:n.P3_to_XYZ_D50(convertNaNToZero(e.channels))};case exports.ColorNotation.Rec2020:return{...e,colorNotation:exports.ColorNotation.XYZ_D50,channels:n.rec_2020_to_XYZ_D50(convertNaNToZero(e.channels))};case exports.ColorNotation.A98_RGB:return{...e,colorNotation:exports.ColorNotation.XYZ_D50,channels:n.a98_RGB_to_XYZ_D50(convertNaNToZero(e.channels))};case exports.ColorNotation.ProPhoto_RGB:return{...e,colorNotation:exports.ColorNotation.XYZ_D50,channels:n.ProPhoto_RGB_to_XYZ_D50(convertNaNToZero(e.channels))};case exports.ColorNotation.HSL:return{...e,colorNotation:exports.ColorNotation.XYZ_D50,channels:n.HSL_to_XYZ_D50(convertNaNToZero(e.channels))};case exports.ColorNotation.HWB:return{...e,colorNotation:exports.ColorNotation.XYZ_D50,channels:n.HWB_to_XYZ_D50(convertNaNToZero(e.channels))};case exports.ColorNotation.Lab:return{...e,colorNotation:exports.ColorNotation.XYZ_D50,channels:n.Lab_to_XYZ_D50(convertNaNToZero(e.channels))};case exports.ColorNotation.OKLab:return{...e,colorNotation:exports.ColorNotation.XYZ_D50,channels:n.OKLab_to_XYZ_D50(convertNaNToZero(e.channels))};case exports.ColorNotation.LCH:return{...e,colorNotation:exports.ColorNotation.XYZ_D50,channels:n.LCH_to_XYZ_D50(convertNaNToZero(e.channels))};case exports.ColorNotation.OKLCH:return{...e,colorNotation:exports.ColorNotation.XYZ_D50,channels:n.OKLCH_to_XYZ_D50(convertNaNToZero(e.channels))};case exports.ColorNotation.XYZ_D50:return{...e,colorNotation:exports.ColorNotation.XYZ_D50,channels:n.XYZ_D50_to_XYZ_D50(convertNaNToZero(e.channels))};case exports.ColorNotation.XYZ_D65:return{...e,colorNotation:exports.ColorNotation.XYZ_D50,channels:n.XYZ_D65_to_XYZ_D50(convertNaNToZero(e.channels))};default:throw new Error("Unsupported color notation")}}exports.ColorNotation=void 0,(e=exports.ColorNotation||(exports.ColorNotation={})).A98_RGB="a98-rgb",e.Display_P3="display-p3",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",exports.SyntaxFlag=void 0,(o=exports.SyntaxFlag||(exports.SyntaxFlag={})).ColorKeyword="color-keyword",o.HasAlpha="has-alpha",o.HasDimensionValues="has-dimension-values",o.HasNoneKeywords="has-none-keywords",o.HasNumberValues="has-number-values",o.HasPercentageAlpha="has-percentage-alpha",o.HasPercentageValues="has-percentage-values",o.HasVariableAlpha="has-variable-alpha",o.Hex="hex",o.LegacyHSL="legacy-hsl",o.LegacyRGB="legacy-rgb",o.NamedColor="named-color",o.RelativeColorSyntax="relative-color-syntax",o.ColorMix="color-mix",o.ContrastColor="contrast-color",o.Experimental="experimental";const s=new Set([exports.ColorNotation.A98_RGB,exports.ColorNotation.Display_P3,exports.ColorNotation.HEX,exports.ColorNotation.Linear_sRGB,exports.ColorNotation.ProPhoto_RGB,exports.ColorNotation.RGB,exports.ColorNotation.sRGB,exports.ColorNotation.Rec2020,exports.ColorNotation.XYZ_D50,exports.ColorNotation.XYZ_D65]);function colorDataTo(e,o){const a={...e};if(e.colorNotation!==o){const e=colorData_to_XYZ_D50(a);switch(o){case exports.ColorNotation.HEX:case exports.ColorNotation.RGB:a.colorNotation=exports.ColorNotation.RGB,a.channels=n.XYZ_D50_to_sRGB(e.channels);break;case exports.ColorNotation.sRGB:a.colorNotation=exports.ColorNotation.sRGB,a.channels=n.XYZ_D50_to_sRGB(e.channels);break;case exports.ColorNotation.Linear_sRGB:a.colorNotation=exports.ColorNotation.Linear_sRGB,a.channels=n.XYZ_D50_to_lin_sRGB(e.channels);break;case exports.ColorNotation.Display_P3:a.colorNotation=exports.ColorNotation.Display_P3,a.channels=n.XYZ_D50_to_P3(e.channels);break;case exports.ColorNotation.Rec2020:a.colorNotation=exports.ColorNotation.Rec2020,a.channels=n.XYZ_D50_to_rec_2020(e.channels);break;case exports.ColorNotation.ProPhoto_RGB:a.colorNotation=exports.ColorNotation.ProPhoto_RGB,a.channels=n.XYZ_D50_to_ProPhoto(e.channels);break;case exports.ColorNotation.A98_RGB:a.colorNotation=exports.ColorNotation.A98_RGB,a.channels=n.XYZ_D50_to_a98_RGB(e.channels);break;case exports.ColorNotation.HSL:a.colorNotation=exports.ColorNotation.HSL,a.channels=n.XYZ_D50_to_HSL(e.channels);break;case exports.ColorNotation.HWB:a.colorNotation=exports.ColorNotation.HWB,a.channels=n.XYZ_D50_to_HWB(e.channels);break;case exports.ColorNotation.Lab:a.colorNotation=exports.ColorNotation.Lab,a.channels=n.XYZ_D50_to_Lab(e.channels);break;case exports.ColorNotation.LCH:a.colorNotation=exports.ColorNotation.LCH,a.channels=n.XYZ_D50_to_LCH(e.channels);break;case exports.ColorNotation.OKLCH:a.colorNotation=exports.ColorNotation.OKLCH,a.channels=n.XYZ_D50_to_OKLCH(e.channels);break;case exports.ColorNotation.OKLab:a.colorNotation=exports.ColorNotation.OKLab,a.channels=n.XYZ_D50_to_OKLab(e.channels);break;case exports.ColorNotation.XYZ_D50:a.colorNotation=exports.ColorNotation.XYZ_D50,a.channels=n.XYZ_D50_to_XYZ_D50(e.channels);break;case exports.ColorNotation.XYZ_D65:a.colorNotation=exports.ColorNotation.XYZ_D65,a.channels=n.XYZ_D50_to_XYZ_D65(e.channels);break;default:throw new Error("Unsupported color notation")}}else a.channels=convertNaNToZero(e.channels);if(o===e.colorNotation)a.channels=carryForwardMissingComponents(e.channels,[0,1,2],a.channels,[0,1,2]);else if(s.has(o)&&s.has(e.colorNotation))a.channels=carryForwardMissingComponents(e.channels,[0,1,2],a.channels,[0,1,2]);else switch(o){case exports.ColorNotation.HSL:switch(e.colorNotation){case exports.ColorNotation.HWB:a.channels=carryForwardMissingComponents(e.channels,[0],a.channels,[0]);break;case exports.ColorNotation.Lab:case exports.ColorNotation.OKLab:a.channels=carryForwardMissingComponents(e.channels,[2],a.channels,[0]);break;case exports.ColorNotation.LCH:case exports.ColorNotation.OKLCH:a.channels=carryForwardMissingComponents(e.channels,[0,1,2],a.channels,[2,1,0])}break;case exports.ColorNotation.HWB:switch(e.colorNotation){case exports.ColorNotation.HSL:a.channels=carryForwardMissingComponents(e.channels,[0],a.channels,[0]);break;case exports.ColorNotation.LCH:case exports.ColorNotation.OKLCH:a.channels=carryForwardMissingComponents(e.channels,[0],a.channels,[2])}break;case exports.ColorNotation.Lab:case exports.ColorNotation.OKLab:switch(e.colorNotation){case exports.ColorNotation.HSL:a.channels=carryForwardMissingComponents(e.channels,[0],a.channels,[2]);break;case exports.ColorNotation.Lab:case exports.ColorNotation.OKLab:a.channels=carryForwardMissingComponents(e.channels,[0,1,2],a.channels,[0,1,2]);break;case exports.ColorNotation.LCH:case exports.ColorNotation.OKLCH:a.channels=carryForwardMissingComponents(e.channels,[0],a.channels,[0])}break;case exports.ColorNotation.LCH:case exports.ColorNotation.OKLCH:switch(e.colorNotation){case exports.ColorNotation.HSL:a.channels=carryForwardMissingComponents(e.channels,[0,1,2],a.channels,[2,1,0]);break;case exports.ColorNotation.HWB:a.channels=carryForwardMissingComponents(e.channels,[0],a.channels,[2]);break;case exports.ColorNotation.Lab:case exports.ColorNotation.OKLab:a.channels=carryForwardMissingComponents(e.channels,[0],a.channels,[0]);break;case exports.ColorNotation.LCH:case exports.ColorNotation.OKLCH:a.channels=carryForwardMissingComponents(e.channels,[0,1,2],a.channels,[0,1,2])}}return a.channels=convertPowerlessComponentsToMissingComponents(a.channels,o),a}function convertPowerlessComponentsToMissingComponents(e,o){const a=[...e];switch(o){case exports.ColorNotation.HSL:!Number.isNaN(a[1])&&reducePrecision(a[1],4)<=0&&(a[0]=Number.NaN);break;case exports.ColorNotation.HWB:Math.max(0,reducePrecision(a[1],4))+Math.max(0,reducePrecision(a[2],4))>=100&&(a[0]=Number.NaN);break;case exports.ColorNotation.LCH:!Number.isNaN(a[1])&&reducePrecision(a[1],4)<=0&&(a[2]=Number.NaN);break;case exports.ColorNotation.OKLCH:!Number.isNaN(a[1])&&reducePrecision(a[1],6)<=0&&(a[2]=Number.NaN)}return a}function convertPowerlessComponentsToZeroValuesForDisplay(e,o){const a=[...e];switch(o){case exports.ColorNotation.HSL:(reducePrecision(a[2])<=0||reducePrecision(a[2])>=100)&&(a[0]=Number.NaN,a[1]=Number.NaN),reducePrecision(a[1])<=0&&(a[0]=Number.NaN);break;case exports.ColorNotation.HWB:Math.max(0,reducePrecision(a[1]))+Math.max(0,reducePrecision(a[2]))>=100&&(a[0]=Number.NaN);break;case exports.ColorNotation.Lab:(reducePrecision(a[0])<=0||reducePrecision(a[0])>=100)&&(a[1]=Number.NaN,a[2]=Number.NaN);break;case exports.ColorNotation.LCH:reducePrecision(a[1])<=0&&(a[2]=Number.NaN),(reducePrecision(a[0])<=0||reducePrecision(a[0])>=100)&&(a[1]=Number.NaN,a[2]=Number.NaN);break;case exports.ColorNotation.OKLab:(reducePrecision(a[0])<=0||reducePrecision(a[0])>=1)&&(a[1]=Number.NaN,a[2]=Number.NaN);break;case exports.ColorNotation.OKLCH:reducePrecision(a[1])<=0&&(a[2]=Number.NaN),(reducePrecision(a[0])<=0||reducePrecision(a[0])>=1)&&(a[1]=Number.NaN,a[2]=Number.NaN)}return a}function carryForwardMissingComponents(e,o,a,n){const t=[...a];for(const a of o)Number.isNaN(e[o[a]])&&(t[n[a]]=Number.NaN);return t}function normalizeRelativeColorDataChannels(e){const o=new Map;switch(e.colorNotation){case exports.ColorNotation.RGB:case exports.ColorNotation.HEX:o.set("r",dummyNumberToken(255*e.channels[0])),o.set("g",dummyNumberToken(255*e.channels[1])),o.set("b",dummyNumberToken(255*e.channels[2])),"number"==typeof e.alpha&&o.set("alpha",dummyNumberToken(e.alpha));break;case exports.ColorNotation.HSL:o.set("h",dummyNumberToken(e.channels[0])),o.set("s",dummyNumberToken(e.channels[1])),o.set("l",dummyNumberToken(e.channels[2])),"number"==typeof e.alpha&&o.set("alpha",dummyNumberToken(e.alpha));break;case exports.ColorNotation.HWB:o.set("h",dummyNumberToken(e.channels[0])),o.set("w",dummyNumberToken(e.channels[1])),o.set("b",dummyNumberToken(e.channels[2])),"number"==typeof e.alpha&&o.set("alpha",dummyNumberToken(e.alpha));break;case exports.ColorNotation.Lab:case exports.ColorNotation.OKLab:o.set("l",dummyNumberToken(e.channels[0])),o.set("a",dummyNumberToken(e.channels[1])),o.set("b",dummyNumberToken(e.channels[2])),"number"==typeof e.alpha&&o.set("alpha",dummyNumberToken(e.alpha));break;case exports.ColorNotation.LCH:case exports.ColorNotation.OKLCH:o.set("l",dummyNumberToken(e.channels[0])),o.set("c",dummyNumberToken(e.channels[1])),o.set("h",dummyNumberToken(e.channels[2])),"number"==typeof e.alpha&&o.set("alpha",dummyNumberToken(e.alpha));break;case exports.ColorNotation.sRGB:case exports.ColorNotation.A98_RGB:case exports.ColorNotation.Display_P3:case exports.ColorNotation.Rec2020:case exports.ColorNotation.Linear_sRGB:case exports.ColorNotation.ProPhoto_RGB:o.set("r",dummyNumberToken(e.channels[0])),o.set("g",dummyNumberToken(e.channels[1])),o.set("b",dummyNumberToken(e.channels[2])),"number"==typeof e.alpha&&o.set("alpha",dummyNumberToken(e.alpha));break;case exports.ColorNotation.XYZ_D50:case exports.ColorNotation.XYZ_D65:o.set("x",dummyNumberToken(e.channels[0])),o.set("y",dummyNumberToken(e.channels[1])),o.set("z",dummyNumberToken(e.channels[2])),"number"==typeof e.alpha&&o.set("alpha",dummyNumberToken(e.alpha))}return o}function noneToZeroInRelativeColorDataChannels(e){const o=new Map(e);for(const[a,n]of e)Number.isNaN(n[4].value)&&o.set(a,dummyNumberToken(0));return o}function dummyNumberToken(e){return Number.isNaN(e)?[a.TokenType.Number,"none",-1,-1,{value:Number.NaN,type:a.NumberType.Number}]:[a.TokenType.Number,e.toString(),-1,-1,{value:e,type:a.NumberType.Number}]}function reducePrecision(e,o=7){if(Number.isNaN(e))return 0;const a=Math.pow(10,o);return Math.round(e*a)/a}function normalize(e,o,a,n){return Math.min(Math.max(e/o,a),n)}const l=/[A-Z]/g;function toLowerCaseAZ(e){return e.replace(l,(e=>String.fromCharCode(e.charCodeAt(0)+32)))}function normalize_Color_ChannelValues(e,o,n){if(a.isTokenIdent(e)&&"none"===toLowerCaseAZ(e[4].value))return n.syntaxFlags.add(exports.SyntaxFlag.HasNoneKeywords),[a.TokenType.Number,"none",e[2],e[3],{value:Number.NaN,type:a.NumberType.Number}];if(a.isTokenPercentage(e)){3!==o&&n.syntaxFlags.add(exports.SyntaxFlag.HasPercentageValues);let t=normalize(e[4].value,100,-2147483647,2147483647);return 3===o&&(t=normalize(e[4].value,100,0,1)),[a.TokenType.Number,t.toString(),e[2],e[3],{value:t,type:a.NumberType.Number}]}if(a.isTokenNumber(e)){3!==o&&n.syntaxFlags.add(exports.SyntaxFlag.HasNumberValues);let t=normalize(e[4].value,1,-2147483647,2147483647);return 3===o&&(t=normalize(e[4].value,1,0,1)),[a.TokenType.Number,t.toString(),e[2],e[3],{value:t,type:a.NumberType.Number}]}return!1}const i=new Set(["srgb","srgb-linear","display-p3","a98-rgb","prophoto-rgb","rec2020","xyz","xyz-d50","xyz-d65"]);function color$1(e,o){const n=[],s=[],l=[],u=[];let c,p,N=!1,m=!1;const h={colorNotation:exports.ColorNotation.sRGB,channels:[0,0,0],alpha:1,syntaxFlags:new Set([])};let x=n;for(let b=0;b=0){i=u.value[4].value;continue}}return!1}if(!l)return!1;n.push({color:l,percentage:i}),l=!1,i=!1}}if(l&&n.push({color:l,percentage:i}),2!==n.length)return!1;let u=n[0].percentage,c=n[1].percentage;return(!1===u||!(u<0||u>100))&&((!1===c||!(c<0||c>100))&&(!1===u&&!1===c?(u=50,c=50):!1!==u&&!1===c?c=100-u:!1===u&&!1!==c&&(u=100-c),(0!==u||0!==c)&&(!1!==u&&!1!==c&&(u+c>100&&(u=u/(u+c)*100,c=c/(u+c)*100),u+c<100&&(s=(u+c)/100,u=u/(u+c)*100,c=c/(u+c)*100),{a:{color:n[0].color,percentage:u},b:{color:n[1].color,percentage:c},alphaMultiplier:s}))))}function colorMixRectangular(e,o){if(!o)return!1;const a=o.a.color,n=o.b.color,t=o.a.percentage/100;let r=a.channels,s=n.channels,l=exports.ColorNotation.RGB,i=a.alpha;if("number"!=typeof i)return!1;let u=n.alpha;if("number"!=typeof u)return!1;switch(i=Number.isNaN(i)?u:i,u=Number.isNaN(u)?i:u,e){case"srgb":l=exports.ColorNotation.RGB;break;case"srgb-linear":l=exports.ColorNotation.Linear_sRGB;break;case"display-p3":l=exports.ColorNotation.Display_P3;break;case"a98-rgb":l=exports.ColorNotation.A98_RGB;break;case"prophoto-rgb":l=exports.ColorNotation.ProPhoto_RGB;break;case"rec2020":l=exports.ColorNotation.Rec2020;break;case"lab":l=exports.ColorNotation.Lab;break;case"oklab":l=exports.ColorNotation.OKLab;break;case"xyz-d50":l=exports.ColorNotation.XYZ_D50;break;case"xyz":case"xyz-d65":l=exports.ColorNotation.XYZ_D65}r=colorDataTo(a,l).channels,s=colorDataTo(n,l).channels,r[0]=fillInMissingComponent(r[0],s[0]),s[0]=fillInMissingComponent(s[0],r[0]),r[1]=fillInMissingComponent(r[1],s[1]),s[1]=fillInMissingComponent(s[1],r[1]),r[2]=fillInMissingComponent(r[2],s[2]),s[2]=fillInMissingComponent(s[2],r[2]),r[0]=premultiply(r[0],i),r[1]=premultiply(r[1],i),r[2]=premultiply(r[2],i),s[0]=premultiply(s[0],u),s[1]=premultiply(s[1],u),s[2]=premultiply(s[2],u);const c=interpolate(i,u,t),p={colorNotation:l,channels:[un_premultiply(interpolate(r[0],s[0],t),c),un_premultiply(interpolate(r[1],s[1],t),c),un_premultiply(interpolate(r[2],s[2],t),c)],alpha:c*o.alphaMultiplier,syntaxFlags:new Set([exports.SyntaxFlag.ColorMix])};return(o.a.color.syntaxFlags.has(exports.SyntaxFlag.Experimental)||o.b.color.syntaxFlags.has(exports.SyntaxFlag.Experimental))&&p.syntaxFlags.add(exports.SyntaxFlag.Experimental),p}function colorMixPolar(e,o,a){if(!a)return!1;const n=a.a.color,t=a.b.color,r=a.a.percentage/100;let s=n.channels,l=t.channels,i=0,u=0,c=0,p=0,N=0,m=0,h=exports.ColorNotation.RGB,x=n.alpha;if("number"!=typeof x)return!1;let b=t.alpha;if("number"!=typeof b)return!1;switch(x=Number.isNaN(x)?b:x,b=Number.isNaN(b)?x:b,e){case"hsl":h=exports.ColorNotation.HSL;break;case"hwb":h=exports.ColorNotation.HWB;break;case"lch":h=exports.ColorNotation.LCH;break;case"oklch":h=exports.ColorNotation.OKLCH}switch(s=colorDataTo(n,h).channels,l=colorDataTo(t,h).channels,e){case"hsl":case"hwb":i=s[0],u=l[0],c=s[1],p=l[1],N=s[2],m=l[2];break;case"lch":case"oklch":c=s[0],p=l[0],N=s[1],m=l[1],i=s[2],u=l[2]}i=fillInMissingComponent(i,u),Number.isNaN(i)&&(i=0),u=fillInMissingComponent(u,i),Number.isNaN(u)&&(u=0),c=fillInMissingComponent(c,p),p=fillInMissingComponent(p,c),N=fillInMissingComponent(N,m),m=fillInMissingComponent(m,N);const y=u-i;switch(o){case"shorter":y>180?i+=360:y<-180&&(u+=360);break;case"longer":-1800?i+=360:u+=360);break;case"increasing":y<0&&(u+=360);break;case"decreasing":y>0&&(i+=360);break;default:throw new Error("Unknown hue interpolation method")}c=premultiply(c,x),N=premultiply(N,x),p=premultiply(p,b),m=premultiply(m,b);let C=[0,0,0];const d=interpolate(x,b,r);switch(e){case"hsl":case"hwb":C=[interpolate(i,u,r),un_premultiply(interpolate(c,p,r),d),un_premultiply(interpolate(N,m,r),d)];break;case"lch":case"oklch":C=[un_premultiply(interpolate(c,p,r),d),un_premultiply(interpolate(N,m,r),d),interpolate(i,u,r)]}const T={colorNotation:h,channels:C,alpha:d*a.alphaMultiplier,syntaxFlags:new Set([exports.SyntaxFlag.ColorMix])};return(a.a.color.syntaxFlags.has(exports.SyntaxFlag.Experimental)||a.b.color.syntaxFlags.has(exports.SyntaxFlag.Experimental))&&T.syntaxFlags.add(exports.SyntaxFlag.Experimental),T}function fillInMissingComponent(e,o){return Number.isNaN(e)?o:e}function interpolate(e,o,a){return e*a+o*(1-a)}function premultiply(e,o){return Number.isNaN(o)?e:Number.isNaN(e)?Number.NaN:e*o}function un_premultiply(e,o){return 0===o||Number.isNaN(o)?e:Number.isNaN(e)?Number.NaN:e/o}function hex(e){const o=toLowerCaseAZ(e[4].value);if(o.match(/[^a-f0-9]/))return!1;const a={colorNotation:exports.ColorNotation.HEX,channels:[0,0,0],alpha:1,syntaxFlags:new Set([exports.SyntaxFlag.Hex])},n=o.length;if(3===n){const e=o[0],n=o[1],t=o[2];return a.channels=[parseInt(e+e,16)/255,parseInt(n+n,16)/255,parseInt(t+t,16)/255],a}if(6===n){const e=o[0]+o[1],n=o[2]+o[3],t=o[4]+o[5];return a.channels=[parseInt(e,16)/255,parseInt(n,16)/255,parseInt(t,16)/255],a}if(4===n){const e=o[0],n=o[1],t=o[2],r=o[3];return a.channels=[parseInt(e+e,16)/255,parseInt(n+n,16)/255,parseInt(t+t,16)/255],a.alpha=parseInt(r+r,16)/255,a.syntaxFlags.add(exports.SyntaxFlag.HasAlpha),a}if(8===n){const e=o[0]+o[1],n=o[2]+o[3],t=o[4]+o[5],r=o[6]+o[7];return a.channels=[parseInt(e,16)/255,parseInt(n,16)/255,parseInt(t,16)/255],a.alpha=parseInt(r,16)/255,a.syntaxFlags.add(exports.SyntaxFlag.HasAlpha),a}return!1}function normalizeHue(e){if(a.isTokenNumber(e))return e[4].value=e[4].value%360,e[1]=e[4].value.toString(),e;if(a.isTokenDimension(e)){let o=e[4].value;switch(toLowerCaseAZ(e[4].unit)){case"deg":break;case"rad":o=180*e[4].value/Math.PI;break;case"grad":o=.9*e[4].value;break;case"turn":o=360*e[4].value;break;default:return!1}return o%=360,[a.TokenType.Number,o.toString(),e[2],e[3],{value:o,type:a.NumberType.Number}]}return!1}function normalize_legacy_HSL_ChannelValues(e,o,n){if(0===o){const o=normalizeHue(e);return!1!==o&&(a.isTokenDimension(e)&&n.syntaxFlags.add(exports.SyntaxFlag.HasDimensionValues),o)}if(a.isTokenPercentage(e)){3===o?n.syntaxFlags.add(exports.SyntaxFlag.HasPercentageAlpha):n.syntaxFlags.add(exports.SyntaxFlag.HasPercentageValues);let t=normalize(e[4].value,1,0,100);return 3===o&&(t=normalize(e[4].value,100,0,1)),[a.TokenType.Number,t.toString(),e[2],e[3],{value:t,type:a.NumberType.Number}]}if(a.isTokenNumber(e)){if(3!==o)return!1;let n=normalize(e[4].value,1,0,100);return 3===o&&(n=normalize(e[4].value,1,0,1)),[a.TokenType.Number,n.toString(),e[2],e[3],{value:n,type:a.NumberType.Number}]}return!1}function normalize_modern_HSL_ChannelValues(e,o,n){if(a.isTokenIdent(e)&&"none"===toLowerCaseAZ(e[4].value))return n.syntaxFlags.add(exports.SyntaxFlag.HasNoneKeywords),[a.TokenType.Number,"none",e[2],e[3],{value:Number.NaN,type:a.NumberType.Number}];if(0===o){const o=normalizeHue(e);return!1!==o&&(a.isTokenDimension(e)&&n.syntaxFlags.add(exports.SyntaxFlag.HasDimensionValues),o)}if(a.isTokenPercentage(e)){3===o?n.syntaxFlags.add(exports.SyntaxFlag.HasPercentageAlpha):n.syntaxFlags.add(exports.SyntaxFlag.HasPercentageValues);let t=e[4].value;return 3===o?t=normalize(e[4].value,100,0,1):1===o&&(t=normalize(e[4].value,1,0,2147483647)),[a.TokenType.Number,t.toString(),e[2],e[3],{value:t,type:a.NumberType.Number}]}if(a.isTokenNumber(e)){3!==o&&n.syntaxFlags.add(exports.SyntaxFlag.HasNumberValues);let t=e[4].value;return 3===o?t=normalize(e[4].value,1,0,1):1===o&&(t=normalize(e[4].value,1,0,2147483647)),[a.TokenType.Number,t.toString(),e[2],e[3],{value:t,type:a.NumberType.Number}]}return!1}function threeChannelLegacySyntax(e,o,n,s){const l=[],i=[],u=[],c=[],p={colorNotation:n,channels:[0,0,0],alpha:1,syntaxFlags:new Set(s)};let N=l;for(let o=0;ot.isTokenNode(e)&&a.isTokenComma(e.value)))){const o=hslCommaSeparated(e);if(!1!==o)return o}{const a=hslSpaceSeparated(e,o);if(!1!==a)return a}return!1}function hslCommaSeparated(e){return threeChannelLegacySyntax(e,normalize_legacy_HSL_ChannelValues,exports.ColorNotation.HSL,[exports.SyntaxFlag.LegacyHSL])}function hslSpaceSeparated(e,o){return threeChannelSpaceSeparated(e,normalize_modern_HSL_ChannelValues,exports.ColorNotation.HSL,[],o)}function normalize_HWB_ChannelValues(e,o,n){if(a.isTokenIdent(e)&&"none"===toLowerCaseAZ(e[4].value))return n.syntaxFlags.add(exports.SyntaxFlag.HasNoneKeywords),[a.TokenType.Number,"none",e[2],e[3],{value:Number.NaN,type:a.NumberType.Number}];if(0===o){const o=normalizeHue(e);return!1!==o&&(a.isTokenDimension(e)&&n.syntaxFlags.add(exports.SyntaxFlag.HasDimensionValues),o)}if(a.isTokenPercentage(e)){3===o?n.syntaxFlags.add(exports.SyntaxFlag.HasPercentageAlpha):n.syntaxFlags.add(exports.SyntaxFlag.HasPercentageValues);let t=e[4].value;return 3===o&&(t=normalize(e[4].value,100,0,1)),[a.TokenType.Number,t.toString(),e[2],e[3],{value:t,type:a.NumberType.Number}]}if(a.isTokenNumber(e)){3!==o&&n.syntaxFlags.add(exports.SyntaxFlag.HasNumberValues);let t=e[4].value;return 3===o&&(t=normalize(e[4].value,1,0,1)),[a.TokenType.Number,t.toString(),e[2],e[3],{value:t,type:a.NumberType.Number}]}return!1}function normalize_Lab_ChannelValues(e,o,n){if(a.isTokenIdent(e)&&"none"===toLowerCaseAZ(e[4].value))return n.syntaxFlags.add(exports.SyntaxFlag.HasNoneKeywords),[a.TokenType.Number,"none",e[2],e[3],{value:Number.NaN,type:a.NumberType.Number}];if(a.isTokenPercentage(e)){3!==o&&n.syntaxFlags.add(exports.SyntaxFlag.HasPercentageValues);let t=normalize(e[4].value,1,0,100);return 1===o||2===o?t=normalize(e[4].value,.8,-2147483647,2147483647):3===o&&(t=normalize(e[4].value,100,0,1)),[a.TokenType.Number,t.toString(),e[2],e[3],{value:t,type:a.NumberType.Number}]}if(a.isTokenNumber(e)){3!==o&&n.syntaxFlags.add(exports.SyntaxFlag.HasNumberValues);let t=normalize(e[4].value,1,0,100);return 1===o||2===o?t=normalize(e[4].value,1,-2147483647,2147483647):3===o&&(t=normalize(e[4].value,1,0,1)),[a.TokenType.Number,t.toString(),e[2],e[3],{value:t,type:a.NumberType.Number}]}return!1}function lab(e,o){return threeChannelSpaceSeparated(e,normalize_Lab_ChannelValues,exports.ColorNotation.Lab,[],o)}function normalize_LCH_ChannelValues(e,o,n){if(a.isTokenIdent(e)&&"none"===toLowerCaseAZ(e[4].value))return n.syntaxFlags.add(exports.SyntaxFlag.HasNoneKeywords),[a.TokenType.Number,"none",e[2],e[3],{value:Number.NaN,type:a.NumberType.Number}];if(2===o){const o=normalizeHue(e);return!1!==o&&(a.isTokenDimension(e)&&n.syntaxFlags.add(exports.SyntaxFlag.HasDimensionValues),o)}if(a.isTokenPercentage(e)){3!==o&&n.syntaxFlags.add(exports.SyntaxFlag.HasPercentageValues);let t=normalize(e[4].value,1,0,100);return 1===o?t=normalize(e[4].value,100/150,0,2147483647):3===o&&(t=normalize(e[4].value,100,0,1)),[a.TokenType.Number,t.toString(),e[2],e[3],{value:t,type:a.NumberType.Number}]}if(a.isTokenNumber(e)){3!==o&&n.syntaxFlags.add(exports.SyntaxFlag.HasNumberValues);let t=normalize(e[4].value,1,0,100);return 1===o?t=normalize(e[4].value,1,0,2147483647):3===o&&(t=normalize(e[4].value,1,0,1)),[a.TokenType.Number,t.toString(),e[2],e[3],{value:t,type:a.NumberType.Number}]}return!1}function lch(e,o){return threeChannelSpaceSeparated(e,normalize_LCH_ChannelValues,exports.ColorNotation.LCH,[],o)}const N=new Map;for(const[e,o]of Object.entries(n.namedColors))N.set(e,o);function namedColor(e){const o=N.get(toLowerCaseAZ(e));return!!o&&{colorNotation:exports.ColorNotation.RGB,channels:[o[0]/255,o[1]/255,o[2]/255],alpha:1,syntaxFlags:new Set([exports.SyntaxFlag.ColorKeyword,exports.SyntaxFlag.NamedColor])}}function normalize_OKLab_ChannelValues(e,o,n){if(a.isTokenIdent(e)&&"none"===toLowerCaseAZ(e[4].value))return n.syntaxFlags.add(exports.SyntaxFlag.HasNoneKeywords),[a.TokenType.Number,"none",e[2],e[3],{value:Number.NaN,type:a.NumberType.Number}];if(a.isTokenPercentage(e)){3!==o&&n.syntaxFlags.add(exports.SyntaxFlag.HasPercentageValues);let t=normalize(e[4].value,100,0,1);return 1===o||2===o?t=normalize(e[4].value,250,-2147483647,2147483647):3===o&&(t=normalize(e[4].value,100,0,1)),[a.TokenType.Number,t.toString(),e[2],e[3],{value:t,type:a.NumberType.Number}]}if(a.isTokenNumber(e)){3!==o&&n.syntaxFlags.add(exports.SyntaxFlag.HasNumberValues);let t=normalize(e[4].value,1,0,1);return 1===o||2===o?t=normalize(e[4].value,1,-2147483647,2147483647):3===o&&(t=normalize(e[4].value,1,0,1)),[a.TokenType.Number,t.toString(),e[2],e[3],{value:t,type:a.NumberType.Number}]}return!1}function oklab(e,o){return threeChannelSpaceSeparated(e,normalize_OKLab_ChannelValues,exports.ColorNotation.OKLab,[],o)}function normalize_OKLCH_ChannelValues(e,o,n){if(a.isTokenIdent(e)&&"none"===toLowerCaseAZ(e[4].value))return n.syntaxFlags.add(exports.SyntaxFlag.HasNoneKeywords),[a.TokenType.Number,"none",e[2],e[3],{value:Number.NaN,type:a.NumberType.Number}];if(2===o){const o=normalizeHue(e);return!1!==o&&(a.isTokenDimension(e)&&n.syntaxFlags.add(exports.SyntaxFlag.HasDimensionValues),o)}if(a.isTokenPercentage(e)){3!==o&&n.syntaxFlags.add(exports.SyntaxFlag.HasPercentageValues);let t=normalize(e[4].value,100,0,1);return 1===o?t=normalize(e[4].value,250,0,2147483647):3===o&&(t=normalize(e[4].value,100,0,1)),[a.TokenType.Number,t.toString(),e[2],e[3],{value:t,type:a.NumberType.Number}]}if(a.isTokenNumber(e)){3!==o&&n.syntaxFlags.add(exports.SyntaxFlag.HasNumberValues);let t=normalize(e[4].value,1,0,1);return 1===o?t=normalize(e[4].value,1,0,2147483647):3===o&&(t=normalize(e[4].value,1,0,1)),[a.TokenType.Number,t.toString(),e[2],e[3],{value:t,type:a.NumberType.Number}]}return!1}function oklch(e,o){return threeChannelSpaceSeparated(e,normalize_OKLCH_ChannelValues,exports.ColorNotation.OKLCH,[],o)}function normalize_legacy_sRGB_ChannelValues(e,o,n){if(a.isTokenPercentage(e)){3===o?n.syntaxFlags.add(exports.SyntaxFlag.HasPercentageAlpha):n.syntaxFlags.add(exports.SyntaxFlag.HasPercentageValues);const t=normalize(e[4].value,100,0,1);return[a.TokenType.Number,t.toString(),e[2],e[3],{value:t,type:a.NumberType.Number}]}if(a.isTokenNumber(e)){3!==o&&n.syntaxFlags.add(exports.SyntaxFlag.HasNumberValues);let t=normalize(e[4].value,255,0,1);return 3===o&&(t=normalize(e[4].value,1,0,1)),[a.TokenType.Number,t.toString(),e[2],e[3],{value:t,type:a.NumberType.Number}]}return!1}function normalize_modern_sRGB_ChannelValues(e,o,n){if(a.isTokenIdent(e)&&"none"===e[4].value.toLowerCase())return n.syntaxFlags.add(exports.SyntaxFlag.HasNoneKeywords),[a.TokenType.Number,"none",e[2],e[3],{value:Number.NaN,type:a.NumberType.Number}];if(a.isTokenPercentage(e)){3!==o&&n.syntaxFlags.add(exports.SyntaxFlag.HasPercentageValues);let t=normalize(e[4].value,100,-2147483647,2147483647);return 3===o&&(t=normalize(e[4].value,100,0,1)),[a.TokenType.Number,t.toString(),e[2],e[3],{value:t,type:a.NumberType.Number}]}if(a.isTokenNumber(e)){3!==o&&n.syntaxFlags.add(exports.SyntaxFlag.HasNumberValues);let t=normalize(e[4].value,255,-2147483647,2147483647);return 3===o&&(t=normalize(e[4].value,1,0,1)),[a.TokenType.Number,t.toString(),e[2],e[3],{value:t,type:a.NumberType.Number}]}return!1}function rgb(e,o){if(e.value.some((e=>t.isTokenNode(e)&&a.isTokenComma(e.value)))){const o=rgbCommaSeparated(e);if(!1!==o)return(!o.syntaxFlags.has(exports.SyntaxFlag.HasNumberValues)||!o.syntaxFlags.has(exports.SyntaxFlag.HasPercentageValues))&&o}else{const a=rgbSpaceSeparated(e,o);if(!1!==a)return a}return!1}function rgbCommaSeparated(e){return threeChannelLegacySyntax(e,normalize_legacy_sRGB_ChannelValues,exports.ColorNotation.RGB,[exports.SyntaxFlag.LegacyRGB])}function rgbSpaceSeparated(e,o){return threeChannelSpaceSeparated(e,normalize_modern_sRGB_ChannelValues,exports.ColorNotation.RGB,[],o)}function XYZ_D50_to_sRGB_Gamut(e){const o=n.XYZ_D50_to_sRGB(e);if(n.inGamut(o))return n.clip(o);let a=e;return a=n.XYZ_D50_to_OKLCH(a),a[0]<1e-6&&(a=[0,0,0]),a[0]>.999999&&(a=[1,0,0]),n.gam_sRGB(n.mapGamutRayTrace(a,oklch_to_lin_srgb,lin_srgb_to_oklch))}function oklch_to_lin_srgb(e){return e=n.OKLCH_to_OKLab(e),e=n.OKLab_to_XYZ(e),n.XYZ_to_lin_sRGB(e)}function lin_srgb_to_oklch(e){return e=n.lin_sRGB_to_XYZ(e),e=n.XYZ_to_OKLab(e),n.OKLab_to_OKLCH(e)}function contrastColor(e,o){let a=!1;for(let n=0;nl?[1,1,1]:[0,0,0],r}function XYZ_D50_to_P3_Gamut(e){const o=n.XYZ_D50_to_P3(e);if(n.inGamut(o))return n.clip(o);let a=e;return a=n.XYZ_D50_to_OKLCH(a),a[0]<1e-6&&(a=[0,0,0]),a[0]>.999999&&(a=[1,0,0]),n.gam_P3(n.mapGamutRayTrace(a,oklch_to_lin_p3,lin_p3_to_oklch))}function oklch_to_lin_p3(e){return e=n.OKLCH_to_OKLab(e),e=n.OKLab_to_XYZ(e),n.XYZ_to_lin_P3(e)}function lin_p3_to_oklch(e){return e=n.lin_P3_to_XYZ(e),e=n.XYZ_to_OKLab(e),n.OKLab_to_OKLCH(e)}function toPrecision(e,o=7){e=+e,o=+o;const a=(Math.floor(Math.abs(e))+"").length;if(o>a)return+e.toFixed(o-a);{const n=10**(a-o);return Math.round(e/n)*n}}function serializeWithAlpha(e,o,n,r){const s=[a.TokenType.CloseParen,")",-1,-1,void 0];if("number"==typeof e.alpha){const l=Math.min(1,Math.max(0,toPrecision(Number.isNaN(e.alpha)?0:e.alpha)));return 1===toPrecision(l,4)?new t.FunctionNode(o,s,r):new t.FunctionNode(o,s,[...r,new t.WhitespaceNode([n]),new t.TokenNode([a.TokenType.Delim,"/",-1,-1,{value:"/"}]),new t.WhitespaceNode([n]),new t.TokenNode([a.TokenType.Number,toPrecision(l,4).toString(),-1,-1,{value:e.alpha,type:a.NumberType.Integer}])])}return new t.FunctionNode(o,s,[...r,new t.WhitespaceNode([n]),new t.TokenNode([a.TokenType.Delim,"/",-1,-1,{value:"/"}]),new t.WhitespaceNode([n]),e.alpha])}exports.color=function color(e){if(t.isFunctionNode(e)){switch(toLowerCaseAZ(e.getName())){case"rgb":case"rgba":return rgb(e,color);case"hsl":case"hsla":return hsl(e,color);case"hwb":return o=color,threeChannelSpaceSeparated(e,normalize_HWB_ChannelValues,exports.ColorNotation.HWB,[],o);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)}}var o;if(t.isTokenNode(e)){if(a.isTokenHash(e.value))return hex(e.value);if(a.isTokenIdent(e.value)){const o=namedColor(e.value[4].value);return!1!==o?o:"transparent"===toLowerCaseAZ(e.value[4].value)&&{colorNotation:exports.ColorNotation.RGB,channels:[0,0,0],alpha:0,syntaxFlags:new Set([exports.SyntaxFlag.ColorKeyword])}}}return!1},exports.colorDataFitsDisplayP3_Gamut=function colorDataFitsDisplayP3_Gamut(e){const o={...e,channels:[...e.channels]};return o.channels=convertPowerlessComponentsToZeroValuesForDisplay(o.channels,o.colorNotation),!colorDataTo(o,exports.ColorNotation.Display_P3).channels.find((e=>e<-1e-5||e>1.00001))},exports.colorDataFitsRGB_Gamut=function colorDataFitsRGB_Gamut(e){const o={...e,channels:[...e.channels]};return o.channels=convertPowerlessComponentsToZeroValuesForDisplay(o.channels,o.colorNotation),!colorDataTo(o,exports.ColorNotation.RGB).channels.find((e=>e<-1e-5||e>1.00001))},exports.serializeHSL=function serializeHSL(e,o=!0){e.channels=convertPowerlessComponentsToZeroValuesForDisplay(e.channels,e.colorNotation);let r=e.channels.map((e=>Number.isNaN(e)?0:e));r=o?n.XYZ_D50_to_HSL(n.sRGB_to_XYZ_D50(XYZ_D50_to_sRGB_Gamut(colorData_to_XYZ_D50(e).channels))):n.XYZ_D50_to_HSL(colorData_to_XYZ_D50(e).channels),r=r.map((e=>Number.isNaN(e)?0:e));const s=Math.min(360,Math.max(0,Math.round(toPrecision(r[0])))),l=Math.min(100,Math.max(0,Math.round(toPrecision(r[1])))),i=Math.min(100,Math.max(0,Math.round(toPrecision(r[2])))),u=[a.TokenType.CloseParen,")",-1,-1,void 0],c=[a.TokenType.Whitespace," ",-1,-1,void 0],p=[a.TokenType.Comma,",",-1,-1,void 0],N=[new t.TokenNode([a.TokenType.Number,s.toString(),-1,-1,{value:r[0],type:a.NumberType.Integer}]),new t.TokenNode(p),new t.WhitespaceNode([c]),new t.TokenNode([a.TokenType.Percentage,l.toString()+"%",-1,-1,{value:r[1]}]),new t.TokenNode(p),new t.WhitespaceNode([c]),new t.TokenNode([a.TokenType.Percentage,i.toString()+"%",-1,-1,{value:r[2]}])];if("number"==typeof e.alpha){const o=Math.min(1,Math.max(0,toPrecision(Number.isNaN(e.alpha)?0:e.alpha)));return 1===toPrecision(o,4)?new t.FunctionNode([a.TokenType.Function,"hsl(",-1,-1,{value:"hsl"}],u,N):new t.FunctionNode([a.TokenType.Function,"hsla(",-1,-1,{value:"hsla"}],u,[...N,new t.TokenNode(p),new t.WhitespaceNode([c]),new t.TokenNode([a.TokenType.Number,toPrecision(o,4).toString(),-1,-1,{value:e.alpha,type:a.NumberType.Number}])])}return new t.FunctionNode([a.TokenType.Function,"hsla(",-1,-1,{value:"hsla"}],u,[...N,new t.TokenNode(p),new t.WhitespaceNode([c]),e.alpha])},exports.serializeOKLCH=function serializeOKLCH(e){e.channels=convertPowerlessComponentsToZeroValuesForDisplay(e.channels,e.colorNotation);let o=e.channels.map((e=>Number.isNaN(e)?0:e));e.colorNotation!==exports.ColorNotation.OKLCH&&(o=n.XYZ_D50_to_OKLCH(colorData_to_XYZ_D50(e).channels));const r=toPrecision(o[0],6),s=toPrecision(o[1],6),l=toPrecision(o[2],6),i=[a.TokenType.Function,"oklch(",-1,-1,{value:"oklch"}],u=[a.TokenType.Whitespace," ",-1,-1,void 0];return serializeWithAlpha(e,i,u,[new t.TokenNode([a.TokenType.Number,r.toString(),-1,-1,{value:o[0],type:a.NumberType.Number}]),new t.WhitespaceNode([u]),new t.TokenNode([a.TokenType.Number,s.toString(),-1,-1,{value:o[1],type:a.NumberType.Number}]),new t.WhitespaceNode([u]),new t.TokenNode([a.TokenType.Number,l.toString(),-1,-1,{value:o[2],type:a.NumberType.Number}])])},exports.serializeP3=function serializeP3(e,o=!0){e.channels=convertPowerlessComponentsToZeroValuesForDisplay(e.channels,e.colorNotation);let r=e.channels.map((e=>Number.isNaN(e)?0:e));o?r=XYZ_D50_to_P3_Gamut(colorData_to_XYZ_D50(e).channels):e.colorNotation!==exports.ColorNotation.Display_P3&&(r=n.XYZ_D50_to_P3(colorData_to_XYZ_D50(e).channels));const s=o?Math.min(1,Math.max(0,toPrecision(r[0],6))):toPrecision(r[0],6),l=o?Math.min(1,Math.max(0,toPrecision(r[1],6))):toPrecision(r[1],6),i=o?Math.min(1,Math.max(0,toPrecision(r[2],6))):toPrecision(r[2],6),u=[a.TokenType.Function,"color(",-1,-1,{value:"color"}],c=[a.TokenType.Whitespace," ",-1,-1,void 0];return serializeWithAlpha(e,u,c,[new t.TokenNode([a.TokenType.Ident,"display-p3",-1,-1,{value:"display-p3"}]),new t.WhitespaceNode([c]),new t.TokenNode([a.TokenType.Number,s.toString(),-1,-1,{value:r[0],type:a.NumberType.Number}]),new t.WhitespaceNode([c]),new t.TokenNode([a.TokenType.Number,l.toString(),-1,-1,{value:r[1],type:a.NumberType.Number}]),new t.WhitespaceNode([c]),new t.TokenNode([a.TokenType.Number,i.toString(),-1,-1,{value:r[2],type:a.NumberType.Number}])])},exports.serializeRGB=function serializeRGB(e,o=!0){e.channels=convertPowerlessComponentsToZeroValuesForDisplay(e.channels,e.colorNotation);let r=e.channels.map((e=>Number.isNaN(e)?0:e));r=o?XYZ_D50_to_sRGB_Gamut(colorData_to_XYZ_D50(e).channels):n.XYZ_D50_to_sRGB(colorData_to_XYZ_D50(e).channels);const s=Math.min(255,Math.max(0,Math.round(255*toPrecision(r[0])))),l=Math.min(255,Math.max(0,Math.round(255*toPrecision(r[1])))),i=Math.min(255,Math.max(0,Math.round(255*toPrecision(r[2])))),u=[a.TokenType.CloseParen,")",-1,-1,void 0],c=[a.TokenType.Whitespace," ",-1,-1,void 0],p=[a.TokenType.Comma,",",-1,-1,void 0],N=[new t.TokenNode([a.TokenType.Number,s.toString(),-1,-1,{value:Math.min(255,255*Math.max(0,r[0])),type:a.NumberType.Integer}]),new t.TokenNode(p),new t.WhitespaceNode([c]),new t.TokenNode([a.TokenType.Number,l.toString(),-1,-1,{value:Math.min(255,255*Math.max(0,r[1])),type:a.NumberType.Integer}]),new t.TokenNode(p),new t.WhitespaceNode([c]),new t.TokenNode([a.TokenType.Number,i.toString(),-1,-1,{value:Math.min(255,255*Math.max(0,r[2])),type:a.NumberType.Integer}])];if("number"==typeof e.alpha){const o=Math.min(1,Math.max(0,toPrecision(Number.isNaN(e.alpha)?0:e.alpha)));return 1===toPrecision(o,4)?new t.FunctionNode([a.TokenType.Function,"rgb(",-1,-1,{value:"rgb"}],u,N):new t.FunctionNode([a.TokenType.Function,"rgba(",-1,-1,{value:"rgba"}],u,[...N,new t.TokenNode(p),new t.WhitespaceNode([c]),new t.TokenNode([a.TokenType.Number,toPrecision(o,4).toString(),-1,-1,{value:e.alpha,type:a.NumberType.Number}])])}return new t.FunctionNode([a.TokenType.Function,"rgba(",-1,-1,{value:"rgba"}],u,[...N,new t.TokenNode(p),new t.WhitespaceNode([c]),e.alpha])}; +"use strict";var e,o,a=require("@csstools/css-tokenizer"),n=require("@csstools/color-helpers"),t=require("@csstools/css-parser-algorithms"),r=require("@csstools/css-calc");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 exports.ColorNotation.HEX:case exports.ColorNotation.RGB:case exports.ColorNotation.sRGB:return{...e,colorNotation:exports.ColorNotation.XYZ_D50,channels:n.sRGB_to_XYZ_D50(convertNaNToZero(e.channels))};case exports.ColorNotation.Linear_sRGB:return{...e,colorNotation:exports.ColorNotation.XYZ_D50,channels:n.lin_sRGB_to_XYZ_D50(convertNaNToZero(e.channels))};case exports.ColorNotation.Display_P3:return{...e,colorNotation:exports.ColorNotation.XYZ_D50,channels:n.P3_to_XYZ_D50(convertNaNToZero(e.channels))};case exports.ColorNotation.Rec2020:return{...e,colorNotation:exports.ColorNotation.XYZ_D50,channels:n.rec_2020_to_XYZ_D50(convertNaNToZero(e.channels))};case exports.ColorNotation.A98_RGB:return{...e,colorNotation:exports.ColorNotation.XYZ_D50,channels:n.a98_RGB_to_XYZ_D50(convertNaNToZero(e.channels))};case exports.ColorNotation.ProPhoto_RGB:return{...e,colorNotation:exports.ColorNotation.XYZ_D50,channels:n.ProPhoto_RGB_to_XYZ_D50(convertNaNToZero(e.channels))};case exports.ColorNotation.HSL:return{...e,colorNotation:exports.ColorNotation.XYZ_D50,channels:n.HSL_to_XYZ_D50(convertNaNToZero(e.channels))};case exports.ColorNotation.HWB:return{...e,colorNotation:exports.ColorNotation.XYZ_D50,channels:n.HWB_to_XYZ_D50(convertNaNToZero(e.channels))};case exports.ColorNotation.Lab:return{...e,colorNotation:exports.ColorNotation.XYZ_D50,channels:n.Lab_to_XYZ_D50(convertNaNToZero(e.channels))};case exports.ColorNotation.OKLab:return{...e,colorNotation:exports.ColorNotation.XYZ_D50,channels:n.OKLab_to_XYZ_D50(convertNaNToZero(e.channels))};case exports.ColorNotation.LCH:return{...e,colorNotation:exports.ColorNotation.XYZ_D50,channels:n.LCH_to_XYZ_D50(convertNaNToZero(e.channels))};case exports.ColorNotation.OKLCH:return{...e,colorNotation:exports.ColorNotation.XYZ_D50,channels:n.OKLCH_to_XYZ_D50(convertNaNToZero(e.channels))};case exports.ColorNotation.XYZ_D50:return{...e,colorNotation:exports.ColorNotation.XYZ_D50,channels:n.XYZ_D50_to_XYZ_D50(convertNaNToZero(e.channels))};case exports.ColorNotation.XYZ_D65:return{...e,colorNotation:exports.ColorNotation.XYZ_D50,channels:n.XYZ_D65_to_XYZ_D50(convertNaNToZero(e.channels))};default:throw new Error("Unsupported color notation")}}exports.ColorNotation=void 0,(e=exports.ColorNotation||(exports.ColorNotation={})).A98_RGB="a98-rgb",e.Display_P3="display-p3",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",exports.SyntaxFlag=void 0,(o=exports.SyntaxFlag||(exports.SyntaxFlag={})).ColorKeyword="color-keyword",o.HasAlpha="has-alpha",o.HasDimensionValues="has-dimension-values",o.HasNoneKeywords="has-none-keywords",o.HasNumberValues="has-number-values",o.HasPercentageAlpha="has-percentage-alpha",o.HasPercentageValues="has-percentage-values",o.HasVariableAlpha="has-variable-alpha",o.Hex="hex",o.LegacyHSL="legacy-hsl",o.LegacyRGB="legacy-rgb",o.NamedColor="named-color",o.RelativeColorSyntax="relative-color-syntax",o.ColorMix="color-mix",o.ColorMixVariadic="color-mix-variadic",o.ContrastColor="contrast-color",o.Experimental="experimental";const s=new Set([exports.ColorNotation.A98_RGB,exports.ColorNotation.Display_P3,exports.ColorNotation.HEX,exports.ColorNotation.Linear_sRGB,exports.ColorNotation.ProPhoto_RGB,exports.ColorNotation.RGB,exports.ColorNotation.sRGB,exports.ColorNotation.Rec2020,exports.ColorNotation.XYZ_D50,exports.ColorNotation.XYZ_D65]);function colorDataTo(e,o){const a={...e};if(e.colorNotation!==o){const e=colorData_to_XYZ_D50(a);switch(o){case exports.ColorNotation.HEX:case exports.ColorNotation.RGB:a.colorNotation=exports.ColorNotation.RGB,a.channels=n.XYZ_D50_to_sRGB(e.channels);break;case exports.ColorNotation.sRGB:a.colorNotation=exports.ColorNotation.sRGB,a.channels=n.XYZ_D50_to_sRGB(e.channels);break;case exports.ColorNotation.Linear_sRGB:a.colorNotation=exports.ColorNotation.Linear_sRGB,a.channels=n.XYZ_D50_to_lin_sRGB(e.channels);break;case exports.ColorNotation.Display_P3:a.colorNotation=exports.ColorNotation.Display_P3,a.channels=n.XYZ_D50_to_P3(e.channels);break;case exports.ColorNotation.Rec2020:a.colorNotation=exports.ColorNotation.Rec2020,a.channels=n.XYZ_D50_to_rec_2020(e.channels);break;case exports.ColorNotation.ProPhoto_RGB:a.colorNotation=exports.ColorNotation.ProPhoto_RGB,a.channels=n.XYZ_D50_to_ProPhoto(e.channels);break;case exports.ColorNotation.A98_RGB:a.colorNotation=exports.ColorNotation.A98_RGB,a.channels=n.XYZ_D50_to_a98_RGB(e.channels);break;case exports.ColorNotation.HSL:a.colorNotation=exports.ColorNotation.HSL,a.channels=n.XYZ_D50_to_HSL(e.channels);break;case exports.ColorNotation.HWB:a.colorNotation=exports.ColorNotation.HWB,a.channels=n.XYZ_D50_to_HWB(e.channels);break;case exports.ColorNotation.Lab:a.colorNotation=exports.ColorNotation.Lab,a.channels=n.XYZ_D50_to_Lab(e.channels);break;case exports.ColorNotation.LCH:a.colorNotation=exports.ColorNotation.LCH,a.channels=n.XYZ_D50_to_LCH(e.channels);break;case exports.ColorNotation.OKLCH:a.colorNotation=exports.ColorNotation.OKLCH,a.channels=n.XYZ_D50_to_OKLCH(e.channels);break;case exports.ColorNotation.OKLab:a.colorNotation=exports.ColorNotation.OKLab,a.channels=n.XYZ_D50_to_OKLab(e.channels);break;case exports.ColorNotation.XYZ_D50:a.colorNotation=exports.ColorNotation.XYZ_D50,a.channels=n.XYZ_D50_to_XYZ_D50(e.channels);break;case exports.ColorNotation.XYZ_D65:a.colorNotation=exports.ColorNotation.XYZ_D65,a.channels=n.XYZ_D50_to_XYZ_D65(e.channels);break;default:throw new Error("Unsupported color notation")}}else a.channels=convertNaNToZero(e.channels);if(o===e.colorNotation)a.channels=carryForwardMissingComponents(e.channels,[0,1,2],a.channels,[0,1,2]);else if(s.has(o)&&s.has(e.colorNotation))a.channels=carryForwardMissingComponents(e.channels,[0,1,2],a.channels,[0,1,2]);else switch(o){case exports.ColorNotation.HSL:switch(e.colorNotation){case exports.ColorNotation.HWB:a.channels=carryForwardMissingComponents(e.channels,[0],a.channels,[0]);break;case exports.ColorNotation.Lab:case exports.ColorNotation.OKLab:a.channels=carryForwardMissingComponents(e.channels,[2],a.channels,[0]);break;case exports.ColorNotation.LCH:case exports.ColorNotation.OKLCH:a.channels=carryForwardMissingComponents(e.channels,[0,1,2],a.channels,[2,1,0])}break;case exports.ColorNotation.HWB:switch(e.colorNotation){case exports.ColorNotation.HSL:a.channels=carryForwardMissingComponents(e.channels,[0],a.channels,[0]);break;case exports.ColorNotation.LCH:case exports.ColorNotation.OKLCH:a.channels=carryForwardMissingComponents(e.channels,[0],a.channels,[2])}break;case exports.ColorNotation.Lab:case exports.ColorNotation.OKLab:switch(e.colorNotation){case exports.ColorNotation.HSL:a.channels=carryForwardMissingComponents(e.channels,[0],a.channels,[2]);break;case exports.ColorNotation.Lab:case exports.ColorNotation.OKLab:a.channels=carryForwardMissingComponents(e.channels,[0,1,2],a.channels,[0,1,2]);break;case exports.ColorNotation.LCH:case exports.ColorNotation.OKLCH:a.channels=carryForwardMissingComponents(e.channels,[0],a.channels,[0])}break;case exports.ColorNotation.LCH:case exports.ColorNotation.OKLCH:switch(e.colorNotation){case exports.ColorNotation.HSL:a.channels=carryForwardMissingComponents(e.channels,[0,1,2],a.channels,[2,1,0]);break;case exports.ColorNotation.HWB:a.channels=carryForwardMissingComponents(e.channels,[0],a.channels,[2]);break;case exports.ColorNotation.Lab:case exports.ColorNotation.OKLab:a.channels=carryForwardMissingComponents(e.channels,[0],a.channels,[0]);break;case exports.ColorNotation.LCH:case exports.ColorNotation.OKLCH:a.channels=carryForwardMissingComponents(e.channels,[0,1,2],a.channels,[0,1,2])}}return a.channels=convertPowerlessComponentsToMissingComponents(a.channels,o),a}function convertPowerlessComponentsToMissingComponents(e,o){const a=[...e];switch(o){case exports.ColorNotation.HSL:!Number.isNaN(a[1])&&reducePrecision(a[1],4)<=0&&(a[0]=Number.NaN);break;case exports.ColorNotation.HWB:Math.max(0,reducePrecision(a[1],4))+Math.max(0,reducePrecision(a[2],4))>=100&&(a[0]=Number.NaN);break;case exports.ColorNotation.LCH:!Number.isNaN(a[1])&&reducePrecision(a[1],4)<=0&&(a[2]=Number.NaN);break;case exports.ColorNotation.OKLCH:!Number.isNaN(a[1])&&reducePrecision(a[1],6)<=0&&(a[2]=Number.NaN)}return a}function convertPowerlessComponentsToZeroValuesForDisplay(e,o){const a=[...e];switch(o){case exports.ColorNotation.HSL:(reducePrecision(a[2])<=0||reducePrecision(a[2])>=100)&&(a[0]=Number.NaN,a[1]=Number.NaN),reducePrecision(a[1])<=0&&(a[0]=Number.NaN);break;case exports.ColorNotation.HWB:Math.max(0,reducePrecision(a[1]))+Math.max(0,reducePrecision(a[2]))>=100&&(a[0]=Number.NaN);break;case exports.ColorNotation.Lab:(reducePrecision(a[0])<=0||reducePrecision(a[0])>=100)&&(a[1]=Number.NaN,a[2]=Number.NaN);break;case exports.ColorNotation.LCH:reducePrecision(a[1])<=0&&(a[2]=Number.NaN),(reducePrecision(a[0])<=0||reducePrecision(a[0])>=100)&&(a[1]=Number.NaN,a[2]=Number.NaN);break;case exports.ColorNotation.OKLab:(reducePrecision(a[0])<=0||reducePrecision(a[0])>=1)&&(a[1]=Number.NaN,a[2]=Number.NaN);break;case exports.ColorNotation.OKLCH:reducePrecision(a[1])<=0&&(a[2]=Number.NaN),(reducePrecision(a[0])<=0||reducePrecision(a[0])>=1)&&(a[1]=Number.NaN,a[2]=Number.NaN)}return a}function carryForwardMissingComponents(e,o,a,n){const t=[...a];for(const a of o)Number.isNaN(e[o[a]])&&(t[n[a]]=Number.NaN);return t}function normalizeRelativeColorDataChannels(e){const o=new Map;switch(e.colorNotation){case exports.ColorNotation.RGB:case exports.ColorNotation.HEX:o.set("r",dummyNumberToken(255*e.channels[0])),o.set("g",dummyNumberToken(255*e.channels[1])),o.set("b",dummyNumberToken(255*e.channels[2])),"number"==typeof e.alpha&&o.set("alpha",dummyNumberToken(e.alpha));break;case exports.ColorNotation.HSL:o.set("h",dummyNumberToken(e.channels[0])),o.set("s",dummyNumberToken(e.channels[1])),o.set("l",dummyNumberToken(e.channels[2])),"number"==typeof e.alpha&&o.set("alpha",dummyNumberToken(e.alpha));break;case exports.ColorNotation.HWB:o.set("h",dummyNumberToken(e.channels[0])),o.set("w",dummyNumberToken(e.channels[1])),o.set("b",dummyNumberToken(e.channels[2])),"number"==typeof e.alpha&&o.set("alpha",dummyNumberToken(e.alpha));break;case exports.ColorNotation.Lab:case exports.ColorNotation.OKLab:o.set("l",dummyNumberToken(e.channels[0])),o.set("a",dummyNumberToken(e.channels[1])),o.set("b",dummyNumberToken(e.channels[2])),"number"==typeof e.alpha&&o.set("alpha",dummyNumberToken(e.alpha));break;case exports.ColorNotation.LCH:case exports.ColorNotation.OKLCH:o.set("l",dummyNumberToken(e.channels[0])),o.set("c",dummyNumberToken(e.channels[1])),o.set("h",dummyNumberToken(e.channels[2])),"number"==typeof e.alpha&&o.set("alpha",dummyNumberToken(e.alpha));break;case exports.ColorNotation.sRGB:case exports.ColorNotation.A98_RGB:case exports.ColorNotation.Display_P3:case exports.ColorNotation.Rec2020:case exports.ColorNotation.Linear_sRGB:case exports.ColorNotation.ProPhoto_RGB:o.set("r",dummyNumberToken(e.channels[0])),o.set("g",dummyNumberToken(e.channels[1])),o.set("b",dummyNumberToken(e.channels[2])),"number"==typeof e.alpha&&o.set("alpha",dummyNumberToken(e.alpha));break;case exports.ColorNotation.XYZ_D50:case exports.ColorNotation.XYZ_D65:o.set("x",dummyNumberToken(e.channels[0])),o.set("y",dummyNumberToken(e.channels[1])),o.set("z",dummyNumberToken(e.channels[2])),"number"==typeof e.alpha&&o.set("alpha",dummyNumberToken(e.alpha))}return o}function noneToZeroInRelativeColorDataChannels(e){const o=new Map(e);for(const[a,n]of e)Number.isNaN(n[4].value)&&o.set(a,dummyNumberToken(0));return o}function dummyNumberToken(e){return Number.isNaN(e)?[a.TokenType.Number,"none",-1,-1,{value:Number.NaN,type:a.NumberType.Number}]:[a.TokenType.Number,e.toString(),-1,-1,{value:e,type:a.NumberType.Number}]}function reducePrecision(e,o=7){if(Number.isNaN(e))return 0;const a=Math.pow(10,o);return Math.round(e*a)/a}function normalize(e,o,a,n){return Math.min(Math.max(e/o,a),n)}const l=/[A-Z]/g;function toLowerCaseAZ(e){return e.replace(l,(e=>String.fromCharCode(e.charCodeAt(0)+32)))}function normalize_Color_ChannelValues(e,o,n){if(a.isTokenIdent(e)&&"none"===toLowerCaseAZ(e[4].value))return n.syntaxFlags.add(exports.SyntaxFlag.HasNoneKeywords),[a.TokenType.Number,"none",e[2],e[3],{value:Number.NaN,type:a.NumberType.Number}];if(a.isTokenPercentage(e)){3!==o&&n.syntaxFlags.add(exports.SyntaxFlag.HasPercentageValues);let t=normalize(e[4].value,100,-2147483647,2147483647);return 3===o&&(t=normalize(e[4].value,100,0,1)),[a.TokenType.Number,t.toString(),e[2],e[3],{value:t,type:a.NumberType.Number}]}if(a.isTokenNumber(e)){3!==o&&n.syntaxFlags.add(exports.SyntaxFlag.HasNumberValues);let t=normalize(e[4].value,1,-2147483647,2147483647);return 3===o&&(t=normalize(e[4].value,1,0,1)),[a.TokenType.Number,t.toString(),e[2],e[3],{value:t,type:a.NumberType.Number}]}return!1}const i=new Set(["srgb","srgb-linear","display-p3","a98-rgb","prophoto-rgb","rec2020","xyz","xyz-d50","xyz-d65"]);function color$1(e,o){const n=[],s=[],l=[],u=[];let c,p,N=!1,m=!1;const h={colorNotation:exports.ColorNotation.sRGB,channels:[0,0,0],alpha:1,syntaxFlags:new Set([])};let x=n;for(let y=0;y=0){i=u.value[4].value;continue}}return!1}if(!l)return!1;n.push({color:l,percentage:i}),l=!1,i=!1}}l&&n.push({color:l,percentage:i});let u=0,c=0;for(let e=0;e100)return!1;u+=o}else c++}const p=Math.max(0,100-u);u=0;for(let e=0;e100)for(let e=0;e=2;){const e=a.pop(),o=a.pop();if(!e||!o)return!1;const t=colorMixRectangularPair(n,e.color,e.percentage,o.color,o.percentage);if(!t)return!1;a.push({color:t,percentage:e.percentage+o.percentage})}const t=a[0]?.color;return!!t&&(o.colors.some((e=>e.color.syntaxFlags.has(exports.SyntaxFlag.Experimental)))&&t.syntaxFlags.add(exports.SyntaxFlag.Experimental),"number"==typeof t.alpha&&(t.alpha=t.alpha*o.alphaMultiplier,2!==o.colors.length&&t.syntaxFlags.add(exports.SyntaxFlag.ColorMixVariadic),t))}function colorMixRectangularPair(e,o,a,n,t){const r=a/(a+t);let s=o.alpha;if("number"!=typeof s)return!1;let l=n.alpha;if("number"!=typeof l)return!1;s=Number.isNaN(s)?l:s,l=Number.isNaN(l)?s:l;const i=colorDataTo(o,e).channels,u=colorDataTo(n,e).channels;i[0]=fillInMissingComponent(i[0],u[0]),u[0]=fillInMissingComponent(u[0],i[0]),i[1]=fillInMissingComponent(i[1],u[1]),u[1]=fillInMissingComponent(u[1],i[1]),i[2]=fillInMissingComponent(i[2],u[2]),u[2]=fillInMissingComponent(u[2],i[2]),i[0]=premultiply(i[0],s),i[1]=premultiply(i[1],s),i[2]=premultiply(i[2],s),u[0]=premultiply(u[0],l),u[1]=premultiply(u[1],l),u[2]=premultiply(u[2],l);const c=interpolate(s,l,r);return{colorNotation:e,channels:[un_premultiply(interpolate(i[0],u[0],r),c),un_premultiply(interpolate(i[1],u[1],r),c),un_premultiply(interpolate(i[2],u[2],r),c)],alpha:c,syntaxFlags:new Set([exports.SyntaxFlag.ColorMix])}}function colorMixPolar(e,o,a){if(!a||!a.colors.length)return!1;const n=a.colors.slice();n.reverse();let t=exports.ColorNotation.HSL;switch(e){case"hsl":t=exports.ColorNotation.HSL;break;case"hwb":t=exports.ColorNotation.HWB;break;case"lch":t=exports.ColorNotation.LCH;break;case"oklch":t=exports.ColorNotation.OKLCH;break;default:return!1}if(1===n.length){const e=colorDataTo(n[0].color,t);return e.colorNotation=t,e.syntaxFlags.add(exports.SyntaxFlag.ColorMixVariadic),"number"!=typeof e.alpha?!1:(e.alpha=e.alpha*a.alphaMultiplier,e)}for(;n.length>=2;){const e=n.pop(),a=n.pop();if(!e||!a)return!1;const r=colorMixPolarPair(t,o,e.color,e.percentage,a.color,a.percentage);if(!r)return!1;n.push({color:r,percentage:e.percentage+a.percentage})}const r=n[0]?.color;return!!r&&(a.colors.some((e=>e.color.syntaxFlags.has(exports.SyntaxFlag.Experimental)))&&r.syntaxFlags.add(exports.SyntaxFlag.Experimental),"number"==typeof r.alpha&&(r.alpha=r.alpha*a.alphaMultiplier,2!==a.colors.length&&r.syntaxFlags.add(exports.SyntaxFlag.ColorMixVariadic),r))}function colorMixPolarPair(e,o,a,n,t,r){const s=n/(n+r);let l=0,i=0,u=0,c=0,p=0,N=0,m=a.alpha;if("number"!=typeof m)return!1;let h=t.alpha;if("number"!=typeof h)return!1;m=Number.isNaN(m)?h:m,h=Number.isNaN(h)?m:h;const x=colorDataTo(a,e).channels,y=colorDataTo(t,e).channels;switch(e){case exports.ColorNotation.HSL:case exports.ColorNotation.HWB:l=x[0],i=y[0],u=x[1],c=y[1],p=x[2],N=y[2];break;case exports.ColorNotation.LCH:case exports.ColorNotation.OKLCH:u=x[0],c=y[0],p=x[1],N=y[1],l=x[2],i=y[2]}l=fillInMissingComponent(l,i),Number.isNaN(l)&&(l=0),i=fillInMissingComponent(i,l),Number.isNaN(i)&&(i=0),u=fillInMissingComponent(u,c),c=fillInMissingComponent(c,u),p=fillInMissingComponent(p,N),N=fillInMissingComponent(N,p);const b=i-l;switch(o){case"shorter":b>180?l+=360:b<-180&&(i+=360);break;case"longer":-1800?l+=360:i+=360);break;case"increasing":b<0&&(i+=360);break;case"decreasing":b>0&&(l+=360);break;default:throw new Error("Unknown hue interpolation method")}u=premultiply(u,m),p=premultiply(p,m),c=premultiply(c,h),N=premultiply(N,h);let C=[0,0,0];const d=interpolate(m,h,s);switch(e){case exports.ColorNotation.HSL:case exports.ColorNotation.HWB:C=[interpolate(l,i,s),un_premultiply(interpolate(u,c,s),d),un_premultiply(interpolate(p,N,s),d)];break;case exports.ColorNotation.LCH:case exports.ColorNotation.OKLCH:C=[un_premultiply(interpolate(u,c,s),d),un_premultiply(interpolate(p,N,s),d),interpolate(l,i,s)]}return{colorNotation:e,channels:C,alpha:d,syntaxFlags:new Set([exports.SyntaxFlag.ColorMix])}}function fillInMissingComponent(e,o){return Number.isNaN(e)?o:e}function interpolate(e,o,a){return e*a+o*(1-a)}function premultiply(e,o){return Number.isNaN(o)?e:Number.isNaN(e)?Number.NaN:e*o}function un_premultiply(e,o){return 0===o||Number.isNaN(o)?e:Number.isNaN(e)?Number.NaN:e/o}function hex(e){const o=toLowerCaseAZ(e[4].value);if(o.match(/[^a-f0-9]/))return!1;const a={colorNotation:exports.ColorNotation.HEX,channels:[0,0,0],alpha:1,syntaxFlags:new Set([exports.SyntaxFlag.Hex])},n=o.length;if(3===n){const e=o[0],n=o[1],t=o[2];return a.channels=[parseInt(e+e,16)/255,parseInt(n+n,16)/255,parseInt(t+t,16)/255],a}if(6===n){const e=o[0]+o[1],n=o[2]+o[3],t=o[4]+o[5];return a.channels=[parseInt(e,16)/255,parseInt(n,16)/255,parseInt(t,16)/255],a}if(4===n){const e=o[0],n=o[1],t=o[2],r=o[3];return a.channels=[parseInt(e+e,16)/255,parseInt(n+n,16)/255,parseInt(t+t,16)/255],a.alpha=parseInt(r+r,16)/255,a.syntaxFlags.add(exports.SyntaxFlag.HasAlpha),a}if(8===n){const e=o[0]+o[1],n=o[2]+o[3],t=o[4]+o[5],r=o[6]+o[7];return a.channels=[parseInt(e,16)/255,parseInt(n,16)/255,parseInt(t,16)/255],a.alpha=parseInt(r,16)/255,a.syntaxFlags.add(exports.SyntaxFlag.HasAlpha),a}return!1}function normalizeHue(e){if(a.isTokenNumber(e))return e[4].value=e[4].value%360,e[1]=e[4].value.toString(),e;if(a.isTokenDimension(e)){let o=e[4].value;switch(toLowerCaseAZ(e[4].unit)){case"deg":break;case"rad":o=180*e[4].value/Math.PI;break;case"grad":o=.9*e[4].value;break;case"turn":o=360*e[4].value;break;default:return!1}return o%=360,[a.TokenType.Number,o.toString(),e[2],e[3],{value:o,type:a.NumberType.Number}]}return!1}function normalize_legacy_HSL_ChannelValues(e,o,n){if(0===o){const o=normalizeHue(e);return!1!==o&&(a.isTokenDimension(e)&&n.syntaxFlags.add(exports.SyntaxFlag.HasDimensionValues),o)}if(a.isTokenPercentage(e)){3===o?n.syntaxFlags.add(exports.SyntaxFlag.HasPercentageAlpha):n.syntaxFlags.add(exports.SyntaxFlag.HasPercentageValues);let t=normalize(e[4].value,1,0,100);return 3===o&&(t=normalize(e[4].value,100,0,1)),[a.TokenType.Number,t.toString(),e[2],e[3],{value:t,type:a.NumberType.Number}]}if(a.isTokenNumber(e)){if(3!==o)return!1;let n=normalize(e[4].value,1,0,100);return 3===o&&(n=normalize(e[4].value,1,0,1)),[a.TokenType.Number,n.toString(),e[2],e[3],{value:n,type:a.NumberType.Number}]}return!1}function normalize_modern_HSL_ChannelValues(e,o,n){if(a.isTokenIdent(e)&&"none"===toLowerCaseAZ(e[4].value))return n.syntaxFlags.add(exports.SyntaxFlag.HasNoneKeywords),[a.TokenType.Number,"none",e[2],e[3],{value:Number.NaN,type:a.NumberType.Number}];if(0===o){const o=normalizeHue(e);return!1!==o&&(a.isTokenDimension(e)&&n.syntaxFlags.add(exports.SyntaxFlag.HasDimensionValues),o)}if(a.isTokenPercentage(e)){3===o?n.syntaxFlags.add(exports.SyntaxFlag.HasPercentageAlpha):n.syntaxFlags.add(exports.SyntaxFlag.HasPercentageValues);let t=e[4].value;return 3===o?t=normalize(e[4].value,100,0,1):1===o&&(t=normalize(e[4].value,1,0,2147483647)),[a.TokenType.Number,t.toString(),e[2],e[3],{value:t,type:a.NumberType.Number}]}if(a.isTokenNumber(e)){3!==o&&n.syntaxFlags.add(exports.SyntaxFlag.HasNumberValues);let t=e[4].value;return 3===o?t=normalize(e[4].value,1,0,1):1===o&&(t=normalize(e[4].value,1,0,2147483647)),[a.TokenType.Number,t.toString(),e[2],e[3],{value:t,type:a.NumberType.Number}]}return!1}function threeChannelLegacySyntax(e,o,n,s){const l=[],i=[],u=[],c=[],p={colorNotation:n,channels:[0,0,0],alpha:1,syntaxFlags:new Set(s)};let N=l;for(let o=0;ot.isTokenNode(e)&&a.isTokenComma(e.value)))){const o=hslCommaSeparated(e);if(!1!==o)return o}{const a=hslSpaceSeparated(e,o);if(!1!==a)return a}return!1}function hslCommaSeparated(e){return threeChannelLegacySyntax(e,normalize_legacy_HSL_ChannelValues,exports.ColorNotation.HSL,[exports.SyntaxFlag.LegacyHSL])}function hslSpaceSeparated(e,o){return threeChannelSpaceSeparated(e,normalize_modern_HSL_ChannelValues,exports.ColorNotation.HSL,[],o)}function normalize_HWB_ChannelValues(e,o,n){if(a.isTokenIdent(e)&&"none"===toLowerCaseAZ(e[4].value))return n.syntaxFlags.add(exports.SyntaxFlag.HasNoneKeywords),[a.TokenType.Number,"none",e[2],e[3],{value:Number.NaN,type:a.NumberType.Number}];if(0===o){const o=normalizeHue(e);return!1!==o&&(a.isTokenDimension(e)&&n.syntaxFlags.add(exports.SyntaxFlag.HasDimensionValues),o)}if(a.isTokenPercentage(e)){3===o?n.syntaxFlags.add(exports.SyntaxFlag.HasPercentageAlpha):n.syntaxFlags.add(exports.SyntaxFlag.HasPercentageValues);let t=e[4].value;return 3===o&&(t=normalize(e[4].value,100,0,1)),[a.TokenType.Number,t.toString(),e[2],e[3],{value:t,type:a.NumberType.Number}]}if(a.isTokenNumber(e)){3!==o&&n.syntaxFlags.add(exports.SyntaxFlag.HasNumberValues);let t=e[4].value;return 3===o&&(t=normalize(e[4].value,1,0,1)),[a.TokenType.Number,t.toString(),e[2],e[3],{value:t,type:a.NumberType.Number}]}return!1}function normalize_Lab_ChannelValues(e,o,n){if(a.isTokenIdent(e)&&"none"===toLowerCaseAZ(e[4].value))return n.syntaxFlags.add(exports.SyntaxFlag.HasNoneKeywords),[a.TokenType.Number,"none",e[2],e[3],{value:Number.NaN,type:a.NumberType.Number}];if(a.isTokenPercentage(e)){3!==o&&n.syntaxFlags.add(exports.SyntaxFlag.HasPercentageValues);let t=normalize(e[4].value,1,0,100);return 1===o||2===o?t=normalize(e[4].value,.8,-2147483647,2147483647):3===o&&(t=normalize(e[4].value,100,0,1)),[a.TokenType.Number,t.toString(),e[2],e[3],{value:t,type:a.NumberType.Number}]}if(a.isTokenNumber(e)){3!==o&&n.syntaxFlags.add(exports.SyntaxFlag.HasNumberValues);let t=normalize(e[4].value,1,0,100);return 1===o||2===o?t=normalize(e[4].value,1,-2147483647,2147483647):3===o&&(t=normalize(e[4].value,1,0,1)),[a.TokenType.Number,t.toString(),e[2],e[3],{value:t,type:a.NumberType.Number}]}return!1}function lab(e,o){return threeChannelSpaceSeparated(e,normalize_Lab_ChannelValues,exports.ColorNotation.Lab,[],o)}function normalize_LCH_ChannelValues(e,o,n){if(a.isTokenIdent(e)&&"none"===toLowerCaseAZ(e[4].value))return n.syntaxFlags.add(exports.SyntaxFlag.HasNoneKeywords),[a.TokenType.Number,"none",e[2],e[3],{value:Number.NaN,type:a.NumberType.Number}];if(2===o){const o=normalizeHue(e);return!1!==o&&(a.isTokenDimension(e)&&n.syntaxFlags.add(exports.SyntaxFlag.HasDimensionValues),o)}if(a.isTokenPercentage(e)){3!==o&&n.syntaxFlags.add(exports.SyntaxFlag.HasPercentageValues);let t=normalize(e[4].value,1,0,100);return 1===o?t=normalize(e[4].value,100/150,0,2147483647):3===o&&(t=normalize(e[4].value,100,0,1)),[a.TokenType.Number,t.toString(),e[2],e[3],{value:t,type:a.NumberType.Number}]}if(a.isTokenNumber(e)){3!==o&&n.syntaxFlags.add(exports.SyntaxFlag.HasNumberValues);let t=normalize(e[4].value,1,0,100);return 1===o?t=normalize(e[4].value,1,0,2147483647):3===o&&(t=normalize(e[4].value,1,0,1)),[a.TokenType.Number,t.toString(),e[2],e[3],{value:t,type:a.NumberType.Number}]}return!1}function lch(e,o){return threeChannelSpaceSeparated(e,normalize_LCH_ChannelValues,exports.ColorNotation.LCH,[],o)}const N=new Map;for(const[e,o]of Object.entries(n.namedColors))N.set(e,o);function namedColor(e){const o=N.get(toLowerCaseAZ(e));return!!o&&{colorNotation:exports.ColorNotation.RGB,channels:[o[0]/255,o[1]/255,o[2]/255],alpha:1,syntaxFlags:new Set([exports.SyntaxFlag.ColorKeyword,exports.SyntaxFlag.NamedColor])}}function normalize_OKLab_ChannelValues(e,o,n){if(a.isTokenIdent(e)&&"none"===toLowerCaseAZ(e[4].value))return n.syntaxFlags.add(exports.SyntaxFlag.HasNoneKeywords),[a.TokenType.Number,"none",e[2],e[3],{value:Number.NaN,type:a.NumberType.Number}];if(a.isTokenPercentage(e)){3!==o&&n.syntaxFlags.add(exports.SyntaxFlag.HasPercentageValues);let t=normalize(e[4].value,100,0,1);return 1===o||2===o?t=normalize(e[4].value,250,-2147483647,2147483647):3===o&&(t=normalize(e[4].value,100,0,1)),[a.TokenType.Number,t.toString(),e[2],e[3],{value:t,type:a.NumberType.Number}]}if(a.isTokenNumber(e)){3!==o&&n.syntaxFlags.add(exports.SyntaxFlag.HasNumberValues);let t=normalize(e[4].value,1,0,1);return 1===o||2===o?t=normalize(e[4].value,1,-2147483647,2147483647):3===o&&(t=normalize(e[4].value,1,0,1)),[a.TokenType.Number,t.toString(),e[2],e[3],{value:t,type:a.NumberType.Number}]}return!1}function oklab(e,o){return threeChannelSpaceSeparated(e,normalize_OKLab_ChannelValues,exports.ColorNotation.OKLab,[],o)}function normalize_OKLCH_ChannelValues(e,o,n){if(a.isTokenIdent(e)&&"none"===toLowerCaseAZ(e[4].value))return n.syntaxFlags.add(exports.SyntaxFlag.HasNoneKeywords),[a.TokenType.Number,"none",e[2],e[3],{value:Number.NaN,type:a.NumberType.Number}];if(2===o){const o=normalizeHue(e);return!1!==o&&(a.isTokenDimension(e)&&n.syntaxFlags.add(exports.SyntaxFlag.HasDimensionValues),o)}if(a.isTokenPercentage(e)){3!==o&&n.syntaxFlags.add(exports.SyntaxFlag.HasPercentageValues);let t=normalize(e[4].value,100,0,1);return 1===o?t=normalize(e[4].value,250,0,2147483647):3===o&&(t=normalize(e[4].value,100,0,1)),[a.TokenType.Number,t.toString(),e[2],e[3],{value:t,type:a.NumberType.Number}]}if(a.isTokenNumber(e)){3!==o&&n.syntaxFlags.add(exports.SyntaxFlag.HasNumberValues);let t=normalize(e[4].value,1,0,1);return 1===o?t=normalize(e[4].value,1,0,2147483647):3===o&&(t=normalize(e[4].value,1,0,1)),[a.TokenType.Number,t.toString(),e[2],e[3],{value:t,type:a.NumberType.Number}]}return!1}function oklch(e,o){return threeChannelSpaceSeparated(e,normalize_OKLCH_ChannelValues,exports.ColorNotation.OKLCH,[],o)}function normalize_legacy_sRGB_ChannelValues(e,o,n){if(a.isTokenPercentage(e)){3===o?n.syntaxFlags.add(exports.SyntaxFlag.HasPercentageAlpha):n.syntaxFlags.add(exports.SyntaxFlag.HasPercentageValues);const t=normalize(e[4].value,100,0,1);return[a.TokenType.Number,t.toString(),e[2],e[3],{value:t,type:a.NumberType.Number}]}if(a.isTokenNumber(e)){3!==o&&n.syntaxFlags.add(exports.SyntaxFlag.HasNumberValues);let t=normalize(e[4].value,255,0,1);return 3===o&&(t=normalize(e[4].value,1,0,1)),[a.TokenType.Number,t.toString(),e[2],e[3],{value:t,type:a.NumberType.Number}]}return!1}function normalize_modern_sRGB_ChannelValues(e,o,n){if(a.isTokenIdent(e)&&"none"===e[4].value.toLowerCase())return n.syntaxFlags.add(exports.SyntaxFlag.HasNoneKeywords),[a.TokenType.Number,"none",e[2],e[3],{value:Number.NaN,type:a.NumberType.Number}];if(a.isTokenPercentage(e)){3!==o&&n.syntaxFlags.add(exports.SyntaxFlag.HasPercentageValues);let t=normalize(e[4].value,100,-2147483647,2147483647);return 3===o&&(t=normalize(e[4].value,100,0,1)),[a.TokenType.Number,t.toString(),e[2],e[3],{value:t,type:a.NumberType.Number}]}if(a.isTokenNumber(e)){3!==o&&n.syntaxFlags.add(exports.SyntaxFlag.HasNumberValues);let t=normalize(e[4].value,255,-2147483647,2147483647);return 3===o&&(t=normalize(e[4].value,1,0,1)),[a.TokenType.Number,t.toString(),e[2],e[3],{value:t,type:a.NumberType.Number}]}return!1}function rgb(e,o){if(e.value.some((e=>t.isTokenNode(e)&&a.isTokenComma(e.value)))){const o=rgbCommaSeparated(e);if(!1!==o)return(!o.syntaxFlags.has(exports.SyntaxFlag.HasNumberValues)||!o.syntaxFlags.has(exports.SyntaxFlag.HasPercentageValues))&&o}else{const a=rgbSpaceSeparated(e,o);if(!1!==a)return a}return!1}function rgbCommaSeparated(e){return threeChannelLegacySyntax(e,normalize_legacy_sRGB_ChannelValues,exports.ColorNotation.RGB,[exports.SyntaxFlag.LegacyRGB])}function rgbSpaceSeparated(e,o){return threeChannelSpaceSeparated(e,normalize_modern_sRGB_ChannelValues,exports.ColorNotation.RGB,[],o)}function XYZ_D50_to_sRGB_Gamut(e){const o=n.XYZ_D50_to_sRGB(e);if(n.inGamut(o))return n.clip(o);let a=e;return a=n.XYZ_D50_to_OKLCH(a),a[0]<1e-6&&(a=[0,0,0]),a[0]>.999999&&(a=[1,0,0]),n.gam_sRGB(n.mapGamutRayTrace(a,oklch_to_lin_srgb,lin_srgb_to_oklch))}function oklch_to_lin_srgb(e){return e=n.OKLCH_to_OKLab(e),e=n.OKLab_to_XYZ(e),n.XYZ_to_lin_sRGB(e)}function lin_srgb_to_oklch(e){return e=n.lin_sRGB_to_XYZ(e),e=n.XYZ_to_OKLab(e),n.OKLab_to_OKLCH(e)}function contrastColor(e,o){let a=!1;for(let n=0;nl?[1,1,1]:[0,0,0],r}function XYZ_D50_to_P3_Gamut(e){const o=n.XYZ_D50_to_P3(e);if(n.inGamut(o))return n.clip(o);let a=e;return a=n.XYZ_D50_to_OKLCH(a),a[0]<1e-6&&(a=[0,0,0]),a[0]>.999999&&(a=[1,0,0]),n.gam_P3(n.mapGamutRayTrace(a,oklch_to_lin_p3,lin_p3_to_oklch))}function oklch_to_lin_p3(e){return e=n.OKLCH_to_OKLab(e),e=n.OKLab_to_XYZ(e),n.XYZ_to_lin_P3(e)}function lin_p3_to_oklch(e){return e=n.lin_P3_to_XYZ(e),e=n.XYZ_to_OKLab(e),n.OKLab_to_OKLCH(e)}function toPrecision(e,o=7){e=+e,o=+o;const a=(Math.floor(Math.abs(e))+"").length;if(o>a)return+e.toFixed(o-a);{const n=10**(a-o);return Math.round(e/n)*n}}function serializeWithAlpha(e,o,n,r){const s=[a.TokenType.CloseParen,")",-1,-1,void 0];if("number"==typeof e.alpha){const l=Math.min(1,Math.max(0,toPrecision(Number.isNaN(e.alpha)?0:e.alpha)));return 1===toPrecision(l,4)?new t.FunctionNode(o,s,r):new t.FunctionNode(o,s,[...r,new t.WhitespaceNode([n]),new t.TokenNode([a.TokenType.Delim,"/",-1,-1,{value:"/"}]),new t.WhitespaceNode([n]),new t.TokenNode([a.TokenType.Number,toPrecision(l,4).toString(),-1,-1,{value:e.alpha,type:a.NumberType.Integer}])])}return new t.FunctionNode(o,s,[...r,new t.WhitespaceNode([n]),new t.TokenNode([a.TokenType.Delim,"/",-1,-1,{value:"/"}]),new t.WhitespaceNode([n]),e.alpha])}exports.color=function color(e){if(t.isFunctionNode(e)){switch(toLowerCaseAZ(e.getName())){case"rgb":case"rgba":return rgb(e,color);case"hsl":case"hsla":return hsl(e,color);case"hwb":return o=color,threeChannelSpaceSeparated(e,normalize_HWB_ChannelValues,exports.ColorNotation.HWB,[],o);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)}}var o;if(t.isTokenNode(e)){if(a.isTokenHash(e.value))return hex(e.value);if(a.isTokenIdent(e.value)){const o=namedColor(e.value[4].value);return!1!==o?o:"transparent"===toLowerCaseAZ(e.value[4].value)&&{colorNotation:exports.ColorNotation.RGB,channels:[0,0,0],alpha:0,syntaxFlags:new Set([exports.SyntaxFlag.ColorKeyword])}}}return!1},exports.colorDataFitsDisplayP3_Gamut=function colorDataFitsDisplayP3_Gamut(e){const o={...e,channels:[...e.channels]};return o.channels=convertPowerlessComponentsToZeroValuesForDisplay(o.channels,o.colorNotation),!colorDataTo(o,exports.ColorNotation.Display_P3).channels.find((e=>e<-1e-5||e>1.00001))},exports.colorDataFitsRGB_Gamut=function colorDataFitsRGB_Gamut(e){const o={...e,channels:[...e.channels]};return o.channels=convertPowerlessComponentsToZeroValuesForDisplay(o.channels,o.colorNotation),!colorDataTo(o,exports.ColorNotation.RGB).channels.find((e=>e<-1e-5||e>1.00001))},exports.serializeHSL=function serializeHSL(e,o=!0){e.channels=convertPowerlessComponentsToZeroValuesForDisplay(e.channels,e.colorNotation);let r=e.channels.map((e=>Number.isNaN(e)?0:e));r=o?n.XYZ_D50_to_HSL(n.sRGB_to_XYZ_D50(XYZ_D50_to_sRGB_Gamut(colorData_to_XYZ_D50(e).channels))):n.XYZ_D50_to_HSL(colorData_to_XYZ_D50(e).channels),r=r.map((e=>Number.isNaN(e)?0:e));const s=Math.min(360,Math.max(0,Math.round(toPrecision(r[0])))),l=Math.min(100,Math.max(0,Math.round(toPrecision(r[1])))),i=Math.min(100,Math.max(0,Math.round(toPrecision(r[2])))),u=[a.TokenType.CloseParen,")",-1,-1,void 0],c=[a.TokenType.Whitespace," ",-1,-1,void 0],p=[a.TokenType.Comma,",",-1,-1,void 0],N=[new t.TokenNode([a.TokenType.Number,s.toString(),-1,-1,{value:r[0],type:a.NumberType.Integer}]),new t.TokenNode(p),new t.WhitespaceNode([c]),new t.TokenNode([a.TokenType.Percentage,l.toString()+"%",-1,-1,{value:r[1]}]),new t.TokenNode(p),new t.WhitespaceNode([c]),new t.TokenNode([a.TokenType.Percentage,i.toString()+"%",-1,-1,{value:r[2]}])];if("number"==typeof e.alpha){const o=Math.min(1,Math.max(0,toPrecision(Number.isNaN(e.alpha)?0:e.alpha)));return 1===toPrecision(o,4)?new t.FunctionNode([a.TokenType.Function,"hsl(",-1,-1,{value:"hsl"}],u,N):new t.FunctionNode([a.TokenType.Function,"hsla(",-1,-1,{value:"hsla"}],u,[...N,new t.TokenNode(p),new t.WhitespaceNode([c]),new t.TokenNode([a.TokenType.Number,toPrecision(o,4).toString(),-1,-1,{value:e.alpha,type:a.NumberType.Number}])])}return new t.FunctionNode([a.TokenType.Function,"hsla(",-1,-1,{value:"hsla"}],u,[...N,new t.TokenNode(p),new t.WhitespaceNode([c]),e.alpha])},exports.serializeOKLCH=function serializeOKLCH(e){e.channels=convertPowerlessComponentsToZeroValuesForDisplay(e.channels,e.colorNotation);let o=e.channels.map((e=>Number.isNaN(e)?0:e));e.colorNotation!==exports.ColorNotation.OKLCH&&(o=n.XYZ_D50_to_OKLCH(colorData_to_XYZ_D50(e).channels));const r=toPrecision(o[0],6),s=toPrecision(o[1],6),l=toPrecision(o[2],6),i=[a.TokenType.Function,"oklch(",-1,-1,{value:"oklch"}],u=[a.TokenType.Whitespace," ",-1,-1,void 0];return serializeWithAlpha(e,i,u,[new t.TokenNode([a.TokenType.Number,r.toString(),-1,-1,{value:o[0],type:a.NumberType.Number}]),new t.WhitespaceNode([u]),new t.TokenNode([a.TokenType.Number,s.toString(),-1,-1,{value:o[1],type:a.NumberType.Number}]),new t.WhitespaceNode([u]),new t.TokenNode([a.TokenType.Number,l.toString(),-1,-1,{value:o[2],type:a.NumberType.Number}])])},exports.serializeP3=function serializeP3(e,o=!0){e.channels=convertPowerlessComponentsToZeroValuesForDisplay(e.channels,e.colorNotation);let r=e.channels.map((e=>Number.isNaN(e)?0:e));o?r=XYZ_D50_to_P3_Gamut(colorData_to_XYZ_D50(e).channels):e.colorNotation!==exports.ColorNotation.Display_P3&&(r=n.XYZ_D50_to_P3(colorData_to_XYZ_D50(e).channels));const s=o?Math.min(1,Math.max(0,toPrecision(r[0],6))):toPrecision(r[0],6),l=o?Math.min(1,Math.max(0,toPrecision(r[1],6))):toPrecision(r[1],6),i=o?Math.min(1,Math.max(0,toPrecision(r[2],6))):toPrecision(r[2],6),u=[a.TokenType.Function,"color(",-1,-1,{value:"color"}],c=[a.TokenType.Whitespace," ",-1,-1,void 0];return serializeWithAlpha(e,u,c,[new t.TokenNode([a.TokenType.Ident,"display-p3",-1,-1,{value:"display-p3"}]),new t.WhitespaceNode([c]),new t.TokenNode([a.TokenType.Number,s.toString(),-1,-1,{value:r[0],type:a.NumberType.Number}]),new t.WhitespaceNode([c]),new t.TokenNode([a.TokenType.Number,l.toString(),-1,-1,{value:r[1],type:a.NumberType.Number}]),new t.WhitespaceNode([c]),new t.TokenNode([a.TokenType.Number,i.toString(),-1,-1,{value:r[2],type:a.NumberType.Number}])])},exports.serializeRGB=function serializeRGB(e,o=!0){e.channels=convertPowerlessComponentsToZeroValuesForDisplay(e.channels,e.colorNotation);let r=e.channels.map((e=>Number.isNaN(e)?0:e));r=o?XYZ_D50_to_sRGB_Gamut(colorData_to_XYZ_D50(e).channels):n.XYZ_D50_to_sRGB(colorData_to_XYZ_D50(e).channels);const s=Math.min(255,Math.max(0,Math.round(255*toPrecision(r[0])))),l=Math.min(255,Math.max(0,Math.round(255*toPrecision(r[1])))),i=Math.min(255,Math.max(0,Math.round(255*toPrecision(r[2])))),u=[a.TokenType.CloseParen,")",-1,-1,void 0],c=[a.TokenType.Whitespace," ",-1,-1,void 0],p=[a.TokenType.Comma,",",-1,-1,void 0],N=[new t.TokenNode([a.TokenType.Number,s.toString(),-1,-1,{value:Math.min(255,255*Math.max(0,r[0])),type:a.NumberType.Integer}]),new t.TokenNode(p),new t.WhitespaceNode([c]),new t.TokenNode([a.TokenType.Number,l.toString(),-1,-1,{value:Math.min(255,255*Math.max(0,r[1])),type:a.NumberType.Integer}]),new t.TokenNode(p),new t.WhitespaceNode([c]),new t.TokenNode([a.TokenType.Number,i.toString(),-1,-1,{value:Math.min(255,255*Math.max(0,r[2])),type:a.NumberType.Integer}])];if("number"==typeof e.alpha){const o=Math.min(1,Math.max(0,toPrecision(Number.isNaN(e.alpha)?0:e.alpha)));return 1===toPrecision(o,4)?new t.FunctionNode([a.TokenType.Function,"rgb(",-1,-1,{value:"rgb"}],u,N):new t.FunctionNode([a.TokenType.Function,"rgba(",-1,-1,{value:"rgba"}],u,[...N,new t.TokenNode(p),new t.WhitespaceNode([c]),new t.TokenNode([a.TokenType.Number,toPrecision(o,4).toString(),-1,-1,{value:e.alpha,type:a.NumberType.Number}])])}return new t.FunctionNode([a.TokenType.Function,"rgba(",-1,-1,{value:"rgba"}],u,[...N,new t.TokenNode(p),new t.WhitespaceNode([c]),e.alpha])}; diff --git a/packages/css-color-parser/dist/index.d.ts b/packages/css-color-parser/dist/index.d.ts index 075ed6a63d..bc57e51f09 100644 --- a/packages/css-color-parser/dist/index.d.ts +++ b/packages/css-color-parser/dist/index.d.ts @@ -153,6 +153,8 @@ export declare enum SyntaxFlag { RelativeColorSyntax = "relative-color-syntax", /** Is a mixed color, e.g. `color-mix(in oklch, red, blue)` */ ColorMix = "color-mix", + /** Is a variadic mixed color, e.g. `color-mix(in oklch, red)` `color-mix(in oklch, red, blue, green)` */ + ColorMixVariadic = "color-mix-variadic", /** Is a contrasting color, e.g. `contrast-color()` */ ContrastColor = "contrast-color", /** Is an experimental color syntax */ diff --git a/packages/css-color-parser/dist/index.mjs b/packages/css-color-parser/dist/index.mjs index c70f49fc3f..a300075f25 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 t,isTokenNumeric as l,isTokenComma as s,isTokenDimension as u,isTokenHash as c}from"@csstools/css-tokenizer";import{XYZ_D50_to_XYZ_D65 as i,XYZ_D50_to_XYZ_D50 as h,XYZ_D50_to_OKLab as m,XYZ_D50_to_OKLCH as N,XYZ_D50_to_LCH as p,XYZ_D50_to_Lab as b,XYZ_D50_to_HWB as v,XYZ_D50_to_HSL as g,XYZ_D50_to_a98_RGB as f,XYZ_D50_to_ProPhoto as d,XYZ_D50_to_rec_2020 as y,XYZ_D50_to_P3 as _,XYZ_D50_to_lin_sRGB as C,XYZ_D50_to_sRGB as w,XYZ_D65_to_XYZ_D50 as x,OKLCH_to_XYZ_D50 as H,LCH_to_XYZ_D50 as L,OKLab_to_XYZ_D50 as k,Lab_to_XYZ_D50 as P,HWB_to_XYZ_D50 as S,HSL_to_XYZ_D50 as z,ProPhoto_RGB_to_XYZ_D50 as D,a98_RGB_to_XYZ_D50 as F,rec_2020_to_XYZ_D50 as Z,P3_to_XYZ_D50 as M,lin_sRGB_to_XYZ_D50 as R,sRGB_to_XYZ_D50 as B,namedColors as G,inGamut as T,clip as V,gam_sRGB as A,mapGamutRayTrace as X,OKLCH_to_OKLab as Y,OKLab_to_XYZ as K,XYZ_to_lin_sRGB as I,lin_sRGB_to_XYZ as O,XYZ_to_OKLab as E,OKLab_to_OKLCH as W,contrast_ratio_wcag_2_1 as U,gam_P3 as $,XYZ_to_lin_P3 as j,lin_P3_to_XYZ as q}from"@csstools/color-helpers";import{isWhitespaceNode as J,isCommentNode as Q,isTokenNode as ee,isFunctionNode as ae,TokenNode as ne,FunctionNode as re,WhitespaceNode as oe}from"@csstools/css-parser-algorithms";import{mathFunctionNames as te,calcFromComponentValues as le}from"@csstools/css-calc";var se,ue;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 se.HEX:case se.RGB:case se.sRGB:return{...e,colorNotation:se.XYZ_D50,channels:B(convertNaNToZero(e.channels))};case se.Linear_sRGB:return{...e,colorNotation:se.XYZ_D50,channels:R(convertNaNToZero(e.channels))};case se.Display_P3:return{...e,colorNotation:se.XYZ_D50,channels:M(convertNaNToZero(e.channels))};case se.Rec2020:return{...e,colorNotation:se.XYZ_D50,channels:Z(convertNaNToZero(e.channels))};case se.A98_RGB:return{...e,colorNotation:se.XYZ_D50,channels:F(convertNaNToZero(e.channels))};case se.ProPhoto_RGB:return{...e,colorNotation:se.XYZ_D50,channels:D(convertNaNToZero(e.channels))};case se.HSL:return{...e,colorNotation:se.XYZ_D50,channels:z(convertNaNToZero(e.channels))};case se.HWB:return{...e,colorNotation:se.XYZ_D50,channels:S(convertNaNToZero(e.channels))};case se.Lab:return{...e,colorNotation:se.XYZ_D50,channels:P(convertNaNToZero(e.channels))};case se.OKLab:return{...e,colorNotation:se.XYZ_D50,channels:k(convertNaNToZero(e.channels))};case se.LCH:return{...e,colorNotation:se.XYZ_D50,channels:L(convertNaNToZero(e.channels))};case se.OKLCH:return{...e,colorNotation:se.XYZ_D50,channels:H(convertNaNToZero(e.channels))};case se.XYZ_D50:return{...e,colorNotation:se.XYZ_D50,channels:h(convertNaNToZero(e.channels))};case se.XYZ_D65:return{...e,colorNotation:se.XYZ_D50,channels:x(convertNaNToZero(e.channels))};default:throw new Error("Unsupported color notation")}}!function(e){e.A98_RGB="a98-rgb",e.Display_P3="display-p3",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"}(se||(se={})),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.ContrastColor="contrast-color",e.Experimental="experimental"}(ue||(ue={}));const ce=new Set([se.A98_RGB,se.Display_P3,se.HEX,se.Linear_sRGB,se.ProPhoto_RGB,se.RGB,se.sRGB,se.Rec2020,se.XYZ_D50,se.XYZ_D65]);function colorDataTo(e,a){const n={...e};if(e.colorNotation!==a){const e=colorData_to_XYZ_D50(n);switch(a){case se.HEX:case se.RGB:n.colorNotation=se.RGB,n.channels=w(e.channels);break;case se.sRGB:n.colorNotation=se.sRGB,n.channels=w(e.channels);break;case se.Linear_sRGB:n.colorNotation=se.Linear_sRGB,n.channels=C(e.channels);break;case se.Display_P3:n.colorNotation=se.Display_P3,n.channels=_(e.channels);break;case se.Rec2020:n.colorNotation=se.Rec2020,n.channels=y(e.channels);break;case se.ProPhoto_RGB:n.colorNotation=se.ProPhoto_RGB,n.channels=d(e.channels);break;case se.A98_RGB:n.colorNotation=se.A98_RGB,n.channels=f(e.channels);break;case se.HSL:n.colorNotation=se.HSL,n.channels=g(e.channels);break;case se.HWB:n.colorNotation=se.HWB,n.channels=v(e.channels);break;case se.Lab:n.colorNotation=se.Lab,n.channels=b(e.channels);break;case se.LCH:n.colorNotation=se.LCH,n.channels=p(e.channels);break;case se.OKLCH:n.colorNotation=se.OKLCH,n.channels=N(e.channels);break;case se.OKLab:n.colorNotation=se.OKLab,n.channels=m(e.channels);break;case se.XYZ_D50:n.colorNotation=se.XYZ_D50,n.channels=h(e.channels);break;case se.XYZ_D65:n.colorNotation=se.XYZ_D65,n.channels=i(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(ce.has(a)&&ce.has(e.colorNotation))n.channels=carryForwardMissingComponents(e.channels,[0,1,2],n.channels,[0,1,2]);else switch(a){case se.HSL:switch(e.colorNotation){case se.HWB:n.channels=carryForwardMissingComponents(e.channels,[0],n.channels,[0]);break;case se.Lab:case se.OKLab:n.channels=carryForwardMissingComponents(e.channels,[2],n.channels,[0]);break;case se.LCH:case se.OKLCH:n.channels=carryForwardMissingComponents(e.channels,[0,1,2],n.channels,[2,1,0])}break;case se.HWB:switch(e.colorNotation){case se.HSL:n.channels=carryForwardMissingComponents(e.channels,[0],n.channels,[0]);break;case se.LCH:case se.OKLCH:n.channels=carryForwardMissingComponents(e.channels,[0],n.channels,[2])}break;case se.Lab:case se.OKLab:switch(e.colorNotation){case se.HSL:n.channels=carryForwardMissingComponents(e.channels,[0],n.channels,[2]);break;case se.Lab:case se.OKLab:n.channels=carryForwardMissingComponents(e.channels,[0,1,2],n.channels,[0,1,2]);break;case se.LCH:case se.OKLCH:n.channels=carryForwardMissingComponents(e.channels,[0],n.channels,[0])}break;case se.LCH:case se.OKLCH:switch(e.colorNotation){case se.HSL:n.channels=carryForwardMissingComponents(e.channels,[0,1,2],n.channels,[2,1,0]);break;case se.HWB:n.channels=carryForwardMissingComponents(e.channels,[0],n.channels,[2]);break;case se.Lab:case se.OKLab:n.channels=carryForwardMissingComponents(e.channels,[0],n.channels,[0]);break;case se.LCH:case se.OKLCH:n.channels=carryForwardMissingComponents(e.channels,[0,1,2],n.channels,[0,1,2])}}return n.channels=convertPowerlessComponentsToMissingComponents(n.channels,a),n}function convertPowerlessComponentsToMissingComponents(e,a){const n=[...e];switch(a){case se.HSL:!Number.isNaN(n[1])&&reducePrecision(n[1],4)<=0&&(n[0]=Number.NaN);break;case se.HWB:Math.max(0,reducePrecision(n[1],4))+Math.max(0,reducePrecision(n[2],4))>=100&&(n[0]=Number.NaN);break;case se.LCH:!Number.isNaN(n[1])&&reducePrecision(n[1],4)<=0&&(n[2]=Number.NaN);break;case se.OKLCH:!Number.isNaN(n[1])&&reducePrecision(n[1],6)<=0&&(n[2]=Number.NaN)}return n}function convertPowerlessComponentsToZeroValuesForDisplay(e,a){const n=[...e];switch(a){case se.HSL:(reducePrecision(n[2])<=0||reducePrecision(n[2])>=100)&&(n[0]=Number.NaN,n[1]=Number.NaN),reducePrecision(n[1])<=0&&(n[0]=Number.NaN);break;case se.HWB:Math.max(0,reducePrecision(n[1]))+Math.max(0,reducePrecision(n[2]))>=100&&(n[0]=Number.NaN);break;case se.Lab:(reducePrecision(n[0])<=0||reducePrecision(n[0])>=100)&&(n[1]=Number.NaN,n[2]=Number.NaN);break;case se.LCH:reducePrecision(n[1])<=0&&(n[2]=Number.NaN),(reducePrecision(n[0])<=0||reducePrecision(n[0])>=100)&&(n[1]=Number.NaN,n[2]=Number.NaN);break;case se.OKLab:(reducePrecision(n[0])<=0||reducePrecision(n[0])>=1)&&(n[1]=Number.NaN,n[2]=Number.NaN);break;case se.OKLCH:reducePrecision(n[1])<=0&&(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){const o=[...n];for(const n of a)Number.isNaN(e[a[n]])&&(o[r[n]]=Number.NaN);return o}function normalizeRelativeColorDataChannels(e){const a=new Map;switch(e.colorNotation){case se.RGB:case se.HEX:a.set("r",dummyNumberToken(255*e.channels[0])),a.set("g",dummyNumberToken(255*e.channels[1])),a.set("b",dummyNumberToken(255*e.channels[2])),"number"==typeof e.alpha&&a.set("alpha",dummyNumberToken(e.alpha));break;case se.HSL:a.set("h",dummyNumberToken(e.channels[0])),a.set("s",dummyNumberToken(e.channels[1])),a.set("l",dummyNumberToken(e.channels[2])),"number"==typeof e.alpha&&a.set("alpha",dummyNumberToken(e.alpha));break;case se.HWB:a.set("h",dummyNumberToken(e.channels[0])),a.set("w",dummyNumberToken(e.channels[1])),a.set("b",dummyNumberToken(e.channels[2])),"number"==typeof e.alpha&&a.set("alpha",dummyNumberToken(e.alpha));break;case se.Lab:case se.OKLab:a.set("l",dummyNumberToken(e.channels[0])),a.set("a",dummyNumberToken(e.channels[1])),a.set("b",dummyNumberToken(e.channels[2])),"number"==typeof e.alpha&&a.set("alpha",dummyNumberToken(e.alpha));break;case se.LCH:case se.OKLCH:a.set("l",dummyNumberToken(e.channels[0])),a.set("c",dummyNumberToken(e.channels[1])),a.set("h",dummyNumberToken(e.channels[2])),"number"==typeof e.alpha&&a.set("alpha",dummyNumberToken(e.alpha));break;case se.sRGB:case se.A98_RGB:case se.Display_P3:case se.Rec2020:case se.Linear_sRGB:case se.ProPhoto_RGB:a.set("r",dummyNumberToken(e.channels[0])),a.set("g",dummyNumberToken(e.channels[1])),a.set("b",dummyNumberToken(e.channels[2])),"number"==typeof e.alpha&&a.set("alpha",dummyNumberToken(e.alpha));break;case se.XYZ_D50:case se.XYZ_D65:a.set("x",dummyNumberToken(e.channels[0])),a.set("y",dummyNumberToken(e.channels[1])),a.set("z",dummyNumberToken(e.channels[2])),"number"==typeof e.alpha&&a.set("alpha",dummyNumberToken(e.alpha))}return a}function noneToZeroInRelativeColorDataChannels(e){const a=new Map(e);for(const[n,r]of e)Number.isNaN(r[4].value)&&a.set(n,dummyNumberToken(0));return a}function dummyNumberToken(n){return Number.isNaN(n)?[e.Number,"none",-1,-1,{value:Number.NaN,type:a.Number}]:[e.Number,n.toString(),-1,-1,{value:n,type:a.Number}]}function reducePrecision(e,a=7){if(Number.isNaN(e))return 0;const n=Math.pow(10,a);return Math.round(e*n)/n}function colorDataFitsRGB_Gamut(e){const a={...e,channels:[...e.channels]};a.channels=convertPowerlessComponentsToZeroValuesForDisplay(a.channels,a.colorNotation);return!colorDataTo(a,se.RGB).channels.find((e=>e<-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,se.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 ie=/[A-Z]/g;function toLowerCaseAZ(e){return e.replace(ie,(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(ue.HasNoneKeywords),[e.Number,"none",t[2],t[3],{value:Number.NaN,type:a.Number}];if(r(t)){3!==l&&s.syntaxFlags.add(ue.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(ue.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 he=new Set(["srgb","srgb-linear","display-p3","a98-rgb","prophoto-rgb","rec2020","xyz","xyz-d50","xyz-d65"]);function color$1(e,a){const r=[],s=[],u=[],c=[];let i,h,m=!1,N=!1;const p={colorNotation:se.sRGB,channels:[0,0,0],alpha:1,syntaxFlags:new Set([])};let b=r;for(let o=0;o=0){u=c.value[4].value;continue}}return!1}if(!t)return!1;n.push({color:t,percentage:u}),t=!1,u=!1}}if(t&&n.push({color:t,percentage:u}),2!==n.length)return!1;let c=n[0].percentage,i=n[1].percentage;return(!1===c||!(c<0||c>100))&&((!1===i||!(i<0||i>100))&&(!1===c&&!1===i?(c=50,i=50):!1!==c&&!1===i?i=100-c:!1===c&&!1!==i&&(c=100-i),(0!==c||0!==i)&&(!1!==c&&!1!==i&&(c+i>100&&(c=c/(c+i)*100,i=i/(c+i)*100),c+i<100&&(o=(c+i)/100,c=c/(c+i)*100,i=i/(c+i)*100),{a:{color:n[0].color,percentage:c},b:{color:n[1].color,percentage:i},alphaMultiplier:o}))))}function colorMixRectangular(e,a){if(!a)return!1;const n=a.a.color,r=a.b.color,o=a.a.percentage/100;let t=n.channels,l=r.channels,s=se.RGB,u=n.alpha;if("number"!=typeof u)return!1;let c=r.alpha;if("number"!=typeof c)return!1;switch(u=Number.isNaN(u)?c:u,c=Number.isNaN(c)?u:c,e){case"srgb":s=se.RGB;break;case"srgb-linear":s=se.Linear_sRGB;break;case"display-p3":s=se.Display_P3;break;case"a98-rgb":s=se.A98_RGB;break;case"prophoto-rgb":s=se.ProPhoto_RGB;break;case"rec2020":s=se.Rec2020;break;case"lab":s=se.Lab;break;case"oklab":s=se.OKLab;break;case"xyz-d50":s=se.XYZ_D50;break;case"xyz":case"xyz-d65":s=se.XYZ_D65}t=colorDataTo(n,s).channels,l=colorDataTo(r,s).channels,t[0]=fillInMissingComponent(t[0],l[0]),l[0]=fillInMissingComponent(l[0],t[0]),t[1]=fillInMissingComponent(t[1],l[1]),l[1]=fillInMissingComponent(l[1],t[1]),t[2]=fillInMissingComponent(t[2],l[2]),l[2]=fillInMissingComponent(l[2],t[2]),t[0]=premultiply(t[0],u),t[1]=premultiply(t[1],u),t[2]=premultiply(t[2],u),l[0]=premultiply(l[0],c),l[1]=premultiply(l[1],c),l[2]=premultiply(l[2],c);const i=interpolate(u,c,o),h={colorNotation:s,channels:[un_premultiply(interpolate(t[0],l[0],o),i),un_premultiply(interpolate(t[1],l[1],o),i),un_premultiply(interpolate(t[2],l[2],o),i)],alpha:i*a.alphaMultiplier,syntaxFlags:new Set([ue.ColorMix])};return(a.a.color.syntaxFlags.has(ue.Experimental)||a.b.color.syntaxFlags.has(ue.Experimental))&&h.syntaxFlags.add(ue.Experimental),h}function colorMixPolar(e,a,n){if(!n)return!1;const r=n.a.color,o=n.b.color,t=n.a.percentage/100;let l=r.channels,s=o.channels,u=0,c=0,i=0,h=0,m=0,N=0,p=se.RGB,b=r.alpha;if("number"!=typeof b)return!1;let v=o.alpha;if("number"!=typeof v)return!1;switch(b=Number.isNaN(b)?v:b,v=Number.isNaN(v)?b:v,e){case"hsl":p=se.HSL;break;case"hwb":p=se.HWB;break;case"lch":p=se.LCH;break;case"oklch":p=se.OKLCH}switch(l=colorDataTo(r,p).channels,s=colorDataTo(o,p).channels,e){case"hsl":case"hwb":u=l[0],c=s[0],i=l[1],h=s[1],m=l[2],N=s[2];break;case"lch":case"oklch":i=l[0],h=s[0],m=l[1],N=s[1],u=l[2],c=s[2]}u=fillInMissingComponent(u,c),Number.isNaN(u)&&(u=0),c=fillInMissingComponent(c,u),Number.isNaN(c)&&(c=0),i=fillInMissingComponent(i,h),h=fillInMissingComponent(h,i),m=fillInMissingComponent(m,N),N=fillInMissingComponent(N,m);const g=c-u;switch(a){case"shorter":g>180?u+=360:g<-180&&(c+=360);break;case"longer":-1800?u+=360:c+=360);break;case"increasing":g<0&&(c+=360);break;case"decreasing":g>0&&(u+=360);break;default:throw new Error("Unknown hue interpolation method")}i=premultiply(i,b),m=premultiply(m,b),h=premultiply(h,v),N=premultiply(N,v);let f=[0,0,0];const d=interpolate(b,v,t);switch(e){case"hsl":case"hwb":f=[interpolate(u,c,t),un_premultiply(interpolate(i,h,t),d),un_premultiply(interpolate(m,N,t),d)];break;case"lch":case"oklch":f=[un_premultiply(interpolate(i,h,t),d),un_premultiply(interpolate(m,N,t),d),interpolate(u,c,t)]}const y={colorNotation:p,channels:f,alpha:d*n.alphaMultiplier,syntaxFlags:new Set([ue.ColorMix])};return(n.a.color.syntaxFlags.has(ue.Experimental)||n.b.color.syntaxFlags.has(ue.Experimental))&&y.syntaxFlags.add(ue.Experimental),y}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:se.HEX,channels:[0,0,0],alpha:1,syntaxFlags:new Set([ue.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(ue.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(ue.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(ue.HasDimensionValues),e)}if(r(n)){3===t?l.syntaxFlags.add(ue.HasPercentageAlpha):l.syntaxFlags.add(ue.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(ue.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(ue.HasDimensionValues),e)}if(r(t)){3===l?s.syntaxFlags.add(ue.HasPercentageAlpha):s.syntaxFlags.add(ue.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(ue.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=[],c=[],i=[],h={colorNotation:n,channels:[0,0,0],alpha:1,syntaxFlags:new Set(r)};let m=t;for(let a=0;aee(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,se.HSL,[ue.LegacyHSL])}function hslSpaceSeparated(e,a){return threeChannelSpaceSeparated(e,normalize_modern_HSL_ChannelValues,se.HSL,[],a)}function normalize_HWB_ChannelValues(t,l,s){if(n(t)&&"none"===toLowerCaseAZ(t[4].value))return s.syntaxFlags.add(ue.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(ue.HasDimensionValues),e)}if(r(t)){3===l?s.syntaxFlags.add(ue.HasPercentageAlpha):s.syntaxFlags.add(ue.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(ue.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(ue.HasNoneKeywords),[e.Number,"none",t[2],t[3],{value:Number.NaN,type:a.Number}];if(r(t)){3!==l&&s.syntaxFlags.add(ue.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(ue.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,se.Lab,[],a)}function normalize_LCH_ChannelValues(t,l,s){if(n(t)&&"none"===toLowerCaseAZ(t[4].value))return s.syntaxFlags.add(ue.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(ue.HasDimensionValues),e)}if(r(t)){3!==l&&s.syntaxFlags.add(ue.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(ue.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,se.LCH,[],a)}const be=new Map;for(const[e,a]of Object.entries(G))be.set(e,a);function namedColor(e){const a=be.get(toLowerCaseAZ(e));return!!a&&{colorNotation:se.RGB,channels:[a[0]/255,a[1]/255,a[2]/255],alpha:1,syntaxFlags:new Set([ue.ColorKeyword,ue.NamedColor])}}function normalize_OKLab_ChannelValues(t,l,s){if(n(t)&&"none"===toLowerCaseAZ(t[4].value))return s.syntaxFlags.add(ue.HasNoneKeywords),[e.Number,"none",t[2],t[3],{value:Number.NaN,type:a.Number}];if(r(t)){3!==l&&s.syntaxFlags.add(ue.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(ue.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,se.OKLab,[],a)}function normalize_OKLCH_ChannelValues(t,l,s){if(n(t)&&"none"===toLowerCaseAZ(t[4].value))return s.syntaxFlags.add(ue.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(ue.HasDimensionValues),e)}if(r(t)){3!==l&&s.syntaxFlags.add(ue.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(ue.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,se.OKLCH,[],a)}function normalize_legacy_sRGB_ChannelValues(n,t,l){if(r(n)){3===t?l.syntaxFlags.add(ue.HasPercentageAlpha):l.syntaxFlags.add(ue.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(ue.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(ue.HasNoneKeywords),[e.Number,"none",t[2],t[3],{value:Number.NaN,type:a.Number}];if(r(t)){3!==l&&s.syntaxFlags.add(ue.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(ue.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=>ee(e)&&s(e.value)))){const a=rgbCommaSeparated(e);if(!1!==a)return(!a.syntaxFlags.has(ue.HasNumberValues)||!a.syntaxFlags.has(ue.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,se.RGB,[ue.LegacyRGB])}function rgbSpaceSeparated(e,a){return threeChannelSpaceSeparated(e,normalize_modern_sRGB_ChannelValues,se.RGB,[],a)}function XYZ_D50_to_sRGB_Gamut(e){const a=w(e);if(T(a))return V(a);let n=e;return n=N(n),n[0]<1e-6&&(n=[0,0,0]),n[0]>.999999&&(n=[1,0,0]),A(X(n,oklch_to_lin_srgb,lin_srgb_to_oklch))}function oklch_to_lin_srgb(e){return e=Y(e),e=K(e),I(e)}function lin_srgb_to_oklch(e){return e=O(e),e=E(e),W(e)}function contrastColor(e,a){let n=!1;for(let r=0;rt?[1,1,1]:[0,0,0],r}function XYZ_D50_to_P3_Gamut(e){const a=_(e);if(T(a))return V(a);let n=e;return n=N(n),n[0]<1e-6&&(n=[0,0,0]),n[0]>.999999&&(n=[1,0,0]),$(X(n,oklch_to_lin_p3,lin_p3_to_oklch))}function oklch_to_lin_p3(e){return e=Y(e),e=K(e),j(e)}function lin_p3_to_oklch(e){return e=q(e),e=E(e),W(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 re(r,l,t):new re(r,l,[...t,new oe([o]),new ne([e.Delim,"/",-1,-1,{value:"/"}]),new oe([o]),new ne([e.Number,toPrecision(s,4).toString(),-1,-1,{value:n.alpha,type:a.Integer}])])}return new re(r,l,[...t,new oe([o]),new ne([e.Delim,"/",-1,-1,{value:"/"}]),new oe([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!==se.Display_P3&&(o=_(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"}],c=[e.Whitespace," ",-1,-1,void 0];return serializeWithAlpha(n,u,c,[new ne([e.Ident,"display-p3",-1,-1,{value:"display-p3"}]),new oe([c]),new ne([e.Number,t.toString(),-1,-1,{value:o[0],type:a.Number}]),new oe([c]),new ne([e.Number,l.toString(),-1,-1,{value:o[1],type:a.Number}]),new oe([c]),new ne([e.Number,s.toString(),-1,-1,{value:o[2],type:a.Number}])])}function serializeRGB(n,r=!0){n.channels=convertPowerlessComponentsToZeroValuesForDisplay(n.channels,n.colorNotation);let o=n.channels.map((e=>Number.isNaN(e)?0:e));o=r?XYZ_D50_to_sRGB_Gamut(colorData_to_XYZ_D50(n).channels):w(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],c=[e.Whitespace," ",-1,-1,void 0],i=[e.Comma,",",-1,-1,void 0],h=[new ne([e.Number,t.toString(),-1,-1,{value:Math.min(255,255*Math.max(0,o[0])),type:a.Integer}]),new ne(i),new oe([c]),new ne([e.Number,l.toString(),-1,-1,{value:Math.min(255,255*Math.max(0,o[1])),type:a.Integer}]),new ne(i),new oe([c]),new ne([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 re([e.Function,"rgb(",-1,-1,{value:"rgb"}],u,h):new re([e.Function,"rgba(",-1,-1,{value:"rgba"}],u,[...h,new ne(i),new oe([c]),new ne([e.Number,toPrecision(r,4).toString(),-1,-1,{value:n.alpha,type:a.Number}])])}return new re([e.Function,"rgba(",-1,-1,{value:"rgba"}],u,[...h,new ne(i),new oe([c]),n.alpha])}function serializeHSL(n,r=!0){n.channels=convertPowerlessComponentsToZeroValuesForDisplay(n.channels,n.colorNotation);let o=n.channels.map((e=>Number.isNaN(e)?0:e));o=g(r?B(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],c=[e.Whitespace," ",-1,-1,void 0],i=[e.Comma,",",-1,-1,void 0],h=[new ne([e.Number,t.toString(),-1,-1,{value:o[0],type:a.Integer}]),new ne(i),new oe([c]),new ne([e.Percentage,l.toString()+"%",-1,-1,{value:o[1]}]),new ne(i),new oe([c]),new ne([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 re([e.Function,"hsl(",-1,-1,{value:"hsl"}],u,h):new re([e.Function,"hsla(",-1,-1,{value:"hsla"}],u,[...h,new ne(i),new oe([c]),new ne([e.Number,toPrecision(r,4).toString(),-1,-1,{value:n.alpha,type:a.Number}])])}return new re([e.Function,"hsla(",-1,-1,{value:"hsla"}],u,[...h,new ne(i),new oe([c]),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!==se.OKLCH&&(r=N(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 ne([e.Number,o.toString(),-1,-1,{value:r[0],type:a.Number}]),new oe([u]),new ne([e.Number,t.toString(),-1,-1,{value:r[1],type:a.Number}]),new oe([u]),new ne([e.Number,l.toString(),-1,-1,{value:r[2],type:a.Number}])])}function color(e){if(ae(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,se.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)}}var a;if(ee(e)){if(c(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:se.RGB,channels:[0,0,0],alpha:0,syntaxFlags:new Set([ue.ColorKeyword])}}}return!1}export{se as ColorNotation,ue 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 c}from"@csstools/css-tokenizer";import{XYZ_D50_to_XYZ_D65 as i,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 f,XYZ_D50_to_a98_RGB as v,XYZ_D50_to_ProPhoto as d,XYZ_D50_to_rec_2020 as y,XYZ_D50_to_P3 as _,XYZ_D50_to_lin_sRGB as C,XYZ_D50_to_sRGB as w,XYZ_D65_to_XYZ_D50 as x,OKLCH_to_XYZ_D50 as H,LCH_to_XYZ_D50 as L,OKLab_to_XYZ_D50 as P,Lab_to_XYZ_D50 as k,HWB_to_XYZ_D50 as S,HSL_to_XYZ_D50 as M,ProPhoto_RGB_to_XYZ_D50 as z,a98_RGB_to_XYZ_D50 as F,rec_2020_to_XYZ_D50 as D,P3_to_XYZ_D50 as Z,lin_sRGB_to_XYZ_D50 as R,sRGB_to_XYZ_D50 as B,namedColors as V,inGamut as G,clip as T,gam_sRGB as A,mapGamutRayTrace as X,OKLCH_to_OKLab as K,OKLab_to_XYZ as Y,XYZ_to_lin_sRGB as I,lin_sRGB_to_XYZ as O,XYZ_to_OKLab as W,OKLab_to_OKLCH as E,contrast_ratio_wcag_2_1 as U,gam_P3 as $,XYZ_to_lin_P3 as j,lin_P3_to_XYZ as q}from"@csstools/color-helpers";import{isWhitespaceNode as J,isCommentNode as Q,isTokenNode as ee,isFunctionNode as ae,TokenNode as ne,isWhiteSpaceOrCommentNode as re,FunctionNode as oe,WhitespaceNode as te}from"@csstools/css-parser-algorithms";import{mathFunctionNames as le,calcFromComponentValues as se}from"@csstools/css-calc";var ue,ce;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 ue.HEX:case ue.RGB:case ue.sRGB:return{...e,colorNotation:ue.XYZ_D50,channels:B(convertNaNToZero(e.channels))};case ue.Linear_sRGB:return{...e,colorNotation:ue.XYZ_D50,channels:R(convertNaNToZero(e.channels))};case ue.Display_P3:return{...e,colorNotation:ue.XYZ_D50,channels:Z(convertNaNToZero(e.channels))};case ue.Rec2020:return{...e,colorNotation:ue.XYZ_D50,channels:D(convertNaNToZero(e.channels))};case ue.A98_RGB:return{...e,colorNotation:ue.XYZ_D50,channels:F(convertNaNToZero(e.channels))};case ue.ProPhoto_RGB:return{...e,colorNotation:ue.XYZ_D50,channels:z(convertNaNToZero(e.channels))};case ue.HSL:return{...e,colorNotation:ue.XYZ_D50,channels:M(convertNaNToZero(e.channels))};case ue.HWB:return{...e,colorNotation:ue.XYZ_D50,channels:S(convertNaNToZero(e.channels))};case ue.Lab:return{...e,colorNotation:ue.XYZ_D50,channels:k(convertNaNToZero(e.channels))};case ue.OKLab:return{...e,colorNotation:ue.XYZ_D50,channels:P(convertNaNToZero(e.channels))};case ue.LCH:return{...e,colorNotation:ue.XYZ_D50,channels:L(convertNaNToZero(e.channels))};case ue.OKLCH:return{...e,colorNotation:ue.XYZ_D50,channels:H(convertNaNToZero(e.channels))};case ue.XYZ_D50:return{...e,colorNotation:ue.XYZ_D50,channels:h(convertNaNToZero(e.channels))};case ue.XYZ_D65:return{...e,colorNotation:ue.XYZ_D50,channels:x(convertNaNToZero(e.channels))};default:throw new Error("Unsupported color notation")}}!function(e){e.A98_RGB="a98-rgb",e.Display_P3="display-p3",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"}(ue||(ue={})),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.Experimental="experimental"}(ce||(ce={}));const ie=new Set([ue.A98_RGB,ue.Display_P3,ue.HEX,ue.Linear_sRGB,ue.ProPhoto_RGB,ue.RGB,ue.sRGB,ue.Rec2020,ue.XYZ_D50,ue.XYZ_D65]);function colorDataTo(e,a){const n={...e};if(e.colorNotation!==a){const e=colorData_to_XYZ_D50(n);switch(a){case ue.HEX:case ue.RGB:n.colorNotation=ue.RGB,n.channels=w(e.channels);break;case ue.sRGB:n.colorNotation=ue.sRGB,n.channels=w(e.channels);break;case ue.Linear_sRGB:n.colorNotation=ue.Linear_sRGB,n.channels=C(e.channels);break;case ue.Display_P3:n.colorNotation=ue.Display_P3,n.channels=_(e.channels);break;case ue.Rec2020:n.colorNotation=ue.Rec2020,n.channels=y(e.channels);break;case ue.ProPhoto_RGB:n.colorNotation=ue.ProPhoto_RGB,n.channels=d(e.channels);break;case ue.A98_RGB:n.colorNotation=ue.A98_RGB,n.channels=v(e.channels);break;case ue.HSL:n.colorNotation=ue.HSL,n.channels=f(e.channels);break;case ue.HWB:n.colorNotation=ue.HWB,n.channels=g(e.channels);break;case ue.Lab:n.colorNotation=ue.Lab,n.channels=b(e.channels);break;case ue.LCH:n.colorNotation=ue.LCH,n.channels=N(e.channels);break;case ue.OKLCH:n.colorNotation=ue.OKLCH,n.channels=p(e.channels);break;case ue.OKLab:n.colorNotation=ue.OKLab,n.channels=m(e.channels);break;case ue.XYZ_D50:n.colorNotation=ue.XYZ_D50,n.channels=h(e.channels);break;case ue.XYZ_D65:n.colorNotation=ue.XYZ_D65,n.channels=i(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(ie.has(a)&&ie.has(e.colorNotation))n.channels=carryForwardMissingComponents(e.channels,[0,1,2],n.channels,[0,1,2]);else switch(a){case ue.HSL:switch(e.colorNotation){case ue.HWB:n.channels=carryForwardMissingComponents(e.channels,[0],n.channels,[0]);break;case ue.Lab:case ue.OKLab:n.channels=carryForwardMissingComponents(e.channels,[2],n.channels,[0]);break;case ue.LCH:case ue.OKLCH:n.channels=carryForwardMissingComponents(e.channels,[0,1,2],n.channels,[2,1,0])}break;case ue.HWB:switch(e.colorNotation){case ue.HSL:n.channels=carryForwardMissingComponents(e.channels,[0],n.channels,[0]);break;case ue.LCH:case ue.OKLCH:n.channels=carryForwardMissingComponents(e.channels,[0],n.channels,[2])}break;case ue.Lab:case ue.OKLab:switch(e.colorNotation){case ue.HSL:n.channels=carryForwardMissingComponents(e.channels,[0],n.channels,[2]);break;case ue.Lab:case ue.OKLab:n.channels=carryForwardMissingComponents(e.channels,[0,1,2],n.channels,[0,1,2]);break;case ue.LCH:case ue.OKLCH:n.channels=carryForwardMissingComponents(e.channels,[0],n.channels,[0])}break;case ue.LCH:case ue.OKLCH:switch(e.colorNotation){case ue.HSL:n.channels=carryForwardMissingComponents(e.channels,[0,1,2],n.channels,[2,1,0]);break;case ue.HWB:n.channels=carryForwardMissingComponents(e.channels,[0],n.channels,[2]);break;case ue.Lab:case ue.OKLab:n.channels=carryForwardMissingComponents(e.channels,[0],n.channels,[0]);break;case ue.LCH:case ue.OKLCH:n.channels=carryForwardMissingComponents(e.channels,[0,1,2],n.channels,[0,1,2])}}return n.channels=convertPowerlessComponentsToMissingComponents(n.channels,a),n}function convertPowerlessComponentsToMissingComponents(e,a){const n=[...e];switch(a){case ue.HSL:!Number.isNaN(n[1])&&reducePrecision(n[1],4)<=0&&(n[0]=Number.NaN);break;case ue.HWB:Math.max(0,reducePrecision(n[1],4))+Math.max(0,reducePrecision(n[2],4))>=100&&(n[0]=Number.NaN);break;case ue.LCH:!Number.isNaN(n[1])&&reducePrecision(n[1],4)<=0&&(n[2]=Number.NaN);break;case ue.OKLCH:!Number.isNaN(n[1])&&reducePrecision(n[1],6)<=0&&(n[2]=Number.NaN)}return n}function convertPowerlessComponentsToZeroValuesForDisplay(e,a){const n=[...e];switch(a){case ue.HSL:(reducePrecision(n[2])<=0||reducePrecision(n[2])>=100)&&(n[0]=Number.NaN,n[1]=Number.NaN),reducePrecision(n[1])<=0&&(n[0]=Number.NaN);break;case ue.HWB:Math.max(0,reducePrecision(n[1]))+Math.max(0,reducePrecision(n[2]))>=100&&(n[0]=Number.NaN);break;case ue.Lab:(reducePrecision(n[0])<=0||reducePrecision(n[0])>=100)&&(n[1]=Number.NaN,n[2]=Number.NaN);break;case ue.LCH:reducePrecision(n[1])<=0&&(n[2]=Number.NaN),(reducePrecision(n[0])<=0||reducePrecision(n[0])>=100)&&(n[1]=Number.NaN,n[2]=Number.NaN);break;case ue.OKLab:(reducePrecision(n[0])<=0||reducePrecision(n[0])>=1)&&(n[1]=Number.NaN,n[2]=Number.NaN);break;case ue.OKLCH:reducePrecision(n[1])<=0&&(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){const o=[...n];for(const n of a)Number.isNaN(e[a[n]])&&(o[r[n]]=Number.NaN);return o}function normalizeRelativeColorDataChannels(e){const a=new Map;switch(e.colorNotation){case ue.RGB:case ue.HEX:a.set("r",dummyNumberToken(255*e.channels[0])),a.set("g",dummyNumberToken(255*e.channels[1])),a.set("b",dummyNumberToken(255*e.channels[2])),"number"==typeof e.alpha&&a.set("alpha",dummyNumberToken(e.alpha));break;case ue.HSL:a.set("h",dummyNumberToken(e.channels[0])),a.set("s",dummyNumberToken(e.channels[1])),a.set("l",dummyNumberToken(e.channels[2])),"number"==typeof e.alpha&&a.set("alpha",dummyNumberToken(e.alpha));break;case ue.HWB:a.set("h",dummyNumberToken(e.channels[0])),a.set("w",dummyNumberToken(e.channels[1])),a.set("b",dummyNumberToken(e.channels[2])),"number"==typeof e.alpha&&a.set("alpha",dummyNumberToken(e.alpha));break;case ue.Lab:case ue.OKLab:a.set("l",dummyNumberToken(e.channels[0])),a.set("a",dummyNumberToken(e.channels[1])),a.set("b",dummyNumberToken(e.channels[2])),"number"==typeof e.alpha&&a.set("alpha",dummyNumberToken(e.alpha));break;case ue.LCH:case ue.OKLCH:a.set("l",dummyNumberToken(e.channels[0])),a.set("c",dummyNumberToken(e.channels[1])),a.set("h",dummyNumberToken(e.channels[2])),"number"==typeof e.alpha&&a.set("alpha",dummyNumberToken(e.alpha));break;case ue.sRGB:case ue.A98_RGB:case ue.Display_P3:case ue.Rec2020:case ue.Linear_sRGB:case ue.ProPhoto_RGB:a.set("r",dummyNumberToken(e.channels[0])),a.set("g",dummyNumberToken(e.channels[1])),a.set("b",dummyNumberToken(e.channels[2])),"number"==typeof e.alpha&&a.set("alpha",dummyNumberToken(e.alpha));break;case ue.XYZ_D50:case ue.XYZ_D65:a.set("x",dummyNumberToken(e.channels[0])),a.set("y",dummyNumberToken(e.channels[1])),a.set("z",dummyNumberToken(e.channels[2])),"number"==typeof e.alpha&&a.set("alpha",dummyNumberToken(e.alpha))}return a}function noneToZeroInRelativeColorDataChannels(e){const a=new Map(e);for(const[n,r]of e)Number.isNaN(r[4].value)&&a.set(n,dummyNumberToken(0));return a}function dummyNumberToken(n){return Number.isNaN(n)?[e.Number,"none",-1,-1,{value:Number.NaN,type:a.Number}]:[e.Number,n.toString(),-1,-1,{value:n,type:a.Number}]}function reducePrecision(e,a=7){if(Number.isNaN(e))return 0;const n=Math.pow(10,a);return Math.round(e*n)/n}function colorDataFitsRGB_Gamut(e){const a={...e,channels:[...e.channels]};a.channels=convertPowerlessComponentsToZeroValuesForDisplay(a.channels,a.colorNotation);return!colorDataTo(a,ue.RGB).channels.find((e=>e<-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,ue.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 he=/[A-Z]/g;function toLowerCaseAZ(e){return e.replace(he,(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(ce.HasNoneKeywords),[e.Number,"none",t[2],t[3],{value:Number.NaN,type:a.Number}];if(r(t)){3!==l&&s.syntaxFlags.add(ce.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(ce.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 me=new Set(["srgb","srgb-linear","display-p3","a98-rgb","prophoto-rgb","rec2020","xyz","xyz-d50","xyz-d65"]);function color$1(e,a){const r=[],s=[],u=[],c=[];let i,h,m=!1,p=!1;const N={colorNotation:ue.sRGB,channels:[0,0,0],alpha:1,syntaxFlags:new Set([])};let b=r;for(let o=0;o=0){u=c.value[4].value;continue}}return!1}if(!t)return!1;n.push({color:t,percentage:u}),t=!1,u=!1}}t&&n.push({color:t,percentage:u});let c=0,i=0;for(let e=0;e100)return!1;c+=a}else i++}const h=Math.max(0,100-c);c=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(ce.Experimental)))&&o.syntaxFlags.add(ce.Experimental),"number"==typeof o.alpha&&(o.alpha=o.alpha*a.alphaMultiplier,2!==a.colors.length&&o.syntaxFlags.add(ce.ColorMixVariadic),o))}function colorMixRectangularPair(e,a,n,r,o){const t=n/(n+o);let l=a.alpha;if("number"!=typeof l)return!1;let s=r.alpha;if("number"!=typeof s)return!1;l=Number.isNaN(l)?s:l,s=Number.isNaN(s)?l:s;const u=colorDataTo(a,e).channels,c=colorDataTo(r,e).channels;u[0]=fillInMissingComponent(u[0],c[0]),c[0]=fillInMissingComponent(c[0],u[0]),u[1]=fillInMissingComponent(u[1],c[1]),c[1]=fillInMissingComponent(c[1],u[1]),u[2]=fillInMissingComponent(u[2],c[2]),c[2]=fillInMissingComponent(c[2],u[2]),u[0]=premultiply(u[0],l),u[1]=premultiply(u[1],l),u[2]=premultiply(u[2],l),c[0]=premultiply(c[0],s),c[1]=premultiply(c[1],s),c[2]=premultiply(c[2],s);const i=interpolate(l,s,t);return{colorNotation:e,channels:[un_premultiply(interpolate(u[0],c[0],t),i),un_premultiply(interpolate(u[1],c[1],t),i),un_premultiply(interpolate(u[2],c[2],t),i)],alpha:i,syntaxFlags:new Set([ce.ColorMix])}}function colorMixPolar(e,a,n){if(!n||!n.colors.length)return!1;const r=n.colors.slice();r.reverse();let o=ue.HSL;switch(e){case"hsl":o=ue.HSL;break;case"hwb":o=ue.HWB;break;case"lch":o=ue.LCH;break;case"oklch":o=ue.OKLCH;break;default:return!1}if(1===r.length){const e=colorDataTo(r[0].color,o);return e.colorNotation=o,e.syntaxFlags.add(ce.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 t=colorMixPolarPair(o,a,e.color,e.percentage,n.color,n.percentage);if(!t)return!1;r.push({color:t,percentage:e.percentage+n.percentage})}const t=r[0]?.color;return!!t&&(n.colors.some((e=>e.color.syntaxFlags.has(ce.Experimental)))&&t.syntaxFlags.add(ce.Experimental),"number"==typeof t.alpha&&(t.alpha=t.alpha*n.alphaMultiplier,2!==n.colors.length&&t.syntaxFlags.add(ce.ColorMixVariadic),t))}function colorMixPolarPair(e,a,n,r,o,t){const l=r/(r+t);let s=0,u=0,c=0,i=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 ue.HSL:case ue.HWB:s=b[0],u=g[0],c=b[1],i=g[1],h=b[2],m=g[2];break;case ue.LCH:case ue.OKLCH:c=b[0],i=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),c=fillInMissingComponent(c,i),i=fillInMissingComponent(i,c),h=fillInMissingComponent(h,m),m=fillInMissingComponent(m,h);const f=u-s;switch(a){case"shorter":f>180?s+=360:f<-180&&(u+=360);break;case"longer":-1800?s+=360:u+=360);break;case"increasing":f<0&&(u+=360);break;case"decreasing":f>0&&(s+=360);break;default:throw new Error("Unknown hue interpolation method")}c=premultiply(c,p),h=premultiply(h,p),i=premultiply(i,N),m=premultiply(m,N);let v=[0,0,0];const d=interpolate(p,N,l);switch(e){case ue.HSL:case ue.HWB:v=[interpolate(s,u,l),un_premultiply(interpolate(c,i,l),d),un_premultiply(interpolate(h,m,l),d)];break;case ue.LCH:case ue.OKLCH:v=[un_premultiply(interpolate(c,i,l),d),un_premultiply(interpolate(h,m,l),d),interpolate(s,u,l)]}return{colorNotation:e,channels:v,alpha:d,syntaxFlags:new Set([ce.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:ue.HEX,channels:[0,0,0],alpha:1,syntaxFlags:new Set([ce.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(ce.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(ce.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(ce.HasDimensionValues),e)}if(r(n)){3===t?l.syntaxFlags.add(ce.HasPercentageAlpha):l.syntaxFlags.add(ce.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(ce.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(ce.HasDimensionValues),e)}if(r(t)){3===l?s.syntaxFlags.add(ce.HasPercentageAlpha):s.syntaxFlags.add(ce.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(ce.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=[],c=[],i=[],h={colorNotation:n,channels:[0,0,0],alpha:1,syntaxFlags:new Set(r)};let m=t;for(let a=0;aee(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,ue.HSL,[ce.LegacyHSL])}function hslSpaceSeparated(e,a){return threeChannelSpaceSeparated(e,normalize_modern_HSL_ChannelValues,ue.HSL,[],a)}function normalize_HWB_ChannelValues(t,l,s){if(n(t)&&"none"===toLowerCaseAZ(t[4].value))return s.syntaxFlags.add(ce.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(ce.HasDimensionValues),e)}if(r(t)){3===l?s.syntaxFlags.add(ce.HasPercentageAlpha):s.syntaxFlags.add(ce.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(ce.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(ce.HasNoneKeywords),[e.Number,"none",t[2],t[3],{value:Number.NaN,type:a.Number}];if(r(t)){3!==l&&s.syntaxFlags.add(ce.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(ce.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,ue.Lab,[],a)}function normalize_LCH_ChannelValues(t,l,s){if(n(t)&&"none"===toLowerCaseAZ(t[4].value))return s.syntaxFlags.add(ce.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(ce.HasDimensionValues),e)}if(r(t)){3!==l&&s.syntaxFlags.add(ce.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(ce.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,ue.LCH,[],a)}const ge=new Map;for(const[e,a]of Object.entries(V))ge.set(e,a);function namedColor(e){const a=ge.get(toLowerCaseAZ(e));return!!a&&{colorNotation:ue.RGB,channels:[a[0]/255,a[1]/255,a[2]/255],alpha:1,syntaxFlags:new Set([ce.ColorKeyword,ce.NamedColor])}}function normalize_OKLab_ChannelValues(t,l,s){if(n(t)&&"none"===toLowerCaseAZ(t[4].value))return s.syntaxFlags.add(ce.HasNoneKeywords),[e.Number,"none",t[2],t[3],{value:Number.NaN,type:a.Number}];if(r(t)){3!==l&&s.syntaxFlags.add(ce.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(ce.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,ue.OKLab,[],a)}function normalize_OKLCH_ChannelValues(t,l,s){if(n(t)&&"none"===toLowerCaseAZ(t[4].value))return s.syntaxFlags.add(ce.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(ce.HasDimensionValues),e)}if(r(t)){3!==l&&s.syntaxFlags.add(ce.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(ce.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,ue.OKLCH,[],a)}function normalize_legacy_sRGB_ChannelValues(n,t,l){if(r(n)){3===t?l.syntaxFlags.add(ce.HasPercentageAlpha):l.syntaxFlags.add(ce.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(ce.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(ce.HasNoneKeywords),[e.Number,"none",t[2],t[3],{value:Number.NaN,type:a.Number}];if(r(t)){3!==l&&s.syntaxFlags.add(ce.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(ce.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=>ee(e)&&s(e.value)))){const a=rgbCommaSeparated(e);if(!1!==a)return(!a.syntaxFlags.has(ce.HasNumberValues)||!a.syntaxFlags.has(ce.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,ue.RGB,[ce.LegacyRGB])}function rgbSpaceSeparated(e,a){return threeChannelSpaceSeparated(e,normalize_modern_sRGB_ChannelValues,ue.RGB,[],a)}function XYZ_D50_to_sRGB_Gamut(e){const a=w(e);if(G(a))return T(a);let n=e;return n=p(n),n[0]<1e-6&&(n=[0,0,0]),n[0]>.999999&&(n=[1,0,0]),A(X(n,oklch_to_lin_srgb,lin_srgb_to_oklch))}function oklch_to_lin_srgb(e){return e=K(e),e=Y(e),I(e)}function lin_srgb_to_oklch(e){return e=O(e),e=W(e),E(e)}function contrastColor(e,a){let n=!1;for(let r=0;rt?[1,1,1]:[0,0,0],r}function XYZ_D50_to_P3_Gamut(e){const a=_(e);if(G(a))return T(a);let n=e;return n=p(n),n[0]<1e-6&&(n=[0,0,0]),n[0]>.999999&&(n=[1,0,0]),$(X(n,oklch_to_lin_p3,lin_p3_to_oklch))}function oklch_to_lin_p3(e){return e=K(e),e=Y(e),j(e)}function lin_p3_to_oklch(e){return e=q(e),e=W(e),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 oe(r,l,t):new oe(r,l,[...t,new te([o]),new ne([e.Delim,"/",-1,-1,{value:"/"}]),new te([o]),new ne([e.Number,toPrecision(s,4).toString(),-1,-1,{value:n.alpha,type:a.Integer}])])}return new oe(r,l,[...t,new te([o]),new ne([e.Delim,"/",-1,-1,{value:"/"}]),new te([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!==ue.Display_P3&&(o=_(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"}],c=[e.Whitespace," ",-1,-1,void 0];return serializeWithAlpha(n,u,c,[new ne([e.Ident,"display-p3",-1,-1,{value:"display-p3"}]),new te([c]),new ne([e.Number,t.toString(),-1,-1,{value:o[0],type:a.Number}]),new te([c]),new ne([e.Number,l.toString(),-1,-1,{value:o[1],type:a.Number}]),new te([c]),new ne([e.Number,s.toString(),-1,-1,{value:o[2],type:a.Number}])])}function serializeRGB(n,r=!0){n.channels=convertPowerlessComponentsToZeroValuesForDisplay(n.channels,n.colorNotation);let o=n.channels.map((e=>Number.isNaN(e)?0:e));o=r?XYZ_D50_to_sRGB_Gamut(colorData_to_XYZ_D50(n).channels):w(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],c=[e.Whitespace," ",-1,-1,void 0],i=[e.Comma,",",-1,-1,void 0],h=[new ne([e.Number,t.toString(),-1,-1,{value:Math.min(255,255*Math.max(0,o[0])),type:a.Integer}]),new ne(i),new te([c]),new ne([e.Number,l.toString(),-1,-1,{value:Math.min(255,255*Math.max(0,o[1])),type:a.Integer}]),new ne(i),new te([c]),new ne([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 oe([e.Function,"rgb(",-1,-1,{value:"rgb"}],u,h):new oe([e.Function,"rgba(",-1,-1,{value:"rgba"}],u,[...h,new ne(i),new te([c]),new ne([e.Number,toPrecision(r,4).toString(),-1,-1,{value:n.alpha,type:a.Number}])])}return new oe([e.Function,"rgba(",-1,-1,{value:"rgba"}],u,[...h,new ne(i),new te([c]),n.alpha])}function serializeHSL(n,r=!0){n.channels=convertPowerlessComponentsToZeroValuesForDisplay(n.channels,n.colorNotation);let o=n.channels.map((e=>Number.isNaN(e)?0:e));o=f(r?B(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],c=[e.Whitespace," ",-1,-1,void 0],i=[e.Comma,",",-1,-1,void 0],h=[new ne([e.Number,t.toString(),-1,-1,{value:o[0],type:a.Integer}]),new ne(i),new te([c]),new ne([e.Percentage,l.toString()+"%",-1,-1,{value:o[1]}]),new ne(i),new te([c]),new ne([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 oe([e.Function,"hsl(",-1,-1,{value:"hsl"}],u,h):new oe([e.Function,"hsla(",-1,-1,{value:"hsla"}],u,[...h,new ne(i),new te([c]),new ne([e.Number,toPrecision(r,4).toString(),-1,-1,{value:n.alpha,type:a.Number}])])}return new oe([e.Function,"hsla(",-1,-1,{value:"hsla"}],u,[...h,new ne(i),new te([c]),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!==ue.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 ne([e.Number,o.toString(),-1,-1,{value:r[0],type:a.Number}]),new te([u]),new ne([e.Number,t.toString(),-1,-1,{value:r[1],type:a.Number}]),new te([u]),new ne([e.Number,l.toString(),-1,-1,{value:r[2],type:a.Number}])])}function color(e){if(ae(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,ue.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)}}var a;if(ee(e)){if(c(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:ue.RGB,channels:[0,0,0],alpha:0,syntaxFlags:new Set([ce.ColorKeyword])}}}return!1}export{ue as ColorNotation,ce as SyntaxFlag,color,colorDataFitsDisplayP3_Gamut,colorDataFitsRGB_Gamut,serializeHSL,serializeOKLCH,serializeP3,serializeRGB}; diff --git a/packages/css-color-parser/docs/css-color-parser.api.json b/packages/css-color-parser/docs/css-color-parser.api.json index 723047f150..fe5dde7c60 100644 --- a/packages/css-color-parser/docs/css-color-parser.api.json +++ b/packages/css-color-parser/docs/css-color-parser.api.json @@ -1107,6 +1107,27 @@ "releaseTag": "Public", "name": "ColorMix" }, + { + "kind": "EnumMember", + "canonicalReference": "@csstools/css-color-parser!SyntaxFlag.ColorMixVariadic:member", + "docComment": "/**\n * Is a variadic mixed color, e.g. `color-mix(in oklch, red)` `color-mix(in oklch, red, blue, green)`\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "ColorMixVariadic = " + }, + { + "kind": "Content", + "text": "\"color-mix-variadic\"" + } + ], + "initializerTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "releaseTag": "Public", + "name": "ColorMixVariadic" + }, { "kind": "EnumMember", "canonicalReference": "@csstools/css-color-parser!SyntaxFlag.ContrastColor:member", diff --git a/packages/css-color-parser/src/color-data.ts b/packages/css-color-parser/src/color-data.ts index b812a9d3f0..83ffc67159 100644 --- a/packages/css-color-parser/src/color-data.ts +++ b/packages/css-color-parser/src/color-data.ts @@ -74,6 +74,8 @@ export enum SyntaxFlag { RelativeColorSyntax = 'relative-color-syntax', /** Is a mixed color, e.g. `color-mix(in oklch, red, blue)` */ ColorMix = 'color-mix', + /** Is a variadic mixed color, e.g. `color-mix(in oklch, red)` `color-mix(in oklch, red, blue, green)` */ + ColorMixVariadic = 'color-mix-variadic', /** Is a contrasting color, e.g. `contrast-color()` */ ContrastColor = 'contrast-color', /** Is an experimental color syntax */ diff --git a/packages/css-color-parser/src/functions/color-mix.ts b/packages/css-color-parser/src/functions/color-mix.ts index dc832b2c55..664882efcc 100644 --- a/packages/css-color-parser/src/functions/color-mix.ts +++ b/packages/css-color-parser/src/functions/color-mix.ts @@ -6,7 +6,7 @@ import { ColorNotation } from '../color-notation'; import { isTokenIdent, isTokenNumeric, isTokenPercentage } from '@csstools/css-tokenizer'; import { calcFromComponentValues } from '@csstools/css-calc'; import { colorDataTo, SyntaxFlag } from '../color-data'; -import { isCommentNode, isFunctionNode, isTokenNode, isWhitespaceNode } from '@csstools/css-parser-algorithms'; +import { isFunctionNode, isTokenNode, isWhiteSpaceOrCommentNode } from '@csstools/css-parser-algorithms'; import { toLowerCaseAZ } from '../util/to-lower-case-a-z'; import { mathFunctionNames } from '@csstools/css-calc'; import { isTokenComma } from '@csstools/css-tokenizer'; @@ -24,7 +24,7 @@ export function colorMix(colorMixNode: FunctionNode, colorParser: ColorParser): for (let i = 0; i < colorMixNode.value.length; i++) { const node = colorMixNode.value[i]; - if (isWhitespaceNode(node) || isCommentNode(node)) { + if (isWhiteSpaceOrCommentNode(node)) { continue; } @@ -118,13 +118,12 @@ type ColorMixEntry = { percentage: number } -type ColorMixColors = { - a: ColorMixEntry, - b: ColorMixEntry, +type ColorMixItems = { + colors: Array, alphaMultiplier: number, } -function colorMixComponents(componentValues: Array, colorParser: ColorParser): ColorMixColors | false { +function colorMixComponents(componentValues: Array, colorParser: ColorParser): ColorMixItems | false { const colors: Array<{ color: ColorData, percentage: number | false }> = []; let alphaMultiplier = 1; @@ -134,7 +133,7 @@ function colorMixComponents(componentValues: Array, colorParser: for (let i = 0; i < componentValues.length; i++) { let node = componentValues[i]; - if (isWhitespaceNode(node) || isCommentNode(node)) { + if (isWhiteSpaceOrCommentNode(node)) { continue; } @@ -208,90 +207,85 @@ function colorMixComponents(componentValues: Array, colorParser: }); } - if (colors.length !== 2) { - 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 + } - let p1 = colors[0].percentage; - let p2 = colors[1].percentage; + if (p < 0 || p > 100) { + return false; + } - if (p1 !== false && (p1 < 0 || p1 > 100)) { - return false; - } - if (p2 !== false && (p2 < 0 || p2 > 100)) { - return false; + pSum += p; } - if (p1 === false && p2 === false) { - p1 = 50; - p2 = 50; - } else if (p1 !== false && p2 === false) { - p2 = 100 - p1; - } else if (p1 === false && p2 !== false) { - p1 = 100 - p2; - } + const pRemainder = Math.max(0, 100 - pSum); - if (p1 === 0 && p2 === 0) { - return false; + 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; } - if (p1 === false || p2 === false) { - return false; + 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, + }; } - if ((p1 + p2) > 100) { - p1 = p1 / (p1 + p2) * 100; - p2 = p2 / (p1 + p2) * 100; + 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; + } } - if ((p1 + p2) < 100) { - alphaMultiplier = (p1 + p2) / 100; + 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 - p1 = p1 / (p1 + p2) * 100; - p2 = p2 / (p1 + p2) * 100; + p = (p / pSum) * 100; + colors[i].percentage = p; + } } return { - a: { - color: colors[0].color, - percentage: p1, - }, - b: { - color: colors[1].color, - percentage: p2, - }, + colors: colors as ColorMixItems['colors'], // already handled all percentage `false` cases alphaMultiplier: alphaMultiplier, }; } -function colorMixRectangular(colorSpace: string, colors: ColorMixColors | false): ColorData | false { - if (!colors) { +function colorMixRectangular(colorSpace: string, items: ColorMixItems | false): ColorData | false { + if (!items || !items.colors.length) { return false; } - const a_color = colors.a.color; - const b_color = colors.b.color; - - const ratio = colors.a.percentage / 100; - - let a_channels = a_color.channels; - let b_channels = b_color.channels; + const colors = items.colors.slice(); + colors.reverse(); let outputColorNotation: ColorNotation = ColorNotation.RGB; - - let a_alpha = a_color.alpha; - if (typeof a_alpha !== 'number') { - return false; - } - - let b_alpha = b_color.alpha; - if (typeof b_alpha !== 'number') { - return false; - } - - a_alpha = Number.isNaN(a_alpha) ? b_alpha : a_alpha; - b_alpha = Number.isNaN(b_alpha) ? a_alpha : b_alpha; - switch (colorSpace) { case 'srgb': outputColorNotation = ColorNotation.RGB; @@ -325,11 +319,83 @@ function colorMixRectangular(colorSpace: string, colors: ColorMixColors | false) outputColorNotation = ColorNotation.XYZ_D65; break; default: - break; + return false; + } + + if (colors.length === 1) { + const color = colorDataTo(colors[0].color, outputColorNotation); + color.colorNotation = outputColorNotation; + color.syntaxFlags.add(SyntaxFlag.ColorMixVariadic); + + if (typeof color.alpha === 'number') { + color.alpha = color.alpha * items.alphaMultiplier; + } else { + return 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(); + + if (!a_color || !b_color) { + return false; + } + + const mixed_color = colorMixRectangularPair(outputColorNotation, a_color.color, a_color.percentage, b_color.color, b_color.percentage); + if (!mixed_color) { + return false; + } + + colors.push({ + color: mixed_color, + percentage: a_color.percentage + b_color.percentage + }) + } + + const colorData = colors[0]?.color; + if (!colorData) { + return false + } + + if (items.colors.some((x) => x.color.syntaxFlags.has(SyntaxFlag.Experimental))) { + colorData.syntaxFlags.add(SyntaxFlag.Experimental); + } + + if (typeof colorData.alpha === 'number') { + colorData.alpha = colorData.alpha * items.alphaMultiplier + } else { + return false; } - a_channels = colorDataTo(a_color, outputColorNotation).channels; - b_channels = colorDataTo(b_color, outputColorNotation).channels; + if (items.colors.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); + + let a_alpha = a_color.alpha; + if (typeof a_alpha !== 'number') { + return false; + } + + let b_alpha = b_color.alpha; + if (typeof b_alpha !== 'number') { + return false; + } + + a_alpha = Number.isNaN(a_alpha) ? b_alpha : a_alpha; + b_alpha = Number.isNaN(b_alpha) ? a_alpha : b_alpha; + + const a_channels = colorDataTo(a_color, colorNotation).channels; + const b_channels = colorDataTo(b_color, colorNotation).channels; a_channels[0] = fillInMissingComponent(a_channels[0], b_channels[0]); b_channels[0] = fillInMissingComponent(b_channels[0], a_channels[0]); @@ -356,34 +422,99 @@ function colorMixRectangular(colorSpace: string, colors: ColorMixColors | false) ]; const colorData: ColorData = { - colorNotation: outputColorNotation, + colorNotation: colorNotation, channels: outputChannels, - alpha: alpha * colors.alphaMultiplier, + alpha: alpha, syntaxFlags: (new Set([SyntaxFlag.ColorMix])), }; - if ( - colors.a.color.syntaxFlags.has(SyntaxFlag.Experimental) || - colors.b.color.syntaxFlags.has(SyntaxFlag.Experimental) - ) { - colorData.syntaxFlags.add(SyntaxFlag.Experimental); - } - return colorData; } -function colorMixPolar(colorSpace: string, hueInterpolationMethod: string, colors: ColorMixColors | false): ColorData | false { - if (!colors) { +function colorMixPolar(colorSpace: string, hueInterpolationMethod: string, items: ColorMixItems | false): ColorData | false { + if (!items || !items.colors.length) { return false; } - const a_color = colors.a.color; - const b_color = colors.b.color; + const colors = items.colors.slice(); + colors.reverse(); + + let outputColorNotation: ColorNotation = ColorNotation.HSL; + switch (colorSpace) { + case 'hsl': + outputColorNotation = ColorNotation.HSL; + break; + case 'hwb': + outputColorNotation = ColorNotation.HWB; + break; + case 'lch': + outputColorNotation = ColorNotation.LCH; + break; + case 'oklch': + outputColorNotation = ColorNotation.OKLCH; + break; + default: + return false; + } + + if (colors.length === 1) { + const color = colorDataTo(colors[0].color, outputColorNotation); + color.colorNotation = outputColorNotation; + color.syntaxFlags.add(SyntaxFlag.ColorMixVariadic); - const ratio = colors.a.percentage / 100; + if (typeof color.alpha === 'number') { + color.alpha = color.alpha * items.alphaMultiplier; + } else { + return 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(); + + 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); + if (!mixed_color) { + return false; + } + + colors.push({ + color: mixed_color, + percentage: a_color.percentage + b_color.percentage + }) + } + + const colorData = colors[0]?.color; + if (!colorData) { + return false + } - let a_channels = a_color.channels; - let b_channels = b_color.channels; + if (items.colors.some((x) => x.color.syntaxFlags.has(SyntaxFlag.Experimental))) { + colorData.syntaxFlags.add(SyntaxFlag.Experimental); + } + + if (typeof colorData.alpha === 'number') { + colorData.alpha = colorData.alpha * items.alphaMultiplier + } else { + return false; + } + + if (items.colors.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); let a_hue = 0; let b_hue = 0; @@ -394,8 +525,6 @@ function colorMixPolar(colorSpace: string, hueInterpolationMethod: string, color let a_second = 0; let b_second = 0; - let outputColorNotation: ColorNotation = ColorNotation.RGB; - let a_alpha = a_color.alpha; if (typeof a_alpha !== 'number') { return false; @@ -409,29 +538,12 @@ function colorMixPolar(colorSpace: string, hueInterpolationMethod: string, color a_alpha = Number.isNaN(a_alpha) ? b_alpha : a_alpha; b_alpha = Number.isNaN(b_alpha) ? a_alpha : b_alpha; - switch (colorSpace) { - case 'hsl': - outputColorNotation = ColorNotation.HSL; - break; - case 'hwb': - outputColorNotation = ColorNotation.HWB; - break; - case 'lch': - outputColorNotation = ColorNotation.LCH; - break; - case 'oklch': - outputColorNotation = ColorNotation.OKLCH; - break; - default: - break; - } + const a_channels = colorDataTo(a_color, colorNotation).channels; + const b_channels = colorDataTo(b_color, colorNotation).channels; - a_channels = colorDataTo(a_color, outputColorNotation).channels; - b_channels = colorDataTo(b_color, outputColorNotation).channels; - - switch (colorSpace) { - case 'hsl': - case 'hwb': + switch (colorNotation) { + case ColorNotation.HSL: + case ColorNotation.HWB: a_hue = a_channels[0]; b_hue = b_channels[0]; @@ -442,8 +554,8 @@ function colorMixPolar(colorSpace: string, hueInterpolationMethod: string, color b_second = b_channels[2]; break; - case 'lch': - case 'oklch': + case ColorNotation.LCH: + case ColorNotation.OKLCH: a_first = a_channels[0]; b_first = b_channels[0]; @@ -519,9 +631,9 @@ function colorMixPolar(colorSpace: string, hueInterpolationMethod: string, color let outputChannels: Color = [0, 0, 0]; const alpha = interpolate(a_alpha, b_alpha, ratio); - switch (colorSpace) { - case 'hsl': - case 'hwb': + switch (colorNotation) { + case ColorNotation.HSL: + case ColorNotation.HWB: outputChannels = [ interpolate(a_hue, b_hue, ratio), un_premultiply(interpolate(a_first, b_first, ratio), alpha), @@ -529,8 +641,8 @@ function colorMixPolar(colorSpace: string, hueInterpolationMethod: string, color ]; break; - case 'lch': - case 'oklch': + 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), @@ -543,19 +655,12 @@ function colorMixPolar(colorSpace: string, hueInterpolationMethod: string, color } const colorData: ColorData = { - colorNotation: outputColorNotation, + colorNotation: colorNotation, channels: outputChannels, - alpha: alpha * colors.alphaMultiplier, + alpha: alpha, syntaxFlags: (new Set([SyntaxFlag.ColorMix])), }; - if ( - colors.a.color.syntaxFlags.has(SyntaxFlag.Experimental) || - colors.b.color.syntaxFlags.has(SyntaxFlag.Experimental) - ) { - colorData.syntaxFlags.add(SyntaxFlag.Experimental); - } - return colorData; } 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 0f64b7736d..4f07c4a42e 100644 --- a/packages/css-color-parser/test/basic/color-mix-function.mjs +++ b/packages/css-color-parser/test/basic/color-mix-function.mjs @@ -99,6 +99,31 @@ const tests = [ // TODO : `none` containing support ['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%))', canonicalize('hsl(120deg 10% 20%)')], // single arg + ['color-mix(in hsl, hsl(120deg 10% 20%) 100%)', canonicalize('hsl(120deg 10% 20%)')], // single arg + ['color-mix(in srgb, hsl(none 50% 50%))', canonicalize('rgb(191, 64, 64)')], // single arg + ['color-mix(in srgb, red 50%)', canonicalize('rgba(255, 0, 0, 0.5)')], // single arg + ['color-mix(in srgb, red 100%)', canonicalize('rgb(255, 0, 0)')], // single arg + + ['color-mix(in hsl, hsl(120deg 10% 20%), hsl(120deg 10% 20%), hsl(120deg 10% 20%))', canonicalize('hsl(120deg 10% 20%)')], // multiple args + ['color-mix(in hsl, hsl(120deg 10% 20%) 30%, hsl(120deg 10% 20%) 30%, hsl(120deg 10% 20%) 40%)', canonicalize('hsl(120deg 10% 20%)')], // multiple args + ['color-mix(in srgb, red, lime, blue)', canonicalize('rgb(85, 85, 85)')], // multiple args + ['color-mix(in oklch, red, lime, blue)', canonicalize('rgb(0, 173, 49)')], // multiple args + ['color-mix(in oklch increasing hue, red, lime, blue)', canonicalize('rgb(0, 173, 49)')], // multiple args + ['color-mix(in oklch longer hue, red, lime, blue)', canonicalize('rgb(255, 50, 58)')], // multiple args + ['color-mix(in oklch decreasing hue, red, lime, blue)', canonicalize('rgb(86, 134, 255)')], // multiple args + ['color-mix(in xyz, red, lime, blue)', canonicalize('rgb(156, 156, 156)')], // multiple args + ['color-mix(in srgb, maroon, green, navy)', canonicalize('rgb(43, 43, 43)')], // multiple args + ['color-mix(in srgb, cyan, magenta, yellow)', canonicalize('rgb(170, 170, 170)')], // multiple args + ['color-mix(in srgb, red, lime, blue, maroon, green, navy, cyan, magenta, yellow)', canonicalize('rgb(99, 99, 99)')], // multiple args + ['color-mix(in srgb, cyan 50%, magenta 50%, yellow 50%)', canonicalize('rgb(170, 170, 170)')], // multiple args + ['color-mix(in srgb, cyan 25%, magenta 25%, yellow 25%)', canonicalize('rgba(170, 170, 170, 0.75)')], // multiple args ]; for (const test of tests) { diff --git a/packages/css-color-parser/test/wpt/color-invalid-color-mix-function.mjs b/packages/css-color-parser/test/wpt/color-invalid-color-mix-function.mjs index 026163d215..b1adf37c4b 100644 --- a/packages/css-color-parser/test/wpt/color-invalid-color-mix-function.mjs +++ b/packages/css-color-parser/test/wpt/color-invalid-color-mix-function.mjs @@ -8,10 +8,8 @@ import { serialize_sRGB_data } from '../util/serialize.mjs'; const tests = [ [`color-mix(in ${colorSpace}, ${colorSpace}(10% 20 30deg) -10%, ${colorSpace}(50% 60 70deg))`], // Percentages less than 0 are not valid. [`color-mix(in ${colorSpace}, ${colorSpace}(10% 20 30deg) 150%, ${colorSpace}(50% 60 70deg))`], // Percentages greater than 100 are not valid. - [`color-mix(in ${colorSpace}, ${colorSpace}(10% 20 30deg) 0%, ${colorSpace}(50% 60 70deg) 0%)`], // Sum of percengates cannot be 0%. [`color-mix(in ${colorSpace}, ${colorSpace}(10% 20 30deg / .4) -10%, ${colorSpace}(50% 60 70deg / .8))`], // Percentages less than 0 are not valid. [`color-mix(in ${colorSpace}, ${colorSpace}(10% 20 30deg / .4) 150%, ${colorSpace}(50% 60 70deg / .8))`], // Percentages greater than 100 are not valid. - [`color-mix(in ${colorSpace}, ${colorSpace}(10% 20 30deg / .4) 0%, ${colorSpace}(50% 60 70deg / .8) 0%)`], // Sum of percengates cannot be 0%. [`color-mix(in ${colorSpace} hue, ${colorSpace}(10% 20 30deg), ${colorSpace}(50% 60 70deg))`], // `hue` keyword without a specified method. [`color-mix(in ${colorSpace} shorter, ${colorSpace}(10% 20 30deg), ${colorSpace}(50% 60 70deg))`], // Specified hue method without trailing `hue` keyword. [`color-mix(in ${colorSpace} foo, ${colorSpace}(10% 20 30deg), ${colorSpace}(50% 60 70deg))`], // Trailing identifier after color space that is not a hue method. @@ -36,10 +34,8 @@ import { serialize_sRGB_data } from '../util/serialize.mjs'; const tests = [ [`color-mix(in ${colorSpace}, ${colorSpace}(10% 20 30) -10%, ${colorSpace}(50% 60 70))`], // Percentages less than 0 are not valid. [`color-mix(in ${colorSpace}, ${colorSpace}(10% 20 30) 150%, ${colorSpace}(50% 60 70))`], // Percentages greater than 100 are not valid. - [`color-mix(in ${colorSpace}, ${colorSpace}(10% 20 30) 0%, ${colorSpace}(50% 60 70) 0%)`], // Sum of percengates cannot be 0%. [`color-mix(in ${colorSpace}, ${colorSpace}(10% 20 30 / .4) -10%, ${colorSpace}(50% 60 70 / .8))`], // Percentages less than 0 are not valid. [`color-mix(in ${colorSpace}, ${colorSpace}(10% 20 30 / .4) 150%, ${colorSpace}(50% 60 70 / .8))`], // Percentages greater than 100 are not valid. - [`color-mix(in ${colorSpace}, ${colorSpace}(10% 20 30 / .4) 0%, ${colorSpace}(50% 60 70 / .8) 0%)`], // Sum of percengates cannot be 0%. [`color-mix(in ${colorSpace} longer hue, ${colorSpace}(10% 20 30), ${colorSpace}(50% 60 70))`], // Hue modifier on a non-polar color space. [`color-mix(in ${colorSpace} ${colorSpace}(10% 20 30), ${colorSpace}(50% 60 70))`], // Missing comma after interpolation method. [`color-mix(in ${colorSpace}, ${colorSpace}(10% 20 30) ${colorSpace}(50% 60 70))`], // Missing comma between colors. @@ -62,10 +58,8 @@ import { serialize_sRGB_data } from '../util/serialize.mjs'; const tests = [ [`color-mix(in ${colorSpace}, color(${colorSpace} .1 .2 .3) -10%, color(${colorSpace} .5 .6 .7))`], // Percentages less than 0 are not valid. [`color-mix(in ${colorSpace}, color(${colorSpace} .1 .2 .3) 150%, color(${colorSpace} .5 .6 .7))`], // Percentages greater than 100 are not valid. - [`color-mix(in ${colorSpace}, color(${colorSpace} .1 .2 .3) 0%, color(${colorSpace} .5 .6 .7) 0%)`], // Sum of percengates cannot be 0%. [`color-mix(in ${colorSpace}, color(${colorSpace} .1 .2 .3 / .4) -10%, color(${colorSpace} .5 .6 .7 / .8))`], // Percentages less than 0 are not valid. [`color-mix(in ${colorSpace}, color(${colorSpace} .1 .2 .3 / .4) 150%, color(${colorSpace} .5 .6 .7 / .8))`], // Percentages greater than 100 are not valid. - [`color-mix(in ${colorSpace}, color(${colorSpace} .1 .2 .3 / .4) 0%, color(${colorSpace} .5 .6 .7 / .8) 0%)`], // Sum of percengates cannot be 0%. [`color-mix(in ${colorSpace} longer hue, color(${colorSpace} .1 .2 .3), color(${colorSpace} .5 .6 .7))`], // Hue modifier on a non-polar color space. [`color-mix(in ${colorSpace} color(${colorSpace} .1 .2 .3), color(${colorSpace} .5 .6 .7))`], // Missing comma after interpolation method. [`color-mix(in ${colorSpace}, color(${colorSpace} .1 .2 .3) color(${colorSpace} .5 .6 .7))`], // Missing comma between colors. @@ -87,10 +81,8 @@ import { serialize_sRGB_data } from '../util/serialize.mjs'; const tests = [ ['color-mix(in hsl, hsl(120deg 10% 20%) -10%, hsl(30deg 30% 40%))'], // Percentages less than 0 are not valid. ['color-mix(in hsl, hsl(120deg 10% 20%) 150%, hsl(30deg 30% 40%))'], // Percentages greater than 100 are not valid. - ['color-mix(in hsl, hsl(120deg 10% 20%) 0%, hsl(30deg 30% 40%) 0%)'], // Sum of percengates cannot be 0%. ['color-mix(in hsl, hsl(120deg 10% 20% 40%) -10%, hsl(30deg 30% 40% 80%))'], // Percentages less than 0 are not valid. ['color-mix(in hsl, hsl(120deg 10% 20% 40%) 150%, hsl(30deg 30% 40% 80%))'], // Percentages greater than 100 are not valid. - ['color-mix(in hsl, hsl(120deg 10% 20% 40%) 0%, hsl(30deg 30% 40% 80%) 0%)'], // Sum of percengates cannot be 0%. ['color-mix(in hsl hue, hsl(120deg 10% 20%), hsl(30deg 30% 40%))'], // `hue` keyword without a specified method. ['color-mix(in hsl shorter, hsl(120deg 10% 20%), hsl(30deg 30% 40%))'], // Specified hue method without trailing `hue` keyword. ['color-mix(in hsl foo, hsl(120deg 10% 20%), hsl(30deg 30% 40%))'], // Trailing identifier after color space that is not a hue method. @@ -101,10 +93,8 @@ import { serialize_sRGB_data } from '../util/serialize.mjs'; ['color-mix(in hwb, hwb(120deg 10% 20%) -10%, hwb(30deg 30% 40%))'], // Percentages less than 0 are not valid. ['color-mix(in hwb, hwb(120deg 10% 20%) 150%, hwb(30deg 30% 40%))'], // Percentages greater than 100 are not valid. - ['color-mix(in hwb, hwb(120deg 10% 20%) 0%, hwb(30deg 30% 40%) 0%)'], // Sum of percengates cannot be 0%. ['color-mix(in hwb, hwb(120deg 10% 20% 40%) -10%, hwb(30deg 30% 40% 80%))'], // Percentages less than 0 are not valid. ['color-mix(in hwb, hwb(120deg 10% 20% 40%) 150%, hwb(30deg 30% 40% 80%))'], // Percentages greater than 100 are not valid. - ['color-mix(in hwb, hwb(120deg 10% 20% 40%) 0%, hwb(30deg 30% 40% 80%) 0%)'], // Sum of percengates cannot be 0%. ['color-mix(in hwb hue, hwb(120deg 10% 20%), hwb(30deg 30% 40%))'], // `hue` keyword without a specified method. ['color-mix(in hwb shorter, hwb(120deg 10% 20%), hwb(30deg 30% 40%))'], // Specified hue method without trailing `hue` keyword. ['color-mix(in hwb foo, hwb(120deg 10% 20%), hwb(30deg 30% 40%))'], // Trailing identifier after color space that is not a hue method. diff --git a/plugins/postcss-color-mix-function/dist/index.cjs b/plugins/postcss-color-mix-function/dist/index.cjs index 5bf9fd70ee..6e3baf5a7b 100644 --- a/plugins/postcss-color-mix-function/dist/index.cjs +++ b/plugins/postcss-color-mix-function/dist/index.cjs @@ -1 +1 @@ -"use strict";var s=require("@csstools/postcss-progressive-custom-properties"),e=require("@csstools/css-color-parser"),r=require("@csstools/utilities"),t=require("@csstools/css-parser-algorithms"),o=require("@csstools/css-tokenizer");const i=/\bcolor-mix\(/i,a=/^color-mix$/i,basePlugin=s=>({postcssPlugin:"postcss-color-mix-function",Declaration(n){const l=n.value;if(!i.test(l))return;if(r.hasFallback(n))return;if(r.hasSupportsAtRuleAncestor(n,i))return;const u=o.tokenize({css:l}),c=t.replaceComponentValues(t.parseCommaSeparatedListOfComponentValues(u),(s=>{if(!t.isFunctionNode(s)||!a.test(s.getName()))return;const r=e.color(s);return r&&!r.syntaxFlags.has(e.SyntaxFlag.Experimental)?e.serializeRGB(r):void 0})),p=t.stringify(c);if(p===l)return;let m=p;s?.subFeatures.displayP3&&(m=t.stringify(t.replaceComponentValues(t.parseCommaSeparatedListOfComponentValues(u),(s=>{if(!t.isFunctionNode(s)||!a.test(s.getName()))return;const r=e.color(s);return r&&!r.syntaxFlags.has(e.SyntaxFlag.Experimental)?e.colorDataFitsRGB_Gamut(r)?e.serializeRGB(r):e.serializeP3(r):void 0})))),n.cloneBefore({value:p}),s?.subFeatures.displayP3&&m!==p&&n.cloneBefore({value:m}),s?.preserve||n.remove()}});basePlugin.postcss=!0;const postcssPlugin=e=>{const r=Object.assign({enableProgressiveCustomProperties:!0,preserve:!1,subFeatures:{displayP3:!0}},e);return r.subFeatures=Object.assign({displayP3:!0},r.subFeatures),r.enableProgressiveCustomProperties&&(r.preserve||r.subFeatures.displayP3)?{postcssPlugin:"postcss-color-mix-function",plugins:[s(),basePlugin(r)]}:basePlugin(r)};postcssPlugin.postcss=!0,module.exports=postcssPlugin; +"use strict";var s=require("@csstools/postcss-progressive-custom-properties"),e=require("@csstools/css-color-parser"),r=require("@csstools/utilities"),t=require("@csstools/css-parser-algorithms"),o=require("@csstools/css-tokenizer");const a=/\bcolor-mix\(/i,i=/^color-mix$/i,basePlugin=s=>({postcssPlugin:"postcss-color-mix-function",Declaration(n){const l=n.value;if(!a.test(l))return;if(r.hasFallback(n))return;if(r.hasSupportsAtRuleAncestor(n,a))return;const u=o.tokenize({css:l}),c=t.replaceComponentValues(t.parseCommaSeparatedListOfComponentValues(u),(s=>{if(!t.isFunctionNode(s)||!i.test(s.getName()))return;const r=e.color(s);return!r||r.syntaxFlags.has(e.SyntaxFlag.Experimental)||r.syntaxFlags.has(e.SyntaxFlag.ColorMixVariadic)?void 0:e.serializeRGB(r)})),p=t.stringify(c);if(p===l)return;let m=p;s?.subFeatures.displayP3&&(m=t.stringify(t.replaceComponentValues(t.parseCommaSeparatedListOfComponentValues(u),(s=>{if(!t.isFunctionNode(s)||!i.test(s.getName()))return;const r=e.color(s);return r&&!r.syntaxFlags.has(e.SyntaxFlag.Experimental)?e.colorDataFitsRGB_Gamut(r)?e.serializeRGB(r):e.serializeP3(r):void 0})))),n.cloneBefore({value:p}),s?.subFeatures.displayP3&&m!==p&&n.cloneBefore({value:m}),s?.preserve||n.remove()}});basePlugin.postcss=!0;const postcssPlugin=e=>{const r=Object.assign({enableProgressiveCustomProperties:!0,preserve:!1,subFeatures:{displayP3:!0}},e);return r.subFeatures=Object.assign({displayP3:!0},r.subFeatures),r.enableProgressiveCustomProperties&&(r.preserve||r.subFeatures.displayP3)?{postcssPlugin:"postcss-color-mix-function",plugins:[s(),basePlugin(r)]}:basePlugin(r)};postcssPlugin.postcss=!0,module.exports=postcssPlugin; diff --git a/plugins/postcss-color-mix-function/dist/index.mjs b/plugins/postcss-color-mix-function/dist/index.mjs index 785d573328..805d26e517 100644 --- a/plugins/postcss-color-mix-function/dist/index.mjs +++ b/plugins/postcss-color-mix-function/dist/index.mjs @@ -1 +1 @@ -import s from"@csstools/postcss-progressive-custom-properties";import{color as e,SyntaxFlag as r,serializeRGB as t,colorDataFitsRGB_Gamut as o,serializeP3 as i}from"@csstools/css-color-parser";import{hasFallback as c,hasSupportsAtRuleAncestor as a}from"@csstools/utilities";import{replaceComponentValues as n,parseCommaSeparatedListOfComponentValues as l,isFunctionNode as u,stringify as p}from"@csstools/css-parser-algorithms";import{tokenize as m}from"@csstools/css-tokenizer";const f=/\bcolor-mix\(/i,g=/^color-mix$/i,basePlugin=s=>({postcssPlugin:"postcss-color-mix-function",Declaration(v){const b=v.value;if(!f.test(b))return;if(c(v))return;if(a(v,f))return;const P=m({css:b}),x=n(l(P),(s=>{if(!u(s)||!g.test(s.getName()))return;const o=e(s);return o&&!o.syntaxFlags.has(r.Experimental)?t(o):void 0})),d=p(x);if(d===b)return;let F=d;s?.subFeatures.displayP3&&(F=p(n(l(P),(s=>{if(!u(s)||!g.test(s.getName()))return;const c=e(s);return c&&!c.syntaxFlags.has(r.Experimental)?o(c)?t(c):i(c):void 0})))),v.cloneBefore({value:d}),s?.subFeatures.displayP3&&F!==d&&v.cloneBefore({value:F}),s?.preserve||v.remove()}});basePlugin.postcss=!0;const postcssPlugin=e=>{const r=Object.assign({enableProgressiveCustomProperties:!0,preserve:!1,subFeatures:{displayP3:!0}},e);return r.subFeatures=Object.assign({displayP3:!0},r.subFeatures),r.enableProgressiveCustomProperties&&(r.preserve||r.subFeatures.displayP3)?{postcssPlugin:"postcss-color-mix-function",plugins:[s(),basePlugin(r)]}:basePlugin(r)};postcssPlugin.postcss=!0;export{postcssPlugin as default}; +import s from"@csstools/postcss-progressive-custom-properties";import{color as e,SyntaxFlag as r,serializeRGB as t,colorDataFitsRGB_Gamut as o,serializeP3 as i}from"@csstools/css-color-parser";import{hasFallback as a,hasSupportsAtRuleAncestor as c}from"@csstools/utilities";import{replaceComponentValues as l,parseCommaSeparatedListOfComponentValues as n,isFunctionNode as u,stringify as p}from"@csstools/css-parser-algorithms";import{tokenize as m}from"@csstools/css-tokenizer";const f=/\bcolor-mix\(/i,g=/^color-mix$/i,basePlugin=s=>({postcssPlugin:"postcss-color-mix-function",Declaration(v){const b=v.value;if(!f.test(b))return;if(a(v))return;if(c(v,f))return;const x=m({css:b}),P=l(n(x),(s=>{if(!u(s)||!g.test(s.getName()))return;const o=e(s);return!o||o.syntaxFlags.has(r.Experimental)||o.syntaxFlags.has(r.ColorMixVariadic)?void 0:t(o)})),d=p(P);if(d===b)return;let F=d;s?.subFeatures.displayP3&&(F=p(l(n(x),(s=>{if(!u(s)||!g.test(s.getName()))return;const a=e(s);return a&&!a.syntaxFlags.has(r.Experimental)?o(a)?t(a):i(a):void 0})))),v.cloneBefore({value:d}),s?.subFeatures.displayP3&&F!==d&&v.cloneBefore({value:F}),s?.preserve||v.remove()}});basePlugin.postcss=!0;const postcssPlugin=e=>{const r=Object.assign({enableProgressiveCustomProperties:!0,preserve:!1,subFeatures:{displayP3:!0}},e);return r.subFeatures=Object.assign({displayP3:!0},r.subFeatures),r.enableProgressiveCustomProperties&&(r.preserve||r.subFeatures.displayP3)?{postcssPlugin:"postcss-color-mix-function",plugins:[s(),basePlugin(r)]}:basePlugin(r)};postcssPlugin.postcss=!0;export{postcssPlugin as default}; diff --git a/plugins/postcss-color-mix-function/src/index.ts b/plugins/postcss-color-mix-function/src/index.ts index cf7b417d04..fb0daab27f 100644 --- a/plugins/postcss-color-mix-function/src/index.ts +++ b/plugins/postcss-color-mix-function/src/index.ts @@ -50,6 +50,10 @@ const basePlugin: PluginCreator = (opts?: basePluginOptions) return; } + if (colorData.syntaxFlags.has(SyntaxFlag.ColorMixVariadic)) { + return; + } + return serializeRGB(colorData); }, ); diff --git a/plugins/postcss-color-mix-function/test/_tape.mjs b/plugins/postcss-color-mix-function/test/_tape.mjs index de43c48929..8d5069d23c 100644 --- a/plugins/postcss-color-mix-function/test/_tape.mjs +++ b/plugins/postcss-color-mix-function/test/_tape.mjs @@ -29,4 +29,16 @@ postcssTape(plugin)({ preserve: true, }, }, + 'variadic': { + message: 'ignores later syntax', + }, + 'examples/example': { + message: 'minimal example', + }, + 'examples/example:preserve-true': { + message: 'minimal example', + options: { + preserve: true, + }, + }, }); diff --git a/plugins/postcss-color-mix-function/test/basic.css b/plugins/postcss-color-mix-function/test/basic.css index 4d04da372b..3027dac7a1 100644 --- a/plugins/postcss-color-mix-function/test/basic.css +++ b/plugins/postcss-color-mix-function/test/basic.css @@ -167,3 +167,7 @@ .experimental-features-from-the-color-parser { color: color-mix(in srgb, contrast-color(pink) 30%, yellow); } + +.ignore-zero-percentage { + color: color-mix(in srgb, blue 0%, yellow 0%); +} diff --git a/plugins/postcss-color-mix-function/test/basic.expect.css b/plugins/postcss-color-mix-function/test/basic.expect.css index 906d019c9e..6ff78c674b 100644 --- a/plugins/postcss-color-mix-function/test/basic.expect.css +++ b/plugins/postcss-color-mix-function/test/basic.expect.css @@ -173,3 +173,7 @@ .experimental-features-from-the-color-parser { color: color-mix(in srgb, contrast-color(pink) 30%, yellow); } + +.ignore-zero-percentage { + color: color-mix(in srgb, blue 0%, yellow 0%); +} 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 e16a66b1b5..e160a91298 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 @@ -248,3 +248,7 @@ .experimental-features-from-the-color-parser { color: color-mix(in srgb, contrast-color(pink) 30%, yellow); } + +.ignore-zero-percentage { + 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 e16a66b1b5..e160a91298 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 @@ -248,3 +248,7 @@ .experimental-features-from-the-color-parser { color: color-mix(in srgb, contrast-color(pink) 30%, yellow); } + +.ignore-zero-percentage { + color: color-mix(in srgb, blue 0%, yellow 0%); +} diff --git a/plugins/postcss-color-mix-function/test/variadic.css b/plugins/postcss-color-mix-function/test/variadic.css new file mode 100644 index 0000000000..ce07a06a03 --- /dev/null +++ b/plugins/postcss-color-mix-function/test/variadic.css @@ -0,0 +1,11 @@ +.variadic { + color: color-mix(in srgb, red); +} + +.variadic { + color: color-mix(in srgb, red, green, blue); +} + +.variadic { + color: color-mix(in srgb, cyan, magenta, yellow); +} diff --git a/plugins/postcss-color-mix-function/test/variadic.expect.css b/plugins/postcss-color-mix-function/test/variadic.expect.css new file mode 100644 index 0000000000..ce07a06a03 --- /dev/null +++ b/plugins/postcss-color-mix-function/test/variadic.expect.css @@ -0,0 +1,11 @@ +.variadic { + color: color-mix(in srgb, red); +} + +.variadic { + color: color-mix(in srgb, red, green, blue); +} + +.variadic { + color: color-mix(in srgb, cyan, magenta, yellow); +} diff --git a/plugins/postcss-color-mix-variadic-function-arguments/.gitignore b/plugins/postcss-color-mix-variadic-function-arguments/.gitignore new file mode 100644 index 0000000000..e5b28db4ad --- /dev/null +++ b/plugins/postcss-color-mix-variadic-function-arguments/.gitignore @@ -0,0 +1,6 @@ +node_modules +package-lock.json +yarn.lock +*.result.css +*.result.css.map +*.result.html diff --git a/plugins/postcss-color-mix-variadic-function-arguments/.nvmrc b/plugins/postcss-color-mix-variadic-function-arguments/.nvmrc new file mode 100644 index 0000000000..bb8c76c68e --- /dev/null +++ b/plugins/postcss-color-mix-variadic-function-arguments/.nvmrc @@ -0,0 +1 @@ +v22.11.0 diff --git a/plugins/postcss-color-mix-variadic-function-arguments/CHANGELOG.md b/plugins/postcss-color-mix-variadic-function-arguments/CHANGELOG.md new file mode 100644 index 0000000000..dd5601e4f3 --- /dev/null +++ b/plugins/postcss-color-mix-variadic-function-arguments/CHANGELOG.md @@ -0,0 +1,5 @@ +# Changes to PostCSS Color Mix Variadic Function Arguments + +### Unreleased (major) + +- Initial version diff --git a/plugins/postcss-color-mix-variadic-function-arguments/INSTALL.md b/plugins/postcss-color-mix-variadic-function-arguments/INSTALL.md new file mode 100644 index 0000000000..876ac90afb --- /dev/null +++ b/plugins/postcss-color-mix-variadic-function-arguments/INSTALL.md @@ -0,0 +1,235 @@ +# Installing PostCSS Color Mix Variadic Function Arguments + +[PostCSS Color Mix Variadic Function Arguments] runs in all Node environments, with special instructions for: + +- [Node](#node) +- [PostCSS CLI](#postcss-cli) +- [PostCSS Load Config](#postcss-load-config) +- [Webpack](#webpack) +- [Next.js](#nextjs) +- [Gulp](#gulp) +- [Grunt](#grunt) + + + +## Node + +Add [PostCSS Color Mix Variadic Function Arguments] to your project: + +```bash +npm install postcss @csstools/postcss-color-mix-variadic-function-arguments --save-dev +``` + +Use it as a [PostCSS] plugin: + +```js +// commonjs +const postcss = require('postcss'); +const postcssColorMixVariadicFunctionArguments = require('@csstools/postcss-color-mix-variadic-function-arguments'); + +postcss([ + postcssColorMixVariadicFunctionArguments(/* pluginOptions */) +]).process(YOUR_CSS /*, processOptions */); +``` + +```js +// esm +import postcss from 'postcss'; +import postcssColorMixVariadicFunctionArguments from '@csstools/postcss-color-mix-variadic-function-arguments'; + +postcss([ + postcssColorMixVariadicFunctionArguments(/* pluginOptions */) +]).process(YOUR_CSS /*, processOptions */); +``` + +## PostCSS CLI + +Add [PostCSS CLI] to your project: + +```bash +npm install postcss-cli @csstools/postcss-color-mix-variadic-function-arguments --save-dev +``` + +Use [PostCSS Color Mix Variadic Function Arguments] in your `postcss.config.js` configuration file: + +```js +const postcssColorMixVariadicFunctionArguments = require('@csstools/postcss-color-mix-variadic-function-arguments'); + +module.exports = { + plugins: [ + postcssColorMixVariadicFunctionArguments(/* pluginOptions */) + ] +} +``` + +## PostCSS Load Config + +If your framework/CLI supports [`postcss-load-config`](https://github.com/postcss/postcss-load-config). + +```bash +npm install @csstools/postcss-color-mix-variadic-function-arguments --save-dev +``` + +`package.json`: + +```json +{ + "postcss": { + "plugins": { + "@csstools/postcss-color-mix-variadic-function-arguments": {} + } + } +} +``` + +`.postcssrc.json`: + +```json +{ + "plugins": { + "@csstools/postcss-color-mix-variadic-function-arguments": {} + } +} +``` + +_See the [README of `postcss-load-config`](https://github.com/postcss/postcss-load-config#usage) for more usage options._ + +## Webpack + +_Webpack version 5_ + +Add [PostCSS Loader] to your project: + +```bash +npm install postcss-loader @csstools/postcss-color-mix-variadic-function-arguments --save-dev +``` + +Use [PostCSS Color Mix Variadic Function Arguments] in your Webpack configuration: + +```js +module.exports = { + module: { + rules: [ + { + test: /\.css$/i, + use: [ + "style-loader", + { + loader: "css-loader", + options: { importLoaders: 1 }, + }, + { + loader: "postcss-loader", + options: { + postcssOptions: { + plugins: [ + // Other plugins, + [ + "@csstools/postcss-color-mix-variadic-function-arguments", + { + // Options + }, + ], + ], + }, + }, + }, + ], + }, + ], + }, +}; +``` + +## Next.js + +Read the instructions on how to [customize the PostCSS configuration in Next.js](https://nextjs.org/docs/advanced-features/customizing-postcss-config) + +```bash +npm install @csstools/postcss-color-mix-variadic-function-arguments --save-dev +``` + +Use [PostCSS Color Mix Variadic Function Arguments] in your `postcss.config.json` file: + +```json +{ + "plugins": [ + "@csstools/postcss-color-mix-variadic-function-arguments" + ] +} +``` + +```json5 +{ + "plugins": [ + [ + "@csstools/postcss-color-mix-variadic-function-arguments", + { + // Optionally add plugin options + } + ] + ] +} +``` + +## Gulp + +Add [Gulp PostCSS] to your project: + +```bash +npm install gulp-postcss @csstools/postcss-color-mix-variadic-function-arguments --save-dev +``` + +Use [PostCSS Color Mix Variadic Function Arguments] in your Gulpfile: + +```js +const postcss = require('gulp-postcss'); +const postcssColorMixVariadicFunctionArguments = require('@csstools/postcss-color-mix-variadic-function-arguments'); + +gulp.task('css', function () { + var plugins = [ + postcssColorMixVariadicFunctionArguments(/* pluginOptions */) + ]; + + return gulp.src('./src/*.css') + .pipe(postcss(plugins)) + .pipe(gulp.dest('.')); +}); +``` + +## Grunt + +Add [Grunt PostCSS] to your project: + +```bash +npm install grunt-postcss @csstools/postcss-color-mix-variadic-function-arguments --save-dev +``` + +Use [PostCSS Color Mix Variadic Function Arguments] in your Gruntfile: + +```js +const postcssColorMixVariadicFunctionArguments = require('@csstools/postcss-color-mix-variadic-function-arguments'); + +grunt.loadNpmTasks('grunt-postcss'); + +grunt.initConfig({ + postcss: { + options: { + processors: [ + postcssColorMixVariadicFunctionArguments(/* pluginOptions */) + ] + }, + dist: { + src: '*.css' + } + } +}); +``` + +[Gulp PostCSS]: https://github.com/postcss/gulp-postcss +[Grunt PostCSS]: https://github.com/nDmitry/grunt-postcss +[PostCSS]: https://github.com/postcss/postcss +[PostCSS CLI]: https://github.com/postcss/postcss-cli +[PostCSS Loader]: https://github.com/postcss/postcss-loader +[PostCSS Color Mix Variadic Function Arguments]: https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-color-mix-variadic-function-arguments +[Next.js]: https://nextjs.org diff --git a/plugins/postcss-color-mix-variadic-function-arguments/LICENSE.md b/plugins/postcss-color-mix-variadic-function-arguments/LICENSE.md new file mode 100644 index 0000000000..e8ae93b9f9 --- /dev/null +++ b/plugins/postcss-color-mix-variadic-function-arguments/LICENSE.md @@ -0,0 +1,18 @@ +MIT No Attribution (MIT-0) + +Copyright © CSSTools Contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the “Software”), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/plugins/postcss-color-mix-variadic-function-arguments/README.md b/plugins/postcss-color-mix-variadic-function-arguments/README.md new file mode 100644 index 0000000000..462cfeed9d --- /dev/null +++ b/plugins/postcss-color-mix-variadic-function-arguments/README.md @@ -0,0 +1,104 @@ +# PostCSS Color Mix Variadic Function Arguments [PostCSS Logo][PostCSS] + +[npm version][npm-url] [Build Status][cli-url] [Discord][discord]

[Baseline Status][css-url] [CSS Standard Status][css-url] + +```bash +npm install @csstools/postcss-color-mix-variadic-function-arguments --save-dev +``` + +[PostCSS Color Mix Variadic Function Arguments] lets you use the `color-mix()` function with any number of arguments following the [CSS Color 5 Specification]. + +```css +.red { + color: color-mix(in srgb, red); +} + +.grey { + color: color-mix(in srgb, red, lime, blue); +} + +/* becomes */ + +.red { + color: rgb(255, 0, 0); +} + +.grey { + color: rgb(85, 85, 85); +} +``` + +> [!NOTE] +> We can not dynamically resolve `var()` arguments in `color-mix()`, only static values will work. + +## Usage + +Add [PostCSS Color Mix Variadic Function Arguments] to your project: + +```bash +npm install postcss @csstools/postcss-color-mix-variadic-function-arguments --save-dev +``` + +Use it as a [PostCSS] plugin: + +```js +const postcss = require('postcss'); +const postcssColorMixVariadicFunctionArguments = require('@csstools/postcss-color-mix-variadic-function-arguments'); + +postcss([ + postcssColorMixVariadicFunctionArguments(/* pluginOptions */) +]).process(YOUR_CSS /*, processOptions */); +``` + +[PostCSS Color Mix Variadic Function Arguments] runs in all Node environments, with special +instructions for: + +- [Node](INSTALL.md#node) +- [PostCSS CLI](INSTALL.md#postcss-cli) +- [PostCSS Load Config](INSTALL.md#postcss-load-config) +- [Webpack](INSTALL.md#webpack) +- [Next.js](INSTALL.md#nextjs) +- [Gulp](INSTALL.md#gulp) +- [Grunt](INSTALL.md#grunt) + +## Options + +### preserve + +The `preserve` option determines whether the original notation +is preserved. By default, it is not preserved. + +```js +postcssColorMixVariadicFunctionArguments({ preserve: true }) +``` + +```css +.red { + color: color-mix(in srgb, red); +} + +.grey { + color: color-mix(in srgb, red, lime, blue); +} + +/* becomes */ + +.red { + color: rgb(255, 0, 0); + color: color-mix(in srgb, red); +} + +.grey { + color: rgb(85, 85, 85); + color: color-mix(in srgb, red, lime, blue); +} +``` + +[cli-url]: https://github.com/csstools/postcss-plugins/actions/workflows/test.yml?query=workflow/test +[css-url]: https://cssdb.org/#color-mix-variadic-function-arguments +[discord]: https://discord.gg/bUadyRwkJS +[npm-url]: https://www.npmjs.com/package/@csstools/postcss-color-mix-variadic-function-arguments + +[PostCSS]: https://github.com/postcss/postcss +[PostCSS Color Mix Variadic Function Arguments]: https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-color-mix-variadic-function-arguments +[CSS Color 5 Specification]: https://www.w3.org/TR/css-color-5/#color-mix diff --git a/plugins/postcss-color-mix-variadic-function-arguments/api-extractor.json b/plugins/postcss-color-mix-variadic-function-arguments/api-extractor.json new file mode 100644 index 0000000000..42058be517 --- /dev/null +++ b/plugins/postcss-color-mix-variadic-function-arguments/api-extractor.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", + "extends": "../../api-extractor.json" +} diff --git a/plugins/postcss-color-mix-variadic-function-arguments/dist/index.cjs b/plugins/postcss-color-mix-variadic-function-arguments/dist/index.cjs new file mode 100644 index 0000000000..4e46ab7fa0 --- /dev/null +++ b/plugins/postcss-color-mix-variadic-function-arguments/dist/index.cjs @@ -0,0 +1 @@ +"use strict";var s=require("@csstools/postcss-progressive-custom-properties"),e=require("@csstools/css-color-parser"),r=require("@csstools/utilities"),t=require("@csstools/css-parser-algorithms"),o=require("@csstools/css-tokenizer");const a=/\bcolor-mix\(/i,i=/^color-mix$/i,basePlugin=s=>({postcssPlugin:"color-mix-variadic-function-arguments",Declaration(n){const l=n.value;if(!a.test(l))return;if(r.hasFallback(n))return;if(r.hasSupportsAtRuleAncestor(n,a))return;const u=o.tokenize({css:l}),c=t.replaceComponentValues(t.parseCommaSeparatedListOfComponentValues(u),(s=>{if(!t.isFunctionNode(s)||!i.test(s.getName()))return;const r=e.color(s);return r&&!r.syntaxFlags.has(e.SyntaxFlag.Experimental)&&r.syntaxFlags.has(e.SyntaxFlag.ColorMixVariadic)?e.serializeRGB(r):void 0})),p=t.stringify(c);if(p===l)return;let m=p;s?.subFeatures.displayP3&&(m=t.stringify(t.replaceComponentValues(t.parseCommaSeparatedListOfComponentValues(u),(s=>{if(!t.isFunctionNode(s)||!i.test(s.getName()))return;const r=e.color(s);return r&&!r.syntaxFlags.has(e.SyntaxFlag.Experimental)?e.colorDataFitsRGB_Gamut(r)?e.serializeRGB(r):e.serializeP3(r):void 0})))),n.cloneBefore({value:p}),s?.subFeatures.displayP3&&m!==p&&n.cloneBefore({value:m}),s?.preserve||n.remove()}});basePlugin.postcss=!0;const postcssPlugin=e=>{const r=Object.assign({enableProgressiveCustomProperties:!0,preserve:!1,subFeatures:{displayP3:!0}},e);return r.subFeatures=Object.assign({displayP3:!0},r.subFeatures),r.enableProgressiveCustomProperties&&(r.preserve||r.subFeatures.displayP3)?{postcssPlugin:"color-mix-variadic-function-arguments",plugins:[s(),basePlugin(r)]}:basePlugin(r)};postcssPlugin.postcss=!0,module.exports=postcssPlugin; diff --git a/plugins/postcss-color-mix-variadic-function-arguments/dist/index.d.ts b/plugins/postcss-color-mix-variadic-function-arguments/dist/index.d.ts new file mode 100644 index 0000000000..b84753f3a6 --- /dev/null +++ b/plugins/postcss-color-mix-variadic-function-arguments/dist/index.d.ts @@ -0,0 +1,19 @@ +import type { PluginCreator } from 'postcss'; + +/** color-mix-variadic-function-arguments plugin options */ +export declare type pluginOptions = { + /** Preserve the original notation. default: false */ + preserve?: boolean; + /** Enable "@csstools/postcss-progressive-custom-properties". default: true */ + enableProgressiveCustomProperties?: boolean; + /** Toggle sub features. default: { displayP3: true } */ + subFeatures?: { + /** Enable displayP3 fallbacks. default: true */ + displayP3?: boolean; + }; +}; + +declare const postcssPlugin: PluginCreator; +export default postcssPlugin; + +export { } diff --git a/plugins/postcss-color-mix-variadic-function-arguments/dist/index.mjs b/plugins/postcss-color-mix-variadic-function-arguments/dist/index.mjs new file mode 100644 index 0000000000..e3da3bafcf --- /dev/null +++ b/plugins/postcss-color-mix-variadic-function-arguments/dist/index.mjs @@ -0,0 +1 @@ +import s from"@csstools/postcss-progressive-custom-properties";import{color as e,SyntaxFlag as r,serializeRGB as t,colorDataFitsRGB_Gamut as o,serializeP3 as i}from"@csstools/css-color-parser";import{hasFallback as a,hasSupportsAtRuleAncestor as n}from"@csstools/utilities";import{replaceComponentValues as c,parseCommaSeparatedListOfComponentValues as l,isFunctionNode as u,stringify as p}from"@csstools/css-parser-algorithms";import{tokenize as m}from"@csstools/css-tokenizer";const f=/\bcolor-mix\(/i,g=/^color-mix$/i,basePlugin=s=>({postcssPlugin:"color-mix-variadic-function-arguments",Declaration(v){const b=v.value;if(!f.test(b))return;if(a(v))return;if(n(v,f))return;const d=m({css:b}),x=c(l(d),(s=>{if(!u(s)||!g.test(s.getName()))return;const o=e(s);return o&&!o.syntaxFlags.has(r.Experimental)&&o.syntaxFlags.has(r.ColorMixVariadic)?t(o):void 0})),P=p(x);if(P===b)return;let F=P;s?.subFeatures.displayP3&&(F=p(c(l(d),(s=>{if(!u(s)||!g.test(s.getName()))return;const a=e(s);return a&&!a.syntaxFlags.has(r.Experimental)?o(a)?t(a):i(a):void 0})))),v.cloneBefore({value:P}),s?.subFeatures.displayP3&&F!==P&&v.cloneBefore({value:F}),s?.preserve||v.remove()}});basePlugin.postcss=!0;const postcssPlugin=e=>{const r=Object.assign({enableProgressiveCustomProperties:!0,preserve:!1,subFeatures:{displayP3:!0}},e);return r.subFeatures=Object.assign({displayP3:!0},r.subFeatures),r.enableProgressiveCustomProperties&&(r.preserve||r.subFeatures.displayP3)?{postcssPlugin:"color-mix-variadic-function-arguments",plugins:[s(),basePlugin(r)]}:basePlugin(r)};postcssPlugin.postcss=!0;export{postcssPlugin as default}; diff --git a/plugins/postcss-color-mix-variadic-function-arguments/docs/README.md b/plugins/postcss-color-mix-variadic-function-arguments/docs/README.md new file mode 100644 index 0000000000..8d7277f488 --- /dev/null +++ b/plugins/postcss-color-mix-variadic-function-arguments/docs/README.md @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + +
+ +[] lets you use the `color-mix()` function with any number of arguments following the [CSS Color 5 Specification]. + +```css + + +/* becomes */ + + +``` + +> [!NOTE] +> We can not dynamically resolve `var()` arguments in `color-mix()`, only static values will work. + + + + + +## Options + +### preserve + +The `preserve` option determines whether the original notation +is preserved. By default, it is not preserved. + +```js +({ preserve: true }) +``` + +```css + + +/* becomes */ + + +``` + + +[CSS Color 5 Specification]: diff --git a/plugins/postcss-color-mix-variadic-function-arguments/package.json b/plugins/postcss-color-mix-variadic-function-arguments/package.json new file mode 100644 index 0000000000..2b7a694bdf --- /dev/null +++ b/plugins/postcss-color-mix-variadic-function-arguments/package.json @@ -0,0 +1,102 @@ +{ + "name": "@csstools/postcss-color-mix-variadic-function-arguments", + "description": "Use the color-mix function in CSS", + "version": "0.0.0", + "contributors": [ + { + "name": "Antonio Laguna", + "email": "antonio@laguna.es", + "url": "https://antonio.laguna.es" + }, + { + "name": "Romain Menke", + "email": "romainmenke@gmail.com" + } + ], + "license": "MIT-0", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "engines": { + "node": ">=18" + }, + "type": "module", + "main": "dist/index.cjs", + "module": "dist/index.mjs", + "exports": { + ".": { + "import": { + "types": "./dist/index.d.ts", + "default": "./dist/index.mjs" + }, + "require": { + "default": "./dist/index.cjs" + } + } + }, + "files": [ + "CHANGELOG.md", + "LICENSE.md", + "README.md", + "dist" + ], + "dependencies": { + "@csstools/css-color-parser": "^3.0.9", + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3", + "@csstools/postcss-progressive-custom-properties": "^4.0.1", + "@csstools/utilities": "^2.0.0" + }, + "peerDependencies": { + "postcss": "^8.4" + }, + "devDependencies": { + "@csstools/postcss-tape": "*" + }, + "scripts": { + "build": "rollup -c ../../rollup/default.mjs", + "docs": "node ../../.github/bin/generate-docs/install.mjs && node ../../.github/bin/generate-docs/readme.mjs", + "lint": "node ../../.github/bin/format-package-json.mjs", + "prepublishOnly": "npm run build && npm run test", + "test": "node --test", + "test:rewrite-expects": "REWRITE_EXPECTS=true node --test" + }, + "homepage": "https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-color-mix-variadic-function-arguments#readme", + "repository": { + "type": "git", + "url": "git+https://github.com/csstools/postcss-plugins.git", + "directory": "plugins/postcss-color-mix-variadic-function-arguments" + }, + "bugs": "https://github.com/csstools/postcss-plugins/issues", + "keywords": [ + "color-mix", + "css", + "display-p3", + "interpolation", + "postcss-plugin", + "prophoto-rgb", + "rec2020", + "rgb", + "rgba", + "srgb-linear", + "syntax", + "xyz", + "variadic" + ], + "csstools": { + "cssdbId": "color-mix-variadic-function-arguments", + "exportName": "postcssColorMixVariadicFunctionArguments", + "humanReadableName": "PostCSS Color Mix Variadic Function Arguments", + "specUrl": "https://www.w3.org/TR/css-color-5/#color-mix" + }, + "volta": { + "extends": "../../package.json" + } +} diff --git a/plugins/postcss-color-mix-variadic-function-arguments/src/index.ts b/plugins/postcss-color-mix-variadic-function-arguments/src/index.ts new file mode 100644 index 0000000000..5df17a878c --- /dev/null +++ b/plugins/postcss-color-mix-variadic-function-arguments/src/index.ts @@ -0,0 +1,151 @@ +import postcssProgressiveCustomProperties from '@csstools/postcss-progressive-custom-properties'; +import type { Declaration, PluginCreator } from 'postcss'; +import { SyntaxFlag, color, colorDataFitsRGB_Gamut, serializeP3, serializeRGB } from '@csstools/css-color-parser'; +import { hasFallback, hasSupportsAtRuleAncestor } from '@csstools/utilities'; +import { isFunctionNode, parseCommaSeparatedListOfComponentValues, replaceComponentValues, stringify } from '@csstools/css-parser-algorithms'; +import { tokenize } from '@csstools/css-tokenizer'; + +type basePluginOptions = { + preserve: boolean, + subFeatures: { + displayP3: boolean + } +}; + +const COLOR_MIX_FUNCTION_REGEX = /\bcolor-mix\(/i; +const COLOR_MIX_NAME_REGEX = /^color-mix$/i; + +/* Transform color-mix() functions in CSS. */ +const basePlugin: PluginCreator = (opts?: basePluginOptions) => { + return { + postcssPlugin: 'color-mix-variadic-function-arguments', + Declaration(decl: Declaration): void { + const originalValue = decl.value; + if (!(COLOR_MIX_FUNCTION_REGEX.test(originalValue))) { + return; + } + + if (hasFallback(decl)) { + return; + } + + if (hasSupportsAtRuleAncestor(decl, COLOR_MIX_FUNCTION_REGEX)) { + return; + } + + const tokens = tokenize({ css: originalValue }); + const replacedRGB = replaceComponentValues( + parseCommaSeparatedListOfComponentValues(tokens), + (componentValue) => { + if (!isFunctionNode(componentValue) || !COLOR_MIX_NAME_REGEX.test(componentValue.getName())) { + return; + } + + const colorData = color(componentValue); + if (!colorData) { + return; + } + + if (colorData.syntaxFlags.has(SyntaxFlag.Experimental)) { + return; + } + + if (!colorData.syntaxFlags.has(SyntaxFlag.ColorMixVariadic)) { + return; + } + + return serializeRGB(colorData); + }, + ); + + const modifiedRGB = stringify(replacedRGB); + if (modifiedRGB === originalValue) { + return; + } + + let modifiedP3 = modifiedRGB; + if (opts?.subFeatures.displayP3) { + modifiedP3 = stringify(replaceComponentValues( + parseCommaSeparatedListOfComponentValues(tokens), + (componentValue) => { + if (!isFunctionNode(componentValue) || !COLOR_MIX_NAME_REGEX.test(componentValue.getName())) { + return; + } + + const colorData = color(componentValue); + if (!colorData) { + return; + } + + if (colorData.syntaxFlags.has(SyntaxFlag.Experimental)) { + return; + } + + if (colorDataFitsRGB_Gamut(colorData)) { + return serializeRGB(colorData); + } + + return serializeP3(colorData); + }, + )); + } + + decl.cloneBefore({ value: modifiedRGB }); + + if (opts?.subFeatures.displayP3 && modifiedP3 !== modifiedRGB) { + decl.cloneBefore({ value: modifiedP3 }); + } + + if (!opts?.preserve) { + decl.remove(); + } + }, + }; +}; + +basePlugin.postcss = true; + +/** color-mix-variadic-function-arguments plugin options */ +export type pluginOptions = { + /** Preserve the original notation. default: false */ + preserve?: boolean, + /** Enable "@csstools/postcss-progressive-custom-properties". default: true */ + enableProgressiveCustomProperties?: boolean, + /** Toggle sub features. default: { displayP3: true } */ + subFeatures?: { + /** Enable displayP3 fallbacks. default: true */ + displayP3?: boolean + } +}; + +/* Transform color-mix() functions in CSS. */ +const postcssPlugin: PluginCreator = (opts?: pluginOptions) => { + const options = Object.assign({ + enableProgressiveCustomProperties: true, + preserve: false, + subFeatures: { + displayP3: true, + }, + }, opts); + + // deep merge + options.subFeatures = Object.assign({ + displayP3: true, + }, options.subFeatures); + + if (options.enableProgressiveCustomProperties && (options.preserve || options.subFeatures.displayP3)) { + return { + postcssPlugin: 'color-mix-variadic-function-arguments', + plugins: [ + postcssProgressiveCustomProperties(), + basePlugin(options), + ], + }; + } + + return basePlugin(options); +}; + +postcssPlugin.postcss = true; + +export default postcssPlugin; diff --git a/plugins/postcss-color-mix-variadic-function-arguments/test/_import.mjs b/plugins/postcss-color-mix-variadic-function-arguments/test/_import.mjs new file mode 100644 index 0000000000..b19e8e4f64 --- /dev/null +++ b/plugins/postcss-color-mix-variadic-function-arguments/test/_import.mjs @@ -0,0 +1,6 @@ +import assert from 'node:assert'; +import plugin from '@csstools/postcss-color-mix-variadic-function-arguments'; +plugin(); + +assert.ok(plugin.postcss, 'should have "postcss flag"'); +assert.equal(typeof plugin, 'function', 'should return a function'); diff --git a/plugins/postcss-color-mix-variadic-function-arguments/test/_require.cjs b/plugins/postcss-color-mix-variadic-function-arguments/test/_require.cjs new file mode 100644 index 0000000000..efde985ed5 --- /dev/null +++ b/plugins/postcss-color-mix-variadic-function-arguments/test/_require.cjs @@ -0,0 +1,6 @@ +const assert = require('assert'); +const plugin = require('@csstools/postcss-color-mix-variadic-function-arguments'); +plugin(); + +assert.ok(plugin.postcss, 'should have "postcss flag"'); +assert.equal(typeof plugin, 'function', 'should return a function'); diff --git a/plugins/postcss-color-mix-variadic-function-arguments/test/_tape.mjs b/plugins/postcss-color-mix-variadic-function-arguments/test/_tape.mjs new file mode 100644 index 0000000000..50a43a3ad4 --- /dev/null +++ b/plugins/postcss-color-mix-variadic-function-arguments/test/_tape.mjs @@ -0,0 +1,41 @@ +import { postcssTape, ruleClonerPlugin } from '@csstools/postcss-tape'; +import plugin from '@csstools/postcss-color-mix-variadic-function-arguments'; + +postcssTape(plugin)({ + 'basic': { + message: 'supports basic usage', + }, + 'basic:preserve-true': { + message: 'supports basic usage with { preserve: true }', + options: { + preserve: true, + }, + }, + 'basic:with-cloned-rules': { + message: 'doesn\'t cause duplicate CSS', + plugins: [ + ruleClonerPlugin, + plugin({ + preserve: true, + }), + ], + }, + 'variables': { + message: 'supports variables', + }, + 'variables:preserve-true': { + message: 'supports variables with { preserve: true } usage', + options: { + preserve: true, + }, + }, + 'examples/example': { + message: 'minimal example', + }, + 'examples/example:preserve-true': { + message: 'minimal example', + options: { + preserve: true, + }, + }, +}); diff --git a/plugins/postcss-color-mix-variadic-function-arguments/test/basic.css b/plugins/postcss-color-mix-variadic-function-arguments/test/basic.css new file mode 100644 index 0000000000..169c94572a --- /dev/null +++ b/plugins/postcss-color-mix-variadic-function-arguments/test/basic.css @@ -0,0 +1,15 @@ +.variadic { + color: color-mix(in srgb, red); +} + +.variadic { + color: color-mix(in srgb, red, green, blue); +} + +.variadic { + color: color-mix(in srgb, cyan, magenta, yellow); +} + +.zero-percentage { + 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 new file mode 100644 index 0000000000..b97ba910a8 --- /dev/null +++ b/plugins/postcss-color-mix-variadic-function-arguments/test/basic.expect.css @@ -0,0 +1,15 @@ +.variadic { + color: rgb(255, 0, 0); +} + +.variadic { + color: rgb(85, 43, 85); +} + +.variadic { + color: rgb(170, 170, 170); +} + +.zero-percentage { + color: rgba(0, 0, 0, 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 new file mode 100644 index 0000000000..2911e7a379 --- /dev/null +++ b/plugins/postcss-color-mix-variadic-function-arguments/test/basic.preserve-true.expect.css @@ -0,0 +1,19 @@ +.variadic { + color: rgb(255, 0, 0); + color: color-mix(in srgb, red); +} + +.variadic { + color: rgb(85, 43, 85); + color: color-mix(in srgb, red, green, blue); +} + +.variadic { + color: rgb(170, 170, 170); + color: color-mix(in srgb, cyan, magenta, yellow); +} + +.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 new file mode 100644 index 0000000000..2911e7a379 --- /dev/null +++ b/plugins/postcss-color-mix-variadic-function-arguments/test/basic.with-cloned-rules.expect.css @@ -0,0 +1,19 @@ +.variadic { + color: rgb(255, 0, 0); + color: color-mix(in srgb, red); +} + +.variadic { + color: rgb(85, 43, 85); + color: color-mix(in srgb, red, green, blue); +} + +.variadic { + color: rgb(170, 170, 170); + color: color-mix(in srgb, cyan, magenta, yellow); +} + +.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/examples/example.css b/plugins/postcss-color-mix-variadic-function-arguments/test/examples/example.css new file mode 100644 index 0000000000..2f4b71ff5d --- /dev/null +++ b/plugins/postcss-color-mix-variadic-function-arguments/test/examples/example.css @@ -0,0 +1,7 @@ +.red { + color: color-mix(in srgb, red); +} + +.grey { + color: color-mix(in srgb, red, lime, blue); +} diff --git a/plugins/postcss-color-mix-variadic-function-arguments/test/examples/example.expect.css b/plugins/postcss-color-mix-variadic-function-arguments/test/examples/example.expect.css new file mode 100644 index 0000000000..7826e136a9 --- /dev/null +++ b/plugins/postcss-color-mix-variadic-function-arguments/test/examples/example.expect.css @@ -0,0 +1,7 @@ +.red { + color: rgb(255, 0, 0); +} + +.grey { + color: rgb(85, 85, 85); +} diff --git a/plugins/postcss-color-mix-variadic-function-arguments/test/examples/example.preserve-true.expect.css b/plugins/postcss-color-mix-variadic-function-arguments/test/examples/example.preserve-true.expect.css new file mode 100644 index 0000000000..0fc5bb3977 --- /dev/null +++ b/plugins/postcss-color-mix-variadic-function-arguments/test/examples/example.preserve-true.expect.css @@ -0,0 +1,9 @@ +.red { + color: rgb(255, 0, 0); + color: color-mix(in srgb, red); +} + +.grey { + color: rgb(85, 85, 85); + color: color-mix(in srgb, red, lime, blue); +} diff --git a/plugins/postcss-color-mix-variadic-function-arguments/test/variables.css b/plugins/postcss-color-mix-variadic-function-arguments/test/variables.css new file mode 100644 index 0000000000..34baba5a5f --- /dev/null +++ b/plugins/postcss-color-mix-variadic-function-arguments/test/variables.css @@ -0,0 +1,7 @@ +:root { + --firebrick: color-mix(in lch, red 90%); + --firebrick-a50: color-mix(in lch, red 90%); + + --opacity-50: 50%; + --firebrick-a50-var: color-mix(in lch, red var(--opacity-50)); +} diff --git a/plugins/postcss-color-mix-variadic-function-arguments/test/variables.expect.css b/plugins/postcss-color-mix-variadic-function-arguments/test/variables.expect.css new file mode 100644 index 0000000000..365b0599dc --- /dev/null +++ b/plugins/postcss-color-mix-variadic-function-arguments/test/variables.expect.css @@ -0,0 +1,7 @@ +:root { + --firebrick: rgba(255, 0, 0, 0.9); + --firebrick-a50: rgba(255, 0, 0, 0.9); + + --opacity-50: 50%; + --firebrick-a50-var: color-mix(in lch, red var(--opacity-50)); +} diff --git a/plugins/postcss-color-mix-variadic-function-arguments/test/variables.preserve-true.expect.css b/plugins/postcss-color-mix-variadic-function-arguments/test/variables.preserve-true.expect.css new file mode 100644 index 0000000000..52eb189fb7 --- /dev/null +++ b/plugins/postcss-color-mix-variadic-function-arguments/test/variables.preserve-true.expect.css @@ -0,0 +1,9 @@ +:root { + --firebrick: rgba(255, 0, 0, 0.9); + --firebrick: color-mix(in lch, red 90%); + --firebrick-a50: rgba(255, 0, 0, 0.9); + --firebrick-a50: color-mix(in lch, red 90%); + + --opacity-50: 50%; + --firebrick-a50-var: color-mix(in lch, red var(--opacity-50)); +} diff --git a/plugins/postcss-color-mix-variadic-function-arguments/tsconfig.json b/plugins/postcss-color-mix-variadic-function-arguments/tsconfig.json new file mode 100644 index 0000000000..500af6d266 --- /dev/null +++ b/plugins/postcss-color-mix-variadic-function-arguments/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist", + "declarationDir": ".", + "strict": true + }, + "include": ["./src/**/*"], + "exclude": ["dist"] +} From 6efd6df157c6b8721c0520469cf91081be73a8ee Mon Sep 17 00:00:00 2001 From: Romain Menke Date: Mon, 26 May 2025 22:23:54 +0200 Subject: [PATCH 2/5] wip --- package-lock.json | 35 + .../dist/index.json | 3 - packages/postcss-tape/dist/index.d.ts | 111 ---- packages/postcss-tape/dist/index.mjs | 3 - .../package.json | 4 +- .../test/basic.css | 8 + .../test/basic.expect.css | 10 + .../test/basic.preserve-true.expect.css | 12 + .../test/basic.with-cloned-rules.expect.css | 12 + .../test/variables.css | 2 +- .../test/variables.expect.css | 2 +- .../test/variables.preserve-true.expect.css | 11 +- .../CHANGELOG.md | 4 + .../dist/index.cjs | 2 +- .../dist/index.mjs | 2 +- .../scripts/color-mix.mjs | 30 +- .../scripts/matcher-for-value.mjs | 3 + .../src/match.ts | 7 +- .../src/matchers.ts | 617 ++++++++++++++++++ .../test/basic.css | 8 +- .../test/basic.expect.css | 13 +- 21 files changed, 764 insertions(+), 135 deletions(-) delete mode 100644 packages/postcss-tape/dist/index.d.ts delete mode 100644 packages/postcss-tape/dist/index.mjs diff --git a/package-lock.json b/package-lock.json index 8cb8d6a0a1..605d10bd63 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1917,6 +1917,10 @@ "resolved": "plugins/postcss-color-mix-function", "link": true }, + "node_modules/@csstools/postcss-color-mix-variadic-function-arguments": { + "resolved": "plugins/postcss-color-mix-variadic-function-arguments", + "link": true + }, "node_modules/@csstools/postcss-conditional-values": { "resolved": "plugins/postcss-conditional-values", "link": true @@ -10151,6 +10155,37 @@ "postcss": "^8.4" } }, + "plugins/postcss-color-mix-variadic-function-arguments": { + "name": "@csstools/postcss-color-mix-variadic-function-arguments", + "version": "0.0.0", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "dependencies": { + "@csstools/css-color-parser": "^3.0.9", + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3", + "@csstools/postcss-progressive-custom-properties": "^4.0.1", + "@csstools/utilities": "^2.0.0" + }, + "devDependencies": { + "@csstools/postcss-tape": "*" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, "plugins/postcss-color-rebeccapurple": { "version": "10.0.0", "funding": [ diff --git a/packages/css-syntax-patches-for-csstree/dist/index.json b/packages/css-syntax-patches-for-csstree/dist/index.json index afce6b584d..005df41f52 100644 --- a/packages/css-syntax-patches-for-csstree/dist/index.json +++ b/packages/css-syntax-patches-for-csstree/dist/index.json @@ -513,7 +513,6 @@ "calc-keyword": "e | pi | infinity | -infinity | NaN", "calc-mix()": "calc-mix( , , )", "calc-product": " [ [ '*' | / ] ]*", - "calc-size-basis": "[ | | any | ]", "calc-size()": "calc-size( , )", "calc-value": " | | | | ( )", "cf-image": "[ | ] && ?", @@ -533,7 +532,6 @@ "conic-gradient-syntax": "[ [ [ from [ | ] ]? [ at ]? ] || ]? , ", "conic-gradient()": "conic-gradient( [ ] )", "container-condition": "[ ? ? ]!", - "container-progress()": "container-progress( [ of ]? , , )", "container-query": "not | [ [ and ]* | [ or ]* ]", "content-level": "element | content | text | | ", "content-list": "[ | | | contents | | | | | | | | ]+", @@ -639,7 +637,6 @@ "media-condition": " | [ * | * ]", "media-condition-without-or": " | *", "media-or": "or ", - "media-progress()": "media-progress( , , )", "media()": "media( [ | | ] )", "mf-comparison": " | | ", "mf-eq": "'='", diff --git a/packages/postcss-tape/dist/index.d.ts b/packages/postcss-tape/dist/index.d.ts deleted file mode 100644 index 5a0c1b4542..0000000000 --- a/packages/postcss-tape/dist/index.d.ts +++ /dev/null @@ -1,111 +0,0 @@ -/** - * A test suite for PostCSS plugins. - * - * @example - * ```sh - * node --test - * ``` - * - * ```js - * // test/_tape.mjs - * import { postcssTape } from '@csstools/postcss-tape'; - * import plugin from ''; - * - * postcssTape(plugin)({ - * basic: { - * message: "supports basic usage", - * }, - * 'basic:color': { - * message: "supports { color: '' }", - * options: { - * color: 'purple' - * } - * }, - * }); - * ``` - * - * @packageDocumentation - */ - -import type { Declaration } from 'postcss'; -import type { Plugin } from 'postcss'; -import type { PluginCreator } from 'postcss'; - -/** - * A dummy PostCSS plugin that clones any at rule with params `to-clone` to a new at rule with params `cloned`. - */ -export declare const atRuleClonerPlugin: { - postcssPlugin: string; - prepare(): Plugin; -}; - -/** - * A dummy PostCSS plugin that clones any declaration with the property `to-clone` to a new declaration with the property `cloned`. - */ -export declare const declarationClonerPlugin: { - postcssPlugin: string; - Declaration(decl: Declaration): void; -}; - -/** - * General options for `@csstools/postcss-tape`. - * These affect the entire test run, not individual test cases. - * - * @example - * ```js - * import { postcssTape } from '@csstools/postcss-tape'; - * import plugin from 'your-postcss-plugin'; - * - * postcssTape(plugin, { - * skipPackageNameCheck: true, - * })(...); - * ``` - */ -export declare type Options = { - /** - * PostCSS plugins should start their name with `postcss-`. - * If this is something you do not want to do, you can set this to `true` to skip this check. - */ - skipPackageNameCheck?: boolean; -}; - -/** - * Create a test suite for a PostCSS plugin. - */ -export declare function postcssTape(pluginCreator: PluginCreator, runOptions?: Options): (options: Record) => Promise; - -/** - * A dummy PostCSS plugin that clones any rule with the selector `to-clone` to a new rule with the selector `cloned`. - */ -export declare const ruleClonerPlugin: { - postcssPlugin: string; - prepare(): Plugin; -}; - -/** - * Options for a test case. - */ -export declare interface TestCaseOptions { - /** Debug message */ - message?: string; - /** Plugin options. Only used if `plugins` is not specified. */ - options?: unknown; - /** Plugins to use. When specified the original plugin is not used. */ - plugins?: Array; - /** The expected number of warnings. */ - warnings?: number; - /** Expected exception */ - exception?: RegExp; - /** Override the file name of the "source" file. */ - source?: string; - /** Override the file name of the "expect" file. */ - expect?: string; - /** Override the file name of the "result" file. */ - result?: string; - /** Do something before the test is run. */ - before?: () => void | Promise; - /** Do something after the test is run. */ - after?: () => void | Promise; -} - -export { } diff --git a/packages/postcss-tape/dist/index.mjs b/packages/postcss-tape/dist/index.mjs deleted file mode 100644 index b046d0df5f..0000000000 --- a/packages/postcss-tape/dist/index.mjs +++ /dev/null @@ -1,3 +0,0 @@ -/* node:coverage disable */ -import e from"node:assert/strict";import s from"node:fs/promises";import t from"node:fs";import o from"node:path";import n from"postcss";import r from"postcss-8.4";import i from"node:test";import a from"node:url";const noopPlugin=()=>({postcssPlugin:"noop-plugin",Once(){}});async function fileContentsOrEmptyString(e){try{return await s.readFile(e,"utf8")}catch{return""}}function reduceInformationInCssSyntaxError(e){process.env.DEBUG||(delete e.source,e.input&&delete e.input.source,delete e.postcssNode)}function postcssTape(a,c){return async p=>{c=c??{},await i("`postcss` flag is set on exported plugin creator",(()=>{e.equal(a.postcss,!0)})),await i("exported plugin creator is a function",(()=>{e.equal(typeof a,"function")})),await i("`postcssPlugin` is set on a plugin instance",(()=>{const s=a();e.ok(s.postcssPlugin),e.equal(typeof s.postcssPlugin,"string")})),await i("package.json",(async t=>{const o=await s.readFile("./package.json","utf-8"),n=JSON.parse(o);await t.test('includes "postcss-plugin" keyword',(()=>{e.ok(Array.isArray(n.keywords)&&n.keywords?.includes("postcss-plugin"),new PackageDescriptionError('Missing "postcss-plugin" keyword in package.json',"keywords"))})),await t.test('name starts with "postcss-"',{skip:c?.skipPackageNameCheck},(()=>{let s="string"==typeof n.name?n.name:"";if(s.startsWith("@")){const e=s.split("/");s=e.slice(1).join("/")}e.ok(s.startsWith("postcss-"),new PackageDescriptionError(`package name "${s}" does not start with "postcss-"`,"name"))})),await t.test("`postcss` is a peer dependency and not a direct dependency",{skip:"postcssTapeSelfTest"in a},(()=>{e.ok(Object.keys(Object(n.peerDependencies)).includes("postcss"),new PackageDescriptionError('"postcss" must be listed in "peerDependencies"',"peerDependencies")),e.ok(!Object.keys(Object(n.dependencies)).includes("postcss"),new PackageDescriptionError('"postcss" must not be listed in "dependencies"',"dependencies"))}))}));const l=a().postcssPlugin;await i(l,(async i=>{for(const c in p)await i.test(c,(async i=>{const l=p[c];l.before&&await l.before();const u=o.join(".","test",...c.split(":")[0].split(o.posix.sep)),d=o.join(".","test",...c.replace(/:/g,".").split(o.posix.sep)),m="css";let g=`${u}.${m}`,w=`${d}.expect.${m}`,f=`${d}.result.${m}`;l.source&&(g=o.join(".","test",l.source)),l.expect&&(w=o.join(".","test",l.expect)),l.result&&(f=o.join(".","test",l.result));const k=l.plugins??[a(l.options)],h=await fileContentsOrEmptyString(g),y=await fileContentsOrEmptyString(w);let E;try{E=await n(k).process(h,{from:g,to:f,map:{inline:!1,annotation:!1}})}catch(e){if(!(e instanceof Error))throw e;if(reduceInformationInCssSyntaxError(e),l.exception&&l.exception.test(e.message))return;throw e}e.ok(!l.exception,new OutcomeError(`expected an exception matching "${l.exception}"`,g));const x=E.css.toString();{const e=[s.writeFile(f,x,"utf8")];process.env.REWRITE_EXPECTS&&e.push(s.writeFile(w,x,"utf8")),await Promise.all(e)}y||e.ok(t.existsSync(w),new OutcomeError(`Missing expect file: "${w}"`,g)),await i.test("has expected output",(()=>{e.deepEqual(x,y),e.deepEqual(E.warnings().length,l.warnings??0,`Unexpected number warnings:\n${E.warnings().toString()}`)})),await i.test("sourcemaps",(()=>{e.ok(!E.map.toJSON().sources.includes(""),'Sourcemap is broken. This is most likely a newly created PostCSS AST Node without a value for "source". See: https://github.com/postcss/postcss/blob/main/docs/guidelines/plugin.md#24-set-nodesource-for-new-nodes')})),l.after&&await l.after(),await i.test("output is parsable with PostCSS",(async()=>{const s=await fileContentsOrEmptyString(f),t=await n([noopPlugin()]).process(s,{from:f,to:f,map:{inline:!1,annotation:!1}});e.deepEqual(t.warnings(),[],"Unexpected warnings on second pass")})),await i.test("The oldest and current PostCSS version produce the same result",{skip:n([noopPlugin()]).version===r([noopPlugin()]).version},(async()=>{const s=await r(k).process(h,{from:g,to:f,map:{inline:!1,annotation:!1}});e.deepEqual(s.css.toString(),x)}))}))}))}}noopPlugin.postcss=!0;const c={postcssPlugin:"declaration-cloner",Declaration(e){"to-clone"===e.prop&&e.cloneBefore({prop:"cloned"})}},p={postcssPlugin:"rule-cloner",prepare(){const e=new WeakSet;return{postcssPlugin:"rule-cloner",RuleExit(s){e.has(s)||"to-clone"===s.selector&&(e.add(s),s.cloneBefore({selector:"cloned"}))}}}},l={postcssPlugin:"at-rule-cloner",prepare(){const e=new WeakSet;return{postcssPlugin:"at-rule-cloner",AtRuleExit(s){if(!e.has(s))return"to-clone"===s.params?(e.add(s),void s.cloneBefore({params:"cloned"})):"to-clone"===s.name?(e.add(s),void s.cloneBefore({name:"cloned"})):void 0}}}};class PackageDescriptionError extends Error{constructor(e,s){super(e),this.name="PackageDescriptionError",this.stack=`${this.name}: ${this.message}\n at "${s}" (${a.pathToFileURL(o.resolve("package.json")).href}:1:1)`}}class OutcomeError extends Error{constructor(e,s){super(e),this.name="OutcomeError",this.stack=`${this.name}: ${this.message}\n at ${a.pathToFileURL(o.resolve(s)).href}:1:1`}}export{l as atRuleClonerPlugin,c as declarationClonerPlugin,postcssTape,p as ruleClonerPlugin}; -/* node:coverage enable */ diff --git a/plugins/postcss-color-mix-variadic-function-arguments/package.json b/plugins/postcss-color-mix-variadic-function-arguments/package.json index 2b7a694bdf..bf52d757d8 100644 --- a/plugins/postcss-color-mix-variadic-function-arguments/package.json +++ b/plugins/postcss-color-mix-variadic-function-arguments/package.json @@ -87,8 +87,8 @@ "rgba", "srgb-linear", "syntax", - "xyz", - "variadic" + "variadic", + "xyz" ], "csstools": { "cssdbId": "color-mix-variadic-function-arguments", diff --git a/plugins/postcss-color-mix-variadic-function-arguments/test/basic.css b/plugins/postcss-color-mix-variadic-function-arguments/test/basic.css index 169c94572a..3337540a48 100644 --- a/plugins/postcss-color-mix-variadic-function-arguments/test/basic.css +++ b/plugins/postcss-color-mix-variadic-function-arguments/test/basic.css @@ -10,6 +10,14 @@ color: color-mix(in srgb, cyan, magenta, yellow); } +.p3 { + color: color-mix(in oklch, cyan, magenta, yellow); +} + +.p3 { + color: color-mix(in oklch, red, lime, green); +} + .zero-percentage { 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 b97ba910a8..07539747b6 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 @@ -10,6 +10,16 @@ color: rgb(170, 170, 170); } +.p3 { + color: rgb(85, 230, 255); + color: color(display-p3 0.467 0.89663 1); +} + +.p3 { + color: rgb(164, 153, 0); + color: color(display-p3 0.64336 0.60238 0); +} + .zero-percentage { color: rgba(0, 0, 0, 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 2911e7a379..f9642f1195 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 @@ -13,6 +13,18 @@ color: color-mix(in srgb, cyan, magenta, yellow); } +.p3 { + color: rgb(85, 230, 255); + color: color(display-p3 0.467 0.89663 1); + color: color-mix(in oklch, cyan, magenta, yellow); +} + +.p3 { + color: rgb(164, 153, 0); + color: color(display-p3 0.64336 0.60238 0); + color: color-mix(in oklch, red, lime, green); +} + .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 2911e7a379..f9642f1195 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 @@ -13,6 +13,18 @@ color: color-mix(in srgb, cyan, magenta, yellow); } +.p3 { + color: rgb(85, 230, 255); + color: color(display-p3 0.467 0.89663 1); + color: color-mix(in oklch, cyan, magenta, yellow); +} + +.p3 { + color: rgb(164, 153, 0); + color: color(display-p3 0.64336 0.60238 0); + color: color-mix(in oklch, red, lime, green); +} + .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/variables.css b/plugins/postcss-color-mix-variadic-function-arguments/test/variables.css index 34baba5a5f..c1fb9cd782 100644 --- a/plugins/postcss-color-mix-variadic-function-arguments/test/variables.css +++ b/plugins/postcss-color-mix-variadic-function-arguments/test/variables.css @@ -1,6 +1,6 @@ :root { --firebrick: color-mix(in lch, red 90%); - --firebrick-a50: color-mix(in lch, red 90%); + --firebrick-a50: color-mix(in srgb, red 90%, lime, blue); --opacity-50: 50%; --firebrick-a50-var: color-mix(in lch, red var(--opacity-50)); diff --git a/plugins/postcss-color-mix-variadic-function-arguments/test/variables.expect.css b/plugins/postcss-color-mix-variadic-function-arguments/test/variables.expect.css index 365b0599dc..a37ddf59de 100644 --- a/plugins/postcss-color-mix-variadic-function-arguments/test/variables.expect.css +++ b/plugins/postcss-color-mix-variadic-function-arguments/test/variables.expect.css @@ -1,6 +1,6 @@ :root { --firebrick: rgba(255, 0, 0, 0.9); - --firebrick-a50: rgba(255, 0, 0, 0.9); + --firebrick-a50: rgb(230, 13, 13); --opacity-50: 50%; --firebrick-a50-var: color-mix(in lch, red var(--opacity-50)); diff --git a/plugins/postcss-color-mix-variadic-function-arguments/test/variables.preserve-true.expect.css b/plugins/postcss-color-mix-variadic-function-arguments/test/variables.preserve-true.expect.css index 52eb189fb7..3e34a66b6c 100644 --- a/plugins/postcss-color-mix-variadic-function-arguments/test/variables.preserve-true.expect.css +++ b/plugins/postcss-color-mix-variadic-function-arguments/test/variables.preserve-true.expect.css @@ -1,9 +1,14 @@ :root { --firebrick: rgba(255, 0, 0, 0.9); - --firebrick: color-mix(in lch, red 90%); - --firebrick-a50: rgba(255, 0, 0, 0.9); - --firebrick-a50: color-mix(in lch, red 90%); + --firebrick-a50: rgb(230, 13, 13); --opacity-50: 50%; --firebrick-a50-var: color-mix(in lch, red var(--opacity-50)); } + +@supports (color: color-mix(in lch, red)) { +:root { + --firebrick: color-mix(in lch, red 90%); + --firebrick-a50: color-mix(in srgb, red 90%, lime, blue); +} +} diff --git a/plugins/postcss-progressive-custom-properties/CHANGELOG.md b/plugins/postcss-progressive-custom-properties/CHANGELOG.md index 5eb7687294..08e4111aef 100644 --- a/plugins/postcss-progressive-custom-properties/CHANGELOG.md +++ b/plugins/postcss-progressive-custom-properties/CHANGELOG.md @@ -1,5 +1,9 @@ # Changes to PostCSS Progressive Custom Properties +### Unreleased (minor) + +- Add matching rules for variadic function arguments in `color-mix()` + ### 4.0.1 _April 19, 2025_ diff --git a/plugins/postcss-progressive-custom-properties/dist/index.cjs b/plugins/postcss-progressive-custom-properties/dist/index.cjs index 81d6202e1b..164686810d 100644 --- a/plugins/postcss-progressive-custom-properties/dist/index.cjs +++ b/plugins/postcss-progressive-custom-properties/dist/index.cjs @@ -1 +1 @@ -"use strict";var e=require("postcss-value-parser");const a=["at","bottom","center","circle","closest-corner","closest-side","ellipse","farthest-corner","farthest-side","from","in","left","right","to","top"];function doublePositionGradients(e){const r=[],p=e.value.toLowerCase();if("function"===e.type&&("conic-gradient"===p||"linear-gradient"===p||"radial-gradient"===p||"repeating-conic-gradient"===p||"repeating-linear-gradient"===p||"repeating-radial-gradient"===p)){let p=0,t=!1,i=!1;e:for(let o=0;oi.includes(e.sniff)&&(e.only_on_property??a.prop)===a.prop));let y=!1;try{e(i).walk((a=>{"function"===a.type&&p.test(a.value)&&(y=!0);try{const r=e.unit(a.value);!1!==r&&(a.dimension=r)}catch{}for(let e=0;e({postcssPlugin:"postcss-progressive-custom-properties",prepare(){const e=new WeakMap;return{postcssPlugin:"postcss-progressive-custom-properties",OnceExit(a,{postcss:r}){a.walkDecls((a=>{if(!a.parent)return;const p=e.get(a.parent)||{conditionalRules:[],propNames:new Set,lastConditionParams:{support:void 0},lastConditionalRule:void 0};if(e.set(a.parent,p),a.variable){if(!p.propNames.has(a.prop))return void p.propNames.add(a.prop)}else{const e=a.prop.toLowerCase();if(!p.propNames.has(e))return void p.propNames.add(e)}if(!a.variable&&!t.test(a.value))return;if(i.test(a.value))return;if(o.test(a.value))return;const s=conditionsFromValue(a,!a.variable).support.join(" and ");if(!s)return;if(p.lastConditionParams.support!==s&&(p.lastConditionalRule=void 0),p.lastConditionalRule)return p.lastConditionalRule.append(a.clone()),void a.remove();const y=[];if(s&&y.push(r.atRule({name:"supports",params:s,source:a.parent.source,raws:{before:"\n\n",after:"\n"}})),!y.length)return;for(let e=0;e{const r=e.get(a);r&&0!==r.conditionalRules.length&&r.conditionalRules.reverse().forEach((e=>{a.after(e)}))}))}}}});creator.postcss=!0,module.exports=creator; +"use strict";var e=require("postcss-value-parser");const a=["at","bottom","center","circle","closest-corner","closest-side","ellipse","farthest-corner","farthest-side","from","in","left","right","to","top"];function doublePositionGradients(e){const p=[],r=e.value.toLowerCase();if("function"===e.type&&("conic-gradient"===r||"linear-gradient"===r||"radial-gradient"===r||"repeating-conic-gradient"===r||"repeating-linear-gradient"===r||"repeating-radial-gradient"===r)){let r=0,t=!1,i=!1;e:for(let o=0;oi.includes(e.sniff)&&(e.only_on_property??a.prop)===a.prop));let y=!1;try{e(i).walk((a=>{"function"===a.type&&r.test(a.value)&&(y=!0);try{const p=e.unit(a.value);!1!==p&&(a.dimension=p)}catch{}for(let e=0;e({postcssPlugin:"postcss-progressive-custom-properties",prepare(){const e=new WeakMap;return{postcssPlugin:"postcss-progressive-custom-properties",OnceExit(a,{postcss:p}){a.walkDecls((a=>{if(!a.parent)return;const r=e.get(a.parent)||{conditionalRules:[],propNames:new Set,lastConditionParams:{support:void 0},lastConditionalRule:void 0};if(e.set(a.parent,r),a.variable){if(!r.propNames.has(a.prop))return void r.propNames.add(a.prop)}else{const e=a.prop.toLowerCase();if(!r.propNames.has(e))return void r.propNames.add(e)}if(!a.variable&&!t.test(a.value))return;if(i.test(a.value))return;if(o.test(a.value))return;const s=conditionsFromValue(a,!a.variable).support.join(" and ");if(!s)return;if(r.lastConditionParams.support!==s&&(r.lastConditionalRule=void 0),r.lastConditionalRule)return r.lastConditionalRule.append(a.clone()),void a.remove();const y=[];if(s&&y.push(p.atRule({name:"supports",params:s,source:a.parent.source,raws:{before:"\n\n",after:"\n"}})),!y.length)return;for(let e=0;e{const p=e.get(a);p&&0!==p.conditionalRules.length&&p.conditionalRules.reverse().forEach((e=>{a.after(e)}))}))}}}});creator.postcss=!0,module.exports=creator; diff --git a/plugins/postcss-progressive-custom-properties/dist/index.mjs b/plugins/postcss-progressive-custom-properties/dist/index.mjs index 2101bfdd9b..21722d55b2 100644 --- a/plugins/postcss-progressive-custom-properties/dist/index.mjs +++ b/plugins/postcss-progressive-custom-properties/dist/index.mjs @@ -1 +1 @@ -import e from"postcss-value-parser";const a=["at","bottom","center","circle","closest-corner","closest-side","ellipse","farthest-corner","farthest-side","from","in","left","right","to","top"];function doublePositionGradients(e){const r=[],p=e.value.toLowerCase();if("function"===e.type&&("conic-gradient"===p||"linear-gradient"===p||"radial-gradient"===p||"repeating-conic-gradient"===p||"repeating-linear-gradient"===p||"repeating-radial-gradient"===p)){let p=0,t=!1,i=!1;e:for(let o=0;oi.includes(e.sniff)&&(e.only_on_property??a.prop)===a.prop));let y=!1;try{e(i).walk((a=>{"function"===a.type&&p.test(a.value)&&(y=!0);try{const r=e.unit(a.value);!1!==r&&(a.dimension=r)}catch{}for(let e=0;e({postcssPlugin:"postcss-progressive-custom-properties",prepare(){const e=new WeakMap;return{postcssPlugin:"postcss-progressive-custom-properties",OnceExit(a,{postcss:r}){a.walkDecls((a=>{if(!a.parent)return;const p=e.get(a.parent)||{conditionalRules:[],propNames:new Set,lastConditionParams:{support:void 0},lastConditionalRule:void 0};if(e.set(a.parent,p),a.variable){if(!p.propNames.has(a.prop))return void p.propNames.add(a.prop)}else{const e=a.prop.toLowerCase();if(!p.propNames.has(e))return void p.propNames.add(e)}if(!a.variable&&!t.test(a.value))return;if(i.test(a.value))return;if(o.test(a.value))return;const s=conditionsFromValue(a,!a.variable).support.join(" and ");if(!s)return;if(p.lastConditionParams.support!==s&&(p.lastConditionalRule=void 0),p.lastConditionalRule)return p.lastConditionalRule.append(a.clone()),void a.remove();const y=[];if(s&&y.push(r.atRule({name:"supports",params:s,source:a.parent.source,raws:{before:"\n\n",after:"\n"}})),!y.length)return;for(let e=0;e{const r=e.get(a);r&&0!==r.conditionalRules.length&&r.conditionalRules.reverse().forEach((e=>{a.after(e)}))}))}}}});creator.postcss=!0;export{creator as default}; +import e from"postcss-value-parser";const a=["at","bottom","center","circle","closest-corner","closest-side","ellipse","farthest-corner","farthest-side","from","in","left","right","to","top"];function doublePositionGradients(e){const p=[],r=e.value.toLowerCase();if("function"===e.type&&("conic-gradient"===r||"linear-gradient"===r||"radial-gradient"===r||"repeating-conic-gradient"===r||"repeating-linear-gradient"===r||"repeating-radial-gradient"===r)){let r=0,t=!1,i=!1;e:for(let o=0;oi.includes(e.sniff)&&(e.only_on_property??a.prop)===a.prop));let y=!1;try{e(i).walk((a=>{"function"===a.type&&r.test(a.value)&&(y=!0);try{const p=e.unit(a.value);!1!==p&&(a.dimension=p)}catch{}for(let e=0;e({postcssPlugin:"postcss-progressive-custom-properties",prepare(){const e=new WeakMap;return{postcssPlugin:"postcss-progressive-custom-properties",OnceExit(a,{postcss:p}){a.walkDecls((a=>{if(!a.parent)return;const r=e.get(a.parent)||{conditionalRules:[],propNames:new Set,lastConditionParams:{support:void 0},lastConditionalRule:void 0};if(e.set(a.parent,r),a.variable){if(!r.propNames.has(a.prop))return void r.propNames.add(a.prop)}else{const e=a.prop.toLowerCase();if(!r.propNames.has(e))return void r.propNames.add(e)}if(!a.variable&&!t.test(a.value))return;if(i.test(a.value))return;if(o.test(a.value))return;const s=conditionsFromValue(a,!a.variable).support.join(" and ");if(!s)return;if(r.lastConditionParams.support!==s&&(r.lastConditionalRule=void 0),r.lastConditionalRule)return r.lastConditionalRule.append(a.clone()),void a.remove();const y=[];if(s&&y.push(p.atRule({name:"supports",params:s,source:a.parent.source,raws:{before:"\n\n",after:"\n"}})),!y.length)return;for(let e=0;e{const p=e.get(a);p&&0!==p.conditionalRules.length&&p.conditionalRules.reverse().forEach((e=>{a.after(e)}))}))}}}});creator.postcss=!0;export{creator as default}; diff --git a/plugins/postcss-progressive-custom-properties/scripts/color-mix.mjs b/plugins/postcss-progressive-custom-properties/scripts/color-mix.mjs index 7b8053127d..27db0393c9 100644 --- a/plugins/postcss-progressive-custom-properties/scripts/color-mix.mjs +++ b/plugins/postcss-progressive-custom-properties/scripts/color-mix.mjs @@ -11,10 +11,32 @@ export const colorMixMatchers = [ matcherForValue('color-mix(in $a,$1,$2 $3)'), matcherForValue('color-mix(in $a,$1 $2,$3 $4)'), - matcherForValue('color-mix(in $a $b,$1,$2)'), - matcherForValue('color-mix(in $a $b,$1 $2,$3)'), - matcherForValue('color-mix(in $a $b,$1,$2 $3)'), - matcherForValue('color-mix(in $a $b,$1 $2,$3 $4)'), + matcherForValue('color-mix(in $a $b hue,$1,$2)'), + matcherForValue('color-mix(in $a $b hue,$1 $2,$3)'), + matcherForValue('color-mix(in $a $b hue,$1,$2 $3)'), + matcherForValue('color-mix(in $a $b hue,$1 $2,$3 $4)'), + ], + }, + { + 'supports': 'color-mix(in lch, red)', + 'property': 'color', + 'sniff': 'color-mix', + 'matchers': [ + matcherForValue('color-mix(in $a,$1)'), + matcherForValue('color-mix(in $a,$1 $2)'), + + matcherForValue('color-mix(in $a $b hue,$1)'), + matcherForValue('color-mix(in $a $b hue,$1 $2)'), + + matcherForValue('color-mix(in $a,$1,$2, _z)'), + matcherForValue('color-mix(in $a,$1 $2,$3, _z)'), + matcherForValue('color-mix(in $a,$1,$2 $3, _z)'), + matcherForValue('color-mix(in $a,$1 $2,$3 $4, _z)'), + + matcherForValue('color-mix(in $a $b hue,$1,$2, _z)'), + matcherForValue('color-mix(in $a $b hue,$1 $2,$3, _z)'), + matcherForValue('color-mix(in $a $b hue,$1,$2 $3, _z)'), + matcherForValue('color-mix(in $a $b hue,$1 $2,$3 $4, _z)'), ], }, ]; diff --git a/plugins/postcss-progressive-custom-properties/scripts/matcher-for-value.mjs b/plugins/postcss-progressive-custom-properties/scripts/matcher-for-value.mjs index 84d57acbf8..20ff7cfa10 100644 --- a/plugins/postcss-progressive-custom-properties/scripts/matcher-for-value.mjs +++ b/plugins/postcss-progressive-custom-properties/scripts/matcher-for-value.mjs @@ -15,6 +15,9 @@ export function matcherForValue(value) { } else if (node.value.startsWith('$')) { delete node.value; node.isVariable = true; + } else if (node.value.startsWith('_')) { + delete node.value; + node.anyRemainingArguments = true; } else { try { node.dimension = valueParser.unit(node.value); diff --git a/plugins/postcss-progressive-custom-properties/src/match.ts b/plugins/postcss-progressive-custom-properties/src/match.ts index 771fc65d3c..9f7be7a36a 100644 --- a/plugins/postcss-progressive-custom-properties/src/match.ts +++ b/plugins/postcss-progressive-custom-properties/src/match.ts @@ -5,7 +5,8 @@ type MatcherNode = { dimension?: { unit?: string }, - isVariable?: boolean + isVariable?: boolean, + anyRemainingArguments?: boolean } export function matches(a: MatcherNode, b: MatcherNode): boolean { @@ -47,6 +48,10 @@ export function matches(a: MatcherNode, b: MatcherNode): boolean { return false; } + if (a.nodes[ia].anyRemainingArguments && b.nodes[ib]) { + return true; + } + if (matches(a.nodes[ia], b.nodes[ib])) { continue; } diff --git a/plugins/postcss-progressive-custom-properties/src/matchers.ts b/plugins/postcss-progressive-custom-properties/src/matchers.ts index 98acda8a53..668cb3f71e 100644 --- a/plugins/postcss-progressive-custom-properties/src/matchers.ts +++ b/plugins/postcss-progressive-custom-properties/src/matchers.ts @@ -186,6 +186,570 @@ export const matchers = [ "type": "word", "isVariable": true }, + { + "type": "space" + }, + { + "type": "word", + "value": "hue" + }, + { + "type": "div", + "value": "," + }, + { + "type": "word", + "isVariable": true + }, + { + "type": "div", + "value": "," + }, + { + "type": "word", + "isVariable": true + } + ] + }, + { + "type": "function", + "value": "color-mix", + "nodes": [ + { + "type": "word", + "value": "in" + }, + { + "type": "space" + }, + { + "type": "word", + "isVariable": true + }, + { + "type": "space" + }, + { + "type": "word", + "isVariable": true + }, + { + "type": "space" + }, + { + "type": "word", + "value": "hue" + }, + { + "type": "div", + "value": "," + }, + { + "type": "word", + "isVariable": true + }, + { + "type": "space" + }, + { + "type": "word", + "isVariable": true + }, + { + "type": "div", + "value": "," + }, + { + "type": "word", + "isVariable": true + } + ] + }, + { + "type": "function", + "value": "color-mix", + "nodes": [ + { + "type": "word", + "value": "in" + }, + { + "type": "space" + }, + { + "type": "word", + "isVariable": true + }, + { + "type": "space" + }, + { + "type": "word", + "isVariable": true + }, + { + "type": "space" + }, + { + "type": "word", + "value": "hue" + }, + { + "type": "div", + "value": "," + }, + { + "type": "word", + "isVariable": true + }, + { + "type": "div", + "value": "," + }, + { + "type": "word", + "isVariable": true + }, + { + "type": "space" + }, + { + "type": "word", + "isVariable": true + } + ] + }, + { + "type": "function", + "value": "color-mix", + "nodes": [ + { + "type": "word", + "value": "in" + }, + { + "type": "space" + }, + { + "type": "word", + "isVariable": true + }, + { + "type": "space" + }, + { + "type": "word", + "isVariable": true + }, + { + "type": "space" + }, + { + "type": "word", + "value": "hue" + }, + { + "type": "div", + "value": "," + }, + { + "type": "word", + "isVariable": true + }, + { + "type": "space" + }, + { + "type": "word", + "isVariable": true + }, + { + "type": "div", + "value": "," + }, + { + "type": "word", + "isVariable": true + }, + { + "type": "space" + }, + { + "type": "word", + "isVariable": true + } + ] + } + ] + }, + { + "supports": "color-mix(in lch, red)", + "property": "color", + "sniff": "color-mix", + "matchers": [ + { + "type": "function", + "value": "color-mix", + "nodes": [ + { + "type": "word", + "value": "in" + }, + { + "type": "space" + }, + { + "type": "word", + "isVariable": true + }, + { + "type": "div", + "value": "," + }, + { + "type": "word", + "isVariable": true + } + ] + }, + { + "type": "function", + "value": "color-mix", + "nodes": [ + { + "type": "word", + "value": "in" + }, + { + "type": "space" + }, + { + "type": "word", + "isVariable": true + }, + { + "type": "div", + "value": "," + }, + { + "type": "word", + "isVariable": true + }, + { + "type": "space" + }, + { + "type": "word", + "isVariable": true + } + ] + }, + { + "type": "function", + "value": "color-mix", + "nodes": [ + { + "type": "word", + "value": "in" + }, + { + "type": "space" + }, + { + "type": "word", + "isVariable": true + }, + { + "type": "space" + }, + { + "type": "word", + "isVariable": true + }, + { + "type": "space" + }, + { + "type": "word", + "value": "hue" + }, + { + "type": "div", + "value": "," + }, + { + "type": "word", + "isVariable": true + } + ] + }, + { + "type": "function", + "value": "color-mix", + "nodes": [ + { + "type": "word", + "value": "in" + }, + { + "type": "space" + }, + { + "type": "word", + "isVariable": true + }, + { + "type": "space" + }, + { + "type": "word", + "isVariable": true + }, + { + "type": "space" + }, + { + "type": "word", + "value": "hue" + }, + { + "type": "div", + "value": "," + }, + { + "type": "word", + "isVariable": true + }, + { + "type": "space" + }, + { + "type": "word", + "isVariable": true + } + ] + }, + { + "type": "function", + "value": "color-mix", + "nodes": [ + { + "type": "word", + "value": "in" + }, + { + "type": "space" + }, + { + "type": "word", + "isVariable": true + }, + { + "type": "div", + "value": "," + }, + { + "type": "word", + "isVariable": true + }, + { + "type": "div", + "value": "," + }, + { + "type": "word", + "isVariable": true + }, + { + "type": "div", + "value": "," + }, + { + "type": "word", + "anyRemainingArguments": true + } + ] + }, + { + "type": "function", + "value": "color-mix", + "nodes": [ + { + "type": "word", + "value": "in" + }, + { + "type": "space" + }, + { + "type": "word", + "isVariable": true + }, + { + "type": "div", + "value": "," + }, + { + "type": "word", + "isVariable": true + }, + { + "type": "space" + }, + { + "type": "word", + "isVariable": true + }, + { + "type": "div", + "value": "," + }, + { + "type": "word", + "isVariable": true + }, + { + "type": "div", + "value": "," + }, + { + "type": "word", + "anyRemainingArguments": true + } + ] + }, + { + "type": "function", + "value": "color-mix", + "nodes": [ + { + "type": "word", + "value": "in" + }, + { + "type": "space" + }, + { + "type": "word", + "isVariable": true + }, + { + "type": "div", + "value": "," + }, + { + "type": "word", + "isVariable": true + }, + { + "type": "div", + "value": "," + }, + { + "type": "word", + "isVariable": true + }, + { + "type": "space" + }, + { + "type": "word", + "isVariable": true + }, + { + "type": "div", + "value": "," + }, + { + "type": "word", + "anyRemainingArguments": true + } + ] + }, + { + "type": "function", + "value": "color-mix", + "nodes": [ + { + "type": "word", + "value": "in" + }, + { + "type": "space" + }, + { + "type": "word", + "isVariable": true + }, + { + "type": "div", + "value": "," + }, + { + "type": "word", + "isVariable": true + }, + { + "type": "space" + }, + { + "type": "word", + "isVariable": true + }, + { + "type": "div", + "value": "," + }, + { + "type": "word", + "isVariable": true + }, + { + "type": "space" + }, + { + "type": "word", + "isVariable": true + }, + { + "type": "div", + "value": "," + }, + { + "type": "word", + "anyRemainingArguments": true + } + ] + }, + { + "type": "function", + "value": "color-mix", + "nodes": [ + { + "type": "word", + "value": "in" + }, + { + "type": "space" + }, + { + "type": "word", + "isVariable": true + }, + { + "type": "space" + }, + { + "type": "word", + "isVariable": true + }, + { + "type": "space" + }, + { + "type": "word", + "value": "hue" + }, { "type": "div", "value": "," @@ -201,6 +765,14 @@ export const matchers = [ { "type": "word", "isVariable": true + }, + { + "type": "div", + "value": "," + }, + { + "type": "word", + "anyRemainingArguments": true } ] }, @@ -226,6 +798,13 @@ export const matchers = [ "type": "word", "isVariable": true }, + { + "type": "space" + }, + { + "type": "word", + "value": "hue" + }, { "type": "div", "value": "," @@ -248,6 +827,14 @@ export const matchers = [ { "type": "word", "isVariable": true + }, + { + "type": "div", + "value": "," + }, + { + "type": "word", + "anyRemainingArguments": true } ] }, @@ -273,6 +860,13 @@ export const matchers = [ "type": "word", "isVariable": true }, + { + "type": "space" + }, + { + "type": "word", + "value": "hue" + }, { "type": "div", "value": "," @@ -295,6 +889,14 @@ export const matchers = [ { "type": "word", "isVariable": true + }, + { + "type": "div", + "value": "," + }, + { + "type": "word", + "anyRemainingArguments": true } ] }, @@ -320,6 +922,13 @@ export const matchers = [ "type": "word", "isVariable": true }, + { + "type": "space" + }, + { + "type": "word", + "value": "hue" + }, { "type": "div", "value": "," @@ -349,6 +958,14 @@ export const matchers = [ { "type": "word", "isVariable": true + }, + { + "type": "div", + "value": "," + }, + { + "type": "word", + "anyRemainingArguments": true } ] } diff --git a/plugins/postcss-progressive-custom-properties/test/basic.css b/plugins/postcss-progressive-custom-properties/test/basic.css index 87da9f918b..10cd70d0b7 100644 --- a/plugins/postcss-progressive-custom-properties/test/basic.css +++ b/plugins/postcss-progressive-custom-properties/test/basic.css @@ -117,7 +117,7 @@ --color-mix-1: plum; --color-mix-1: color-mix(in lch, purple 50%, plum 50%); --color-mix-2: plum; - --color-mix-2: color-mix(in lch longer, purple 50%, plum 50%); + --color-mix-2: color-mix(in lch longer hue, purple 50%, plum 50%); --color-mix-3: plum; --color-mix-3: color-mix(in lab, purple 50%, plum 50%); --color-mix-4: plum; @@ -149,8 +149,12 @@ --color-mix-1: color(display-p3 0.64331 0.19245 0.16771); --color-mix-1: color-mix(in lch, purple 50%, plum 50%); --color-mix-2: plum; - --color-mix-2: color-mix(in lch longer, purple 50%, plum 50%); + --color-mix-2: color-mix(in lch longer hue, purple 50%, plum 50%); --color-mix-2: color(display-p3 0.64331 0.19245 0.16771); + --color-mix-3: rgb(85, 85, 85); + --color-mix-3: color-mix(in srgb, red, lime, blue); + --color-mix-4: red; + --color-mix-4: color-mix(in srgb, red); } :root { diff --git a/plugins/postcss-progressive-custom-properties/test/basic.expect.css b/plugins/postcss-progressive-custom-properties/test/basic.expect.css index fe5f9e00c8..5fdd945e61 100644 --- a/plugins/postcss-progressive-custom-properties/test/basic.expect.css +++ b/plugins/postcss-progressive-custom-properties/test/basic.expect.css @@ -231,7 +231,7 @@ @supports (color: color-mix(in lch, red, blue)) { :root { --color-mix-1: color-mix(in lch, purple 50%, plum 50%); - --color-mix-2: color-mix(in lch longer, purple 50%, plum 50%); + --color-mix-2: color-mix(in lch longer hue, purple 50%, plum 50%); --color-mix-3: color-mix(in lab, purple 50%, plum 50%); --color-mix-4: color-mix(in srgb, purple, plum 50%); --color-mix-5: color-mix(in srgb-linear, purple 50%, plum); @@ -257,6 +257,8 @@ /* color-mix() */ --color-mix-1: plum; --color-mix-2: plum; + --color-mix-3: rgb(85, 85, 85); + --color-mix-4: red; } @supports (color: color(display-p3 0 0 0%)) { @@ -268,7 +270,7 @@ @supports (color: color-mix(in lch, red, blue)) { :root { --color-mix-1: color-mix(in lch, purple 50%, plum 50%); - --color-mix-2: color-mix(in lch longer, purple 50%, plum 50%); + --color-mix-2: color-mix(in lch longer hue, purple 50%, plum 50%); } } @@ -278,6 +280,13 @@ } } +@supports (color: color-mix(in lch, red)) { +:root { + --color-mix-3: color-mix(in srgb, red, lime, blue); + --color-mix-4: color-mix(in srgb, red); +} +} + :root { --color-rcs-1: green; --color-rcs-2: green; From 1f6f5c218be6addc0e24a3616632bb998d059770 Mon Sep 17 00:00:00 2001 From: Romain Menke Date: Mon, 26 May 2025 22:31:06 +0200 Subject: [PATCH 3/5] rebuild --- .../docs/css-color-parser.syntaxflag.md | 16 +++ packages/postcss-tape/dist/index.d.ts | 111 ++++++++++++++++++ packages/postcss-tape/dist/index.mjs | 3 + 3 files changed, 130 insertions(+) create mode 100644 packages/postcss-tape/dist/index.d.ts create mode 100644 packages/postcss-tape/dist/index.mjs diff --git a/packages/css-color-parser/docs/css-color-parser.syntaxflag.md b/packages/css-color-parser/docs/css-color-parser.syntaxflag.md index cf92b77b97..47abb3d701 100644 --- a/packages/css-color-parser/docs/css-color-parser.syntaxflag.md +++ b/packages/css-color-parser/docs/css-color-parser.syntaxflag.md @@ -59,6 +59,22 @@ ColorMix Is a mixed color, e.g. `color-mix(in oklch, red, blue)` + + + +ColorMixVariadic + + + + +`"color-mix-variadic"` + + + + +Is a variadic mixed color, e.g. `color-mix(in oklch, red)` `color-mix(in oklch, red, blue, green)` + + diff --git a/packages/postcss-tape/dist/index.d.ts b/packages/postcss-tape/dist/index.d.ts new file mode 100644 index 0000000000..5a0c1b4542 --- /dev/null +++ b/packages/postcss-tape/dist/index.d.ts @@ -0,0 +1,111 @@ +/** + * A test suite for PostCSS plugins. + * + * @example + * ```sh + * node --test + * ``` + * + * ```js + * // test/_tape.mjs + * import { postcssTape } from '@csstools/postcss-tape'; + * import plugin from ''; + * + * postcssTape(plugin)({ + * basic: { + * message: "supports basic usage", + * }, + * 'basic:color': { + * message: "supports { color: '' }", + * options: { + * color: 'purple' + * } + * }, + * }); + * ``` + * + * @packageDocumentation + */ + +import type { Declaration } from 'postcss'; +import type { Plugin } from 'postcss'; +import type { PluginCreator } from 'postcss'; + +/** + * A dummy PostCSS plugin that clones any at rule with params `to-clone` to a new at rule with params `cloned`. + */ +export declare const atRuleClonerPlugin: { + postcssPlugin: string; + prepare(): Plugin; +}; + +/** + * A dummy PostCSS plugin that clones any declaration with the property `to-clone` to a new declaration with the property `cloned`. + */ +export declare const declarationClonerPlugin: { + postcssPlugin: string; + Declaration(decl: Declaration): void; +}; + +/** + * General options for `@csstools/postcss-tape`. + * These affect the entire test run, not individual test cases. + * + * @example + * ```js + * import { postcssTape } from '@csstools/postcss-tape'; + * import plugin from 'your-postcss-plugin'; + * + * postcssTape(plugin, { + * skipPackageNameCheck: true, + * })(...); + * ``` + */ +export declare type Options = { + /** + * PostCSS plugins should start their name with `postcss-`. + * If this is something you do not want to do, you can set this to `true` to skip this check. + */ + skipPackageNameCheck?: boolean; +}; + +/** + * Create a test suite for a PostCSS plugin. + */ +export declare function postcssTape(pluginCreator: PluginCreator, runOptions?: Options): (options: Record) => Promise; + +/** + * A dummy PostCSS plugin that clones any rule with the selector `to-clone` to a new rule with the selector `cloned`. + */ +export declare const ruleClonerPlugin: { + postcssPlugin: string; + prepare(): Plugin; +}; + +/** + * Options for a test case. + */ +export declare interface TestCaseOptions { + /** Debug message */ + message?: string; + /** Plugin options. Only used if `plugins` is not specified. */ + options?: unknown; + /** Plugins to use. When specified the original plugin is not used. */ + plugins?: Array; + /** The expected number of warnings. */ + warnings?: number; + /** Expected exception */ + exception?: RegExp; + /** Override the file name of the "source" file. */ + source?: string; + /** Override the file name of the "expect" file. */ + expect?: string; + /** Override the file name of the "result" file. */ + result?: string; + /** Do something before the test is run. */ + before?: () => void | Promise; + /** Do something after the test is run. */ + after?: () => void | Promise; +} + +export { } diff --git a/packages/postcss-tape/dist/index.mjs b/packages/postcss-tape/dist/index.mjs new file mode 100644 index 0000000000..b046d0df5f --- /dev/null +++ b/packages/postcss-tape/dist/index.mjs @@ -0,0 +1,3 @@ +/* node:coverage disable */ +import e from"node:assert/strict";import s from"node:fs/promises";import t from"node:fs";import o from"node:path";import n from"postcss";import r from"postcss-8.4";import i from"node:test";import a from"node:url";const noopPlugin=()=>({postcssPlugin:"noop-plugin",Once(){}});async function fileContentsOrEmptyString(e){try{return await s.readFile(e,"utf8")}catch{return""}}function reduceInformationInCssSyntaxError(e){process.env.DEBUG||(delete e.source,e.input&&delete e.input.source,delete e.postcssNode)}function postcssTape(a,c){return async p=>{c=c??{},await i("`postcss` flag is set on exported plugin creator",(()=>{e.equal(a.postcss,!0)})),await i("exported plugin creator is a function",(()=>{e.equal(typeof a,"function")})),await i("`postcssPlugin` is set on a plugin instance",(()=>{const s=a();e.ok(s.postcssPlugin),e.equal(typeof s.postcssPlugin,"string")})),await i("package.json",(async t=>{const o=await s.readFile("./package.json","utf-8"),n=JSON.parse(o);await t.test('includes "postcss-plugin" keyword',(()=>{e.ok(Array.isArray(n.keywords)&&n.keywords?.includes("postcss-plugin"),new PackageDescriptionError('Missing "postcss-plugin" keyword in package.json',"keywords"))})),await t.test('name starts with "postcss-"',{skip:c?.skipPackageNameCheck},(()=>{let s="string"==typeof n.name?n.name:"";if(s.startsWith("@")){const e=s.split("/");s=e.slice(1).join("/")}e.ok(s.startsWith("postcss-"),new PackageDescriptionError(`package name "${s}" does not start with "postcss-"`,"name"))})),await t.test("`postcss` is a peer dependency and not a direct dependency",{skip:"postcssTapeSelfTest"in a},(()=>{e.ok(Object.keys(Object(n.peerDependencies)).includes("postcss"),new PackageDescriptionError('"postcss" must be listed in "peerDependencies"',"peerDependencies")),e.ok(!Object.keys(Object(n.dependencies)).includes("postcss"),new PackageDescriptionError('"postcss" must not be listed in "dependencies"',"dependencies"))}))}));const l=a().postcssPlugin;await i(l,(async i=>{for(const c in p)await i.test(c,(async i=>{const l=p[c];l.before&&await l.before();const u=o.join(".","test",...c.split(":")[0].split(o.posix.sep)),d=o.join(".","test",...c.replace(/:/g,".").split(o.posix.sep)),m="css";let g=`${u}.${m}`,w=`${d}.expect.${m}`,f=`${d}.result.${m}`;l.source&&(g=o.join(".","test",l.source)),l.expect&&(w=o.join(".","test",l.expect)),l.result&&(f=o.join(".","test",l.result));const k=l.plugins??[a(l.options)],h=await fileContentsOrEmptyString(g),y=await fileContentsOrEmptyString(w);let E;try{E=await n(k).process(h,{from:g,to:f,map:{inline:!1,annotation:!1}})}catch(e){if(!(e instanceof Error))throw e;if(reduceInformationInCssSyntaxError(e),l.exception&&l.exception.test(e.message))return;throw e}e.ok(!l.exception,new OutcomeError(`expected an exception matching "${l.exception}"`,g));const x=E.css.toString();{const e=[s.writeFile(f,x,"utf8")];process.env.REWRITE_EXPECTS&&e.push(s.writeFile(w,x,"utf8")),await Promise.all(e)}y||e.ok(t.existsSync(w),new OutcomeError(`Missing expect file: "${w}"`,g)),await i.test("has expected output",(()=>{e.deepEqual(x,y),e.deepEqual(E.warnings().length,l.warnings??0,`Unexpected number warnings:\n${E.warnings().toString()}`)})),await i.test("sourcemaps",(()=>{e.ok(!E.map.toJSON().sources.includes(""),'Sourcemap is broken. This is most likely a newly created PostCSS AST Node without a value for "source". See: https://github.com/postcss/postcss/blob/main/docs/guidelines/plugin.md#24-set-nodesource-for-new-nodes')})),l.after&&await l.after(),await i.test("output is parsable with PostCSS",(async()=>{const s=await fileContentsOrEmptyString(f),t=await n([noopPlugin()]).process(s,{from:f,to:f,map:{inline:!1,annotation:!1}});e.deepEqual(t.warnings(),[],"Unexpected warnings on second pass")})),await i.test("The oldest and current PostCSS version produce the same result",{skip:n([noopPlugin()]).version===r([noopPlugin()]).version},(async()=>{const s=await r(k).process(h,{from:g,to:f,map:{inline:!1,annotation:!1}});e.deepEqual(s.css.toString(),x)}))}))}))}}noopPlugin.postcss=!0;const c={postcssPlugin:"declaration-cloner",Declaration(e){"to-clone"===e.prop&&e.cloneBefore({prop:"cloned"})}},p={postcssPlugin:"rule-cloner",prepare(){const e=new WeakSet;return{postcssPlugin:"rule-cloner",RuleExit(s){e.has(s)||"to-clone"===s.selector&&(e.add(s),s.cloneBefore({selector:"cloned"}))}}}},l={postcssPlugin:"at-rule-cloner",prepare(){const e=new WeakSet;return{postcssPlugin:"at-rule-cloner",AtRuleExit(s){if(!e.has(s))return"to-clone"===s.params?(e.add(s),void s.cloneBefore({params:"cloned"})):"to-clone"===s.name?(e.add(s),void s.cloneBefore({name:"cloned"})):void 0}}}};class PackageDescriptionError extends Error{constructor(e,s){super(e),this.name="PackageDescriptionError",this.stack=`${this.name}: ${this.message}\n at "${s}" (${a.pathToFileURL(o.resolve("package.json")).href}:1:1)`}}class OutcomeError extends Error{constructor(e,s){super(e),this.name="OutcomeError",this.stack=`${this.name}: ${this.message}\n at ${a.pathToFileURL(o.resolve(s)).href}:1:1`}}export{l as atRuleClonerPlugin,c as declarationClonerPlugin,postcssTape,p as ruleClonerPlugin}; +/* node:coverage enable */ From 8936e02978b0c5196cbcab840cad290d797a4034 Mon Sep 17 00:00:00 2001 From: Romain Menke Date: Tue, 27 May 2025 11:51:54 +0200 Subject: [PATCH 4/5] final tweaks --- .github/ISSUE_TEMPLATE/css-issue.yml | 1 + .github/ISSUE_TEMPLATE/plugin-issue.yml | 1 + .github/labeler.yml | 6 ++++++ .../package.json | 11 ++--------- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/css-issue.yml b/.github/ISSUE_TEMPLATE/css-issue.yml index ef53e06f51..8584f7c2d8 100644 --- a/.github/ISSUE_TEMPLATE/css-issue.yml +++ b/.github/ISSUE_TEMPLATE/css-issue.yml @@ -75,6 +75,7 @@ body: - PostCSS Color Functional Notation - PostCSS Color Hex Alpha - PostCSS Color Mix Function + - PostCSS Color Mix Variadic Function Arguments - PostCSS Conditional Values - PostCSS Content Alt Text - PostCSS Contrast Color Functions diff --git a/.github/ISSUE_TEMPLATE/plugin-issue.yml b/.github/ISSUE_TEMPLATE/plugin-issue.yml index f60956ae6e..94429915e8 100644 --- a/.github/ISSUE_TEMPLATE/plugin-issue.yml +++ b/.github/ISSUE_TEMPLATE/plugin-issue.yml @@ -72,6 +72,7 @@ body: - PostCSS Color Functional Notation - PostCSS Color Hex Alpha - PostCSS Color Mix Function + - PostCSS Color Mix Variadic Function Arguments - PostCSS Conditional Values - PostCSS Content Alt Text - PostCSS Contrast Color Functions diff --git a/.github/labeler.yml b/.github/labeler.yml index 80f5e2986e..aee34bd68b 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -113,6 +113,12 @@ - plugins/postcss-color-rebeccapurple/** - experimental/postcss-color-rebeccapurple/** +"plugins/postcss-color-mix-variadic-function-arguments": + - changed-files: + - any-glob-to-any-file: + - plugins/postcss-color-mix-variadic-function-arguments/** + - experimental/postcss-color-mix-variadic-function-arguments/** + "plugins/postcss-color-mix-function": - changed-files: - any-glob-to-any-file: diff --git a/plugins/postcss-color-mix-variadic-function-arguments/package.json b/plugins/postcss-color-mix-variadic-function-arguments/package.json index bf52d757d8..e63973bd77 100644 --- a/plugins/postcss-color-mix-variadic-function-arguments/package.json +++ b/plugins/postcss-color-mix-variadic-function-arguments/package.json @@ -1,6 +1,6 @@ { "name": "@csstools/postcss-color-mix-variadic-function-arguments", - "description": "Use the color-mix function in CSS", + "description": "Mix any number of colors with the color-mix function in CSS", "version": "0.0.0", "contributors": [ { @@ -78,17 +78,10 @@ "keywords": [ "color-mix", "css", - "display-p3", "interpolation", "postcss-plugin", - "prophoto-rgb", - "rec2020", - "rgb", - "rgba", - "srgb-linear", "syntax", - "variadic", - "xyz" + "variadic" ], "csstools": { "cssdbId": "color-mix-variadic-function-arguments", From 0d3c32f465707eb9ef3926ab5e13de88fa3b8c76 Mon Sep 17 00:00:00 2001 From: Romain Menke Date: Tue, 27 May 2025 11:53:02 +0200 Subject: [PATCH 5/5] abbreviate the label --- .github/labeler.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/labeler.yml b/.github/labeler.yml index aee34bd68b..fd49e18a19 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -113,7 +113,7 @@ - plugins/postcss-color-rebeccapurple/** - experimental/postcss-color-rebeccapurple/** -"plugins/postcss-color-mix-variadic-function-arguments": +"plugins/postcss-color-mix-variadic-function-args": - changed-files: - any-glob-to-any-file: - plugins/postcss-color-mix-variadic-function-arguments/**