@csstools/postcss-gradients-interpolation-method 2.0.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changes to PostCSS Gradients Interpolation Method
2
2
 
3
+ ### 3.0.0 3.0.0 (March 25, 2023)
4
+
5
+ - Handle `color-mix()` internally with `@csstools/css-color-parser`
6
+
7
+ ### 2.0.1 (January 28, 2023)
8
+
9
+ - Improve `types` declaration in `package.json`
10
+
3
11
  ### 2.0.0 (January 24, 2023)
4
12
 
5
13
  - Updated: Support for Node v14+ (major).
package/README.md CHANGED
@@ -1,53 +1,57 @@
1
1
  # PostCSS Gradients Interpolation Method [<img src="https://postcss.github.io/postcss/logo.svg" alt="PostCSS Logo" width="90" height="90" align="right">][PostCSS]
2
2
 
3
- [<img alt="npm version" src="https://img.shields.io/npm/v/@csstools/postcss-gradients-interpolation-method.svg" height="20">][npm-url] [<img alt="CSS Standard Status" src="https://cssdb.org/images/badges/TODO.svg" height="20">][css-url] [<img alt="Build Status" src="https://github.com/csstools/postcss-plugins/workflows/test/badge.svg" height="20">][cli-url] [<img alt="Discord" src="https://shields.io/badge/Discord-5865F2?logo=discord&logoColor=white">][discord]
3
+ [<img alt="npm version" src="https://img.shields.io/npm/v/@csstools/postcss-gradients-interpolation-method.svg" height="20">][npm-url] [<img alt="CSS Standard Status" src="https://cssdb.org/images/badges/gradients-interpolation-method.svg" height="20">][css-url] [<img alt="Build Status" src="https://github.com/csstools/postcss-plugins/workflows/test/badge.svg" height="20">][cli-url] [<img alt="Discord" src="https://shields.io/badge/Discord-5865F2?logo=discord&logoColor=white">][discord]
4
4
 
5
5
  [PostCSS Gradients Interpolation Method] lets you use different interpolation methods in CSS gradient functions following [CSS Specification].
6
6
 
7
7
  ```pcss
8
- .oklch {
9
- background: linear-gradient(in oklch 90deg, black 25% , blue 75%);
8
+ .example {
9
+ background-image: linear-gradient(in oklch, hsl(0deg 85% 75%) 0%, hsl(180deg 80% 65%) 100%);
10
10
  }
11
11
 
12
- :root {
13
- --background: linear-gradient(in oklch 90deg, black 25%, blue 75%);
12
+ .example {
13
+ background-image: linear-gradient(in oklab, hsl(96, 42%, 24%) 0%, hsl(302, 67%, 25%) 100%);
14
14
  }
15
15
 
16
16
  /* becomes */
17
17
 
18
- .oklch {
19
- background: linear-gradient(90deg ,black 25%,color-mix(in oklch, black 90%, blue 10%) calc(25% + ((75% - 25%) * 0.1)),color-mix(in oklch, black 80%, blue 20%) calc(25% + ((75% - 25%) * 0.2)),color-mix(in oklch, black 70%, blue 30%) calc(25% + ((75% - 25%) * 0.3)),color-mix(in oklch, black 60%, blue 40%) calc(25% + ((75% - 25%) * 0.4)),color-mix(in oklch, black 50%, blue 50%) calc(25% + ((75% - 25%) * 0.5)),color-mix(in oklch, black 40%, blue 60%) calc(25% + ((75% - 25%) * 0.6)),color-mix(in oklch, black 30%, blue 70%) calc(25% + ((75% - 25%) * 0.7)),color-mix(in oklch, black 20%, blue 80%) calc(25% + ((75% - 25%) * 0.8)),color-mix(in oklch, black 10%, blue 90%) calc(25% + ((75% - 25%) * 0.9)),blue 75%);
20
- background: linear-gradient(in oklch 90deg, black 25% , blue 75%);
18
+ .example {
19
+ background-image: linear-gradient(rgb(245, 137, 137) 0%, rgb(248, 146, 114), rgb(244, 158, 94), rgb(235, 171, 82), rgb(220, 185, 81), rgb(201, 199, 95), rgb(177, 211, 118), rgb(151, 221, 146), rgb(125, 229, 177), rgb(103, 235, 208), rgb(94, 237, 237) 100%);
20
+ background-image: linear-gradient(in oklch, hsl(0deg 85% 75%) 0%, hsl(180deg 80% 65%) 100%);
21
21
  }
22
22
 
23
- :root {
24
- --background: linear-gradient(90deg ,black 25%,color-mix(in oklch, black 90%, blue 10%) calc(25% + ((75% - 25%) * 0.1)),color-mix(in oklch, black 80%, blue 20%) calc(25% + ((75% - 25%) * 0.2)),color-mix(in oklch, black 70%, blue 30%) calc(25% + ((75% - 25%) * 0.3)),color-mix(in oklch, black 60%, blue 40%) calc(25% + ((75% - 25%) * 0.4)),color-mix(in oklch, black 50%, blue 50%) calc(25% + ((75% - 25%) * 0.5)),color-mix(in oklch, black 40%, blue 60%) calc(25% + ((75% - 25%) * 0.6)),color-mix(in oklch, black 30%, blue 70%) calc(25% + ((75% - 25%) * 0.7)),color-mix(in oklch, black 20%, blue 80%) calc(25% + ((75% - 25%) * 0.8)),color-mix(in oklch, black 10%, blue 90%) calc(25% + ((75% - 25%) * 0.9)),blue 75%);
25
- }
26
-
27
- @supports (background: linear-gradient(in oklch, red 0%, red 0% 1%, red 2%)) {
28
- :root {
29
- --background: linear-gradient(in oklch 90deg, black 25%, blue 75%);
30
- }
23
+ .example {
24
+ background-image: linear-gradient(rgb(56, 87, 35) 0%, rgb(64, 83, 46), rgb(70, 79, 54), rgb(76, 74, 62), rgb(82, 69, 68), rgb(86, 64, 75), rgb(91, 58, 81), rgb(95, 51, 87), rgb(99, 44, 93), rgb(103, 34, 98), rgb(106, 21, 104) 100%);
25
+ background-image: linear-gradient(in oklab, hsl(96, 42%, 24%) 0%, hsl(302, 67%, 25%) 100%);
31
26
  }
32
27
  ```
33
28
 
34
- ## Warnings
29
+ ## Shortcomings
35
30
 
36
- ⚠️ This plugin assumes you have a separate plugin to transform `color-mix()` to something older browsers can understand.
31
+ ⚠️ Color stops with only a color or only an interpolation hint are not supported.
37
32
 
38
- ⚠️ Color stops with only a color and Interpolation hints are not supported.
39
- We can not statically check if a certain value is a single color or an interpolation hint.
33
+ For best results you should always provide at least the color and position for each color stop.
34
+ Double position color stops are supported.
40
35
 
41
- These are equivalent in PostCSS :
36
+ ```pcss
37
+ .foo {
38
+ /* Only a color: can't transform */
39
+ background-image: linear-gradient(in oklch, black 0%, green, blue 100%);
40
+
41
+ /* Only an interpolation hint: can't transform */
42
+ background-image: linear-gradient(in oklch, black 0%, 25%, blue 100%);
43
+ }
44
+ ```
45
+
46
+ ⚠️ Variable colors are also not supported.
47
+ We can not mix colors when the color is a variable.
42
48
 
43
49
  ```pcss
50
+ .foo {
44
51
  --red: red;
45
- /* Color stop variable */
46
- background-image: linear-gradient(90deg, black, var(--red), blue);
47
-
48
- --perc-10: 10%;
49
- /* Interpolation hint */
50
- background-image: linear-gradient(90deg, black, var(--perc-10), blue);
52
+ /* Color stop variable : can't transform */
53
+ background-image: linear-gradient(in oklch, black 0%, var(--red), blue 100%);
54
+ }
51
55
  ```
52
56
 
53
57
  ## Usage
@@ -92,29 +96,24 @@ postcssGradientsInterpolationMethod({ preserve: true })
92
96
  ```
93
97
 
94
98
  ```pcss
95
- .oklch {
96
- background: linear-gradient(in oklch 90deg, black 25% , blue 75%);
99
+ .example {
100
+ background-image: linear-gradient(in oklch, hsl(0deg 85% 75%) 0%, hsl(180deg 80% 65%) 100%);
97
101
  }
98
102
 
99
- :root {
100
- --background: linear-gradient(in oklch 90deg, black 25%, blue 75%);
103
+ .example {
104
+ background-image: linear-gradient(in oklab, hsl(96, 42%, 24%) 0%, hsl(302, 67%, 25%) 100%);
101
105
  }
102
106
 
103
107
  /* becomes */
104
108
 
105
- .oklch {
106
- background: linear-gradient(90deg ,black 25%,color-mix(in oklch, black 90%, blue 10%) calc(25% + ((75% - 25%) * 0.1)),color-mix(in oklch, black 80%, blue 20%) calc(25% + ((75% - 25%) * 0.2)),color-mix(in oklch, black 70%, blue 30%) calc(25% + ((75% - 25%) * 0.3)),color-mix(in oklch, black 60%, blue 40%) calc(25% + ((75% - 25%) * 0.4)),color-mix(in oklch, black 50%, blue 50%) calc(25% + ((75% - 25%) * 0.5)),color-mix(in oklch, black 40%, blue 60%) calc(25% + ((75% - 25%) * 0.6)),color-mix(in oklch, black 30%, blue 70%) calc(25% + ((75% - 25%) * 0.7)),color-mix(in oklch, black 20%, blue 80%) calc(25% + ((75% - 25%) * 0.8)),color-mix(in oklch, black 10%, blue 90%) calc(25% + ((75% - 25%) * 0.9)),blue 75%);
107
- background: linear-gradient(in oklch 90deg, black 25% , blue 75%);
109
+ .example {
110
+ background-image: linear-gradient(rgb(245, 137, 137) 0%, rgb(248, 146, 114), rgb(244, 158, 94), rgb(235, 171, 82), rgb(220, 185, 81), rgb(201, 199, 95), rgb(177, 211, 118), rgb(151, 221, 146), rgb(125, 229, 177), rgb(103, 235, 208), rgb(94, 237, 237) 100%);
111
+ background-image: linear-gradient(in oklch, hsl(0deg 85% 75%) 0%, hsl(180deg 80% 65%) 100%);
108
112
  }
109
113
 
110
- :root {
111
- --background: linear-gradient(90deg ,black 25%,color-mix(in oklch, black 90%, blue 10%) calc(25% + ((75% - 25%) * 0.1)),color-mix(in oklch, black 80%, blue 20%) calc(25% + ((75% - 25%) * 0.2)),color-mix(in oklch, black 70%, blue 30%) calc(25% + ((75% - 25%) * 0.3)),color-mix(in oklch, black 60%, blue 40%) calc(25% + ((75% - 25%) * 0.4)),color-mix(in oklch, black 50%, blue 50%) calc(25% + ((75% - 25%) * 0.5)),color-mix(in oklch, black 40%, blue 60%) calc(25% + ((75% - 25%) * 0.6)),color-mix(in oklch, black 30%, blue 70%) calc(25% + ((75% - 25%) * 0.7)),color-mix(in oklch, black 20%, blue 80%) calc(25% + ((75% - 25%) * 0.8)),color-mix(in oklch, black 10%, blue 90%) calc(25% + ((75% - 25%) * 0.9)),blue 75%);
112
- }
113
-
114
- @supports (background: linear-gradient(in oklch, red 0%, red 0% 1%, red 2%)) {
115
- :root {
116
- --background: linear-gradient(in oklch 90deg, black 25%, blue 75%);
117
- }
114
+ .example {
115
+ background-image: linear-gradient(rgb(56, 87, 35) 0%, rgb(64, 83, 46), rgb(70, 79, 54), rgb(76, 74, 62), rgb(82, 69, 68), rgb(86, 64, 75), rgb(91, 58, 81), rgb(95, 51, 87), rgb(99, 44, 93), rgb(103, 34, 98), rgb(106, 21, 104) 100%);
116
+ background-image: linear-gradient(in oklab, hsl(96, 42%, 24%) 0%, hsl(302, 67%, 25%) 100%);
118
117
  }
119
118
  ```
120
119
 
@@ -130,31 +129,31 @@ postcssGradientsInterpolationMethod({ enableProgressiveCustomProperties: false }
130
129
  ```
131
130
 
132
131
  ```pcss
133
- .oklch {
134
- background: linear-gradient(in oklch 90deg, black 25% , blue 75%);
132
+ .example {
133
+ background-image: linear-gradient(in oklch, hsl(0deg 85% 75%) 0%, hsl(180deg 80% 65%) 100%);
135
134
  }
136
135
 
137
- :root {
138
- --background: linear-gradient(in oklch 90deg, black 25%, blue 75%);
136
+ .example {
137
+ background-image: linear-gradient(in oklab, hsl(96, 42%, 24%) 0%, hsl(302, 67%, 25%) 100%);
139
138
  }
140
139
 
141
140
  /* becomes */
142
141
 
143
- .oklch {
144
- background: linear-gradient(90deg ,black 25%,color-mix(in oklch, black 90%, blue 10%) calc(25% + ((75% - 25%) * 0.1)),color-mix(in oklch, black 80%, blue 20%) calc(25% + ((75% - 25%) * 0.2)),color-mix(in oklch, black 70%, blue 30%) calc(25% + ((75% - 25%) * 0.3)),color-mix(in oklch, black 60%, blue 40%) calc(25% + ((75% - 25%) * 0.4)),color-mix(in oklch, black 50%, blue 50%) calc(25% + ((75% - 25%) * 0.5)),color-mix(in oklch, black 40%, blue 60%) calc(25% + ((75% - 25%) * 0.6)),color-mix(in oklch, black 30%, blue 70%) calc(25% + ((75% - 25%) * 0.7)),color-mix(in oklch, black 20%, blue 80%) calc(25% + ((75% - 25%) * 0.8)),color-mix(in oklch, black 10%, blue 90%) calc(25% + ((75% - 25%) * 0.9)),blue 75%);
145
- background: linear-gradient(in oklch 90deg, black 25% , blue 75%);
142
+ .example {
143
+ background-image: linear-gradient(rgb(245, 137, 137) 0%, rgb(248, 146, 114), rgb(244, 158, 94), rgb(235, 171, 82), rgb(220, 185, 81), rgb(201, 199, 95), rgb(177, 211, 118), rgb(151, 221, 146), rgb(125, 229, 177), rgb(103, 235, 208), rgb(94, 237, 237) 100%);
144
+ background-image: linear-gradient(in oklch, hsl(0deg 85% 75%) 0%, hsl(180deg 80% 65%) 100%);
146
145
  }
147
146
 
148
- :root {
149
- --background: linear-gradient(90deg ,black 25%,color-mix(in oklch, black 90%, blue 10%) calc(25% + ((75% - 25%) * 0.1)),color-mix(in oklch, black 80%, blue 20%) calc(25% + ((75% - 25%) * 0.2)),color-mix(in oklch, black 70%, blue 30%) calc(25% + ((75% - 25%) * 0.3)),color-mix(in oklch, black 60%, blue 40%) calc(25% + ((75% - 25%) * 0.4)),color-mix(in oklch, black 50%, blue 50%) calc(25% + ((75% - 25%) * 0.5)),color-mix(in oklch, black 40%, blue 60%) calc(25% + ((75% - 25%) * 0.6)),color-mix(in oklch, black 30%, blue 70%) calc(25% + ((75% - 25%) * 0.7)),color-mix(in oklch, black 20%, blue 80%) calc(25% + ((75% - 25%) * 0.8)),color-mix(in oklch, black 10%, blue 90%) calc(25% + ((75% - 25%) * 0.9)),blue 75%);
150
- --background: linear-gradient(in oklch 90deg, black 25%, blue 75%);
147
+ .example {
148
+ background-image: linear-gradient(rgb(56, 87, 35) 0%, rgb(64, 83, 46), rgb(70, 79, 54), rgb(76, 74, 62), rgb(82, 69, 68), rgb(86, 64, 75), rgb(91, 58, 81), rgb(95, 51, 87), rgb(99, 44, 93), rgb(103, 34, 98), rgb(106, 21, 104) 100%);
149
+ background-image: linear-gradient(in oklab, hsl(96, 42%, 24%) 0%, hsl(302, 67%, 25%) 100%);
151
150
  }
152
151
  ```
153
152
 
154
153
  _Custom properties do not fallback to the previous declaration_
155
154
 
156
155
  [cli-url]: https://github.com/csstools/postcss-plugins/actions/workflows/test.yml?query=workflow/test
157
- [css-url]: https://cssdb.org/#TODO
156
+ [css-url]: https://cssdb.org/#gradients-interpolation-method
158
157
  [discord]: https://discord.gg/bUadyRwkJS
159
158
  [npm-url]: https://www.npmjs.com/package/@csstools/postcss-gradients-interpolation-method
160
159
 
@@ -1,8 +1,9 @@
1
- import type { Node } from 'postcss-value-parser';
1
+ import type { ColorData } from '@csstools/css-color-parser';
2
+ import type { ComponentValue } from '@csstools/css-parser-algorithms';
3
+ import { TokenNode } from '@csstools/css-parser-algorithms';
2
4
  export type ColorStop = {
3
- color: string;
4
- colorStopLength: string;
5
- colorHintBetween: Array<ColorStop>;
6
- nodes: Array<Node>;
5
+ color: ComponentValue;
6
+ colorData: ColorData;
7
+ position: ComponentValue;
7
8
  };
8
- export declare function colorStopList(nodes: Array<Node>, interpolationArguments: string): Array<ColorStop> | false;
9
+ export declare function interpolateColorsInColorStopsList(colorStops: Array<ColorStop>, colorSpace: TokenNode, hueInterpolationMethod: TokenNode | null): Array<ComponentValue> | false;
@@ -0,0 +1,2 @@
1
+ import type { Declaration } from 'postcss';
2
+ export declare function hasFallback(node: Declaration): boolean;
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";var e=require("@csstools/postcss-progressive-custom-properties"),o=require("postcss-value-parser");function colorStopList(e,t){const n=[];let r={color:"",colorStopLength:"",colorHintBetween:[],nodes:[]};for(let o=0;o<e.length;o++){const t=e[o];"div"!==t.type||","!==t.value?r.nodes.push(t):(n.push(r),r={color:"",colorStopLength:"",colorHintBetween:[],nodes:[]})}n.push(r);const s=[];for(let e=0;e<n.length;e++){const t=n[e];switch(t.nodes.length){case 0:break;case 1:return!1;case 2:t.color=o.stringify(t.nodes[0]),t.colorStopLength=o.stringify(t.nodes[1]),s.push(t);break;case 3:s.push({color:o.stringify(t.nodes[0]),colorStopLength:o.stringify(t.nodes[1]),colorHintBetween:[],nodes:[t.nodes[0],t.nodes[1]]}),s.push({color:o.stringify(t.nodes[0]),colorStopLength:o.stringify(t.nodes[2]),colorHintBetween:[],nodes:[t.nodes[0],t.nodes[2]]})}}for(let e=0;e<s.length;e++){const o=s[e];o.color||(o.color=`color-mix(in ${t}, ${s[e-1].color} 50%, ${s[e+1].color} 50%)`,o.colorHintBetween=[s[e-1],s[e+1]])}return s}function includesGradientsFunction(e){return e.includes("in ")&&(e.includes("conic-gradient(")||e.includes("linear-gradient(")||e.includes("radial-gradient(")||e.includes("repeating-conic-gradient(")||e.includes("repeating-linear-gradient(")||e.includes("repeating-radial-gradient("))}function hasSupportsAtRuleAncestor(e){let o=e.parent;for(;o;)if("atrule"===o.type){if("supports"===o.name&&includesGradientsFunction(o.params))return!0;o=o.parent}else o=o.parent;return!1}const t=["shorter","longer","increasing","decreasing","specified"],basePlugin=e=>({postcssPlugin:"postcss-gradients-interpolation-method",Declaration(n,{result:r}){if(!includesGradientsFunction(n.value))return;if(hasSupportsAtRuleAncestor(n))return;let s;try{s=o(n.value)}catch(e){n.warn(r,`Failed to parse value '${n.value}' as a CSS gradient. Leaving the original value intact.`)}if(void 0===s)return;let i=!1;if(s.walk((e=>{if("function"!==e.type||"conic-gradient"!==(n=e.value)&&"linear-gradient"!==n&&"radial-gradient"!==n&&"repeating-conic-gradient"!==n&&"repeating-linear-gradient"!==n&&"repeating-radial-gradient"!==n)return;var n;const r=e.nodes.filter((e=>"comment"!==e.type&&"space"!==e.type)),s={interpolationArguments:[],argumentsRemainder:[],colorStops:[]};for(let e=0;e<r.length;e++){const n=r[e];if("div"===n.type&&","===n.value){const t=s.interpolationArguments.map((e=>o.stringify(e))).join(" "),n=colorStopList(r.slice(e),t);if(n){s.colorStops=n;break}t&&(i=!0);break}if("word"===n.type&&"in"===n.value&&r[e+1]){if(s.interpolationArguments.push(r[e+1]),e++,r[e+1]&&"word"===r[e+1].type&&t.includes(r[e+1].value)){s.interpolationArguments.push(r[e+1]),e++;continue}}else s.argumentsRemainder.push(n)}if(!s.interpolationArguments.length||!s.colorStops.length)return;e.nodes=[],s.argumentsRemainder.length&&(e.nodes.push(...s.argumentsRemainder.flatMap((e=>[e,{type:"space",value:" "}]))),e.nodes.push({type:"div",value:","}));const l=s.interpolationArguments.map((e=>o.stringify(e))).join(" ");for(let t=0;t<s.colorStops.length;t++){const n=s.colorStops[t],r=s.colorStops[t+1];if(r)if(n.color!==r.color)for(let t=0;t<10;t++){if(0===t){e.nodes.push(o(`${n.color} ${n.colorStopLength}`),{type:"div",value:","});continue}const s=`color-mix(in ${l}, ${n.color} ${100-10*t}%, ${r.color} ${10*t}%)`,i=`calc(${n.colorStopLength} + ((${r.colorStopLength} - ${n.colorStopLength}) * ${t/10}))`;e.nodes.push(o(`${s} ${i}`),{type:"div",value:","})}else e.nodes.push(o(`${n.color} ${n.colorStopLength}`),{type:"div",value:","});else e.nodes.push(o(`${n.color} ${n.colorStopLength}`))}})),i)return void n.warn(r,`Failed to parse value '${n.value}' as a CSS gradient with interpolation. Leaving the original value intact.`);const l=o.stringify(s);l!==n.value&&(e.preserve?n.cloneBefore({prop:n.prop,value:l}):n.value=l)}});basePlugin.postcss=!0;const postcssPlugin=o=>{const t=Object.assign({enableProgressiveCustomProperties:!0,preserve:!0},o);return t.enableProgressiveCustomProperties&&t.preserve?{postcssPlugin:"postcss-gradients-interpolation-method",plugins:[e(),basePlugin(t)]}:basePlugin(t)};postcssPlugin.postcss=!0,module.exports=postcssPlugin;
1
+ "use strict";var e=require("@csstools/postcss-progressive-custom-properties"),o=require("@csstools/css-parser-algorithms"),t=require("@csstools/css-tokenizer"),n=require("@csstools/css-color-parser");const i=/(repeating-)?(linear|radial|conic)-gradient\(/i,s=/^(repeating-)?(linear|radial|conic)-gradient$/i;function hasFallback(e){const o=e.parent;if(!o)return!1;const t=e.prop.toLowerCase(),n=o.index(e);for(let e=0;e<n;e++){const n=o.nodes[e];if("decl"===n.type&&n.prop.toLowerCase()===t)return!0}return!1}function hasSupportsAtRuleAncestor(e){let o=e.parent;for(;o;)if("atrule"===o.type){if("supports"===o.name&&i.test(o.params))return!0;o=o.parent}else o=o.parent;return!1}function interpolateColorsInColorStopsList(e,i,s){const r=[],a=[];for(let r=0;r<e.length-1;r++){const l=e[r],c=e[r+1];if(a.push(l),n.serializeP3(l.colorData).toString()!==n.serializeP3(c.colorData).toString()&&l.position.toString()!==c.position.toString())for(let e=1;e<=9;e++){const r=10*e;let p=[];s&&(p=[new o.WhitespaceNode([[t.TokenType.Whitespace," ",-1,-1,void 0]]),s,new o.WhitespaceNode([[t.TokenType.Whitespace," ",-1,-1,void 0]]),new o.TokenNode([t.TokenType.Ident,"hue",-1,-1,{value:"hue"}])]);const u=new o.FunctionNode([t.TokenType.Function,"color-mix(",-1,-1,{value:"color-mix"}],[t.TokenType.CloseParen,")",-1,-1,void 0],[new o.TokenNode([t.TokenType.Ident,"in",-1,-1,{value:"in"}]),new o.WhitespaceNode([[t.TokenType.Whitespace," ",-1,-1,void 0]]),i,...p,new o.TokenNode([t.TokenType.Comma,",",-1,-1,void 0]),new o.WhitespaceNode([[t.TokenType.Whitespace," ",-1,-1,void 0]]),l.color,new o.WhitespaceNode([[t.TokenType.Whitespace," ",-1,-1,void 0]]),new o.TokenNode([t.TokenType.Percentage,100-r+"%",-1,-1,{value:100-r}]),new o.TokenNode([t.TokenType.Comma,",",-1,-1,void 0]),new o.WhitespaceNode([[t.TokenType.Whitespace," ",-1,-1,void 0]]),c.color,new o.WhitespaceNode([[t.TokenType.Whitespace," ",-1,-1,void 0]]),new o.TokenNode([t.TokenType.Percentage,`${r}%`,-1,-1,{value:r}])]),d=n.color(u);if(!d)return!1;a.push({colorData:d})}r===e.length-2&&a.push(c)}for(let e=0;e<a.length;e++)n.colorDataFitsRGB_Gamut(a[e].colorData)?a[e].color=n.serializeRGB(a[e].colorData):a[e].color=n.serializeP3(a[e].colorData);for(let e=0;e<a.length;e++){const n=a[e];n.position?r.push(n.color,new o.WhitespaceNode([[t.TokenType.Whitespace," ",-1,-1,void 0]]),n.position):r.push(n.color),e!==a.length-1&&r.push(new o.TokenNode([t.TokenType.Comma,",",-1,-1,void 0]),new o.WhitespaceNode([[t.TokenType.Whitespace," ",-1,-1,void 0]]))}return r}function parseColorStops(e){const i=[];let s={};for(let r=0;r<e.length;r++){const a=e[r];if(o.isCommentNode(a)||o.isWhitespaceNode(a))continue;if(o.isTokenNode(a)&&a.value[0]===t.TokenType.Comma){if(s.color&&s.colorData&&s.positionA){i.push({color:s.color,colorData:s.colorData,position:s.positionA}),s.positionB&&i.push({color:s.color,colorData:s.colorData,position:s.positionB}),s={};continue}return!1}const l=n.color(a);if(l&&s.color)return!1;if(l)s.color=a,s.colorData=l;else if(s.positionA){if(!s.positionA||s.positionB)return!1;s.positionB=a}else s.positionA=a}return!(!s.color||!s.positionA)&&(s.color&&s.colorData&&s.positionA&&(i.push({color:s.color,colorData:s.colorData,position:s.positionA}),s.positionB&&i.push({color:s.color,colorData:s.colorData,position:s.positionB})),!(i.length<2)&&i)}const r=/^(srgb|srgb-linear|lab|oklab|xyz|xyz-d50|xyz-d65|hsl|hwb|lch|oklch)$/i,a=/^(hsl|hwb|lch|oklch)$/i,l=/^(shorter|longer|increasing|decreasing)$/i,c=/^in$/i,p=/^hue$/i;function modifyGradientFunctionComponentValues(e){const n=e.getName();if(!s.test(n))return!1;let i="srgb",u=null,d=null,T=null,v=null,h=null,f=[];{let n=0,s=e.value[n];for(;!o.isTokenNode(s)||s.value[0]!==t.TokenType.Ident||!c.test(s.value[4].value);){if(o.isTokenNode(s)&&s.value[0]===t.TokenType.Comma)return!1;n++,s=e.value[n]}for(u=s,n++,s=e.value[n];o.isCommentNode(s)||o.isWhitespaceNode(s);)n++,s=e.value[n];if(o.isTokenNode(s)&&s.value[0]===t.TokenType.Ident&&r.test(s.value[4].value)){if(d)return!1;d=s,i=s.value[4].value,n++,s=e.value[n]}for(;o.isCommentNode(s)||o.isWhitespaceNode(s);)n++,s=e.value[n];if(o.isTokenNode(s)&&s.value[0]===t.TokenType.Ident&&l.test(s.value[4].value)&&a.test(i)){if(T||!d)return!1;T=s,n++,s=e.value[n]}for(;o.isCommentNode(s)||o.isWhitespaceNode(s);)n++,s=e.value[n];if(o.isTokenNode(s)&&s.value[0]===t.TokenType.Ident&&p.test(s.value[4].value)){if(v||!d||!T)return!1;v=s,n++,s=e.value[n]}for(;!o.isTokenNode(s)||s.value[0]!==t.TokenType.Comma;)n++,s=e.value[n];h=s,f=e.value.slice(n+1)}if(!d)return!1;if(T&&!v)return!1;if(v&&!T)return!1;const k=parseColorStops(f);if(!k)return!1;const m=interpolateColorsInColorStopsList(k,d,T);if(!m)return!1;const N=[...e.value.slice(0,e.value.indexOf(u)),...e.value.slice(e.value.indexOf(v||d)+1,e.value.indexOf(h))];return N.length>0&&N.some((e=>!o.isCommentNode(e)&&!o.isWhitespaceNode(e)))&&N.push(new o.TokenNode([t.TokenType.Comma,",",-1,-1,void 0]),new o.WhitespaceNode([[t.TokenType.Whitespace," ",-1,-1,void 0]])),[...N,...m]}const basePlugin=e=>({postcssPlugin:"postcss-gradients-interpolation-method",Declaration(n){if(!i.test(n.value))return;if(hasFallback(n))return;if(hasSupportsAtRuleAncestor(n))return;const s=o.stringify(o.replaceComponentValues(o.parseCommaSeparatedListOfComponentValues(t.tokenize({css:n.value})),(e=>{if(!o.isFunctionNode(e))return;const t=modifyGradientFunctionComponentValues(e);t&&(e.value=t)})));s!==n.value&&(n.cloneBefore({value:s}),null!=e&&e.preserve||n.remove())}});basePlugin.postcss=!0;const postcssPlugin=o=>{const t=Object.assign({enableProgressiveCustomProperties:!0,preserve:!0},o);return t.enableProgressiveCustomProperties&&t.preserve?{postcssPlugin:"postcss-gradients-interpolation-method",plugins:[e(),basePlugin(t)]}:basePlugin(t)};postcssPlugin.postcss=!0,module.exports=postcssPlugin;
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import type { PluginCreator } from 'postcss';
2
2
  /** postcss-gradients-interpolation-method plugin options */
3
3
  export type pluginOptions = {
4
- /** Preserve the original notation. default: true */
4
+ /** Preserve the original notation. default: false */
5
5
  preserve?: boolean;
6
6
  /** Enable "@csstools/postcss-progressive-custom-properties". default: true */
7
7
  enableProgressiveCustomProperties?: boolean;
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import e from"@csstools/postcss-progressive-custom-properties";import o from"postcss-value-parser";function colorStopList(e,t){const n=[];let r={color:"",colorStopLength:"",colorHintBetween:[],nodes:[]};for(let o=0;o<e.length;o++){const t=e[o];"div"!==t.type||","!==t.value?r.nodes.push(t):(n.push(r),r={color:"",colorStopLength:"",colorHintBetween:[],nodes:[]})}n.push(r);const s=[];for(let e=0;e<n.length;e++){const t=n[e];switch(t.nodes.length){case 0:break;case 1:return!1;case 2:t.color=o.stringify(t.nodes[0]),t.colorStopLength=o.stringify(t.nodes[1]),s.push(t);break;case 3:s.push({color:o.stringify(t.nodes[0]),colorStopLength:o.stringify(t.nodes[1]),colorHintBetween:[],nodes:[t.nodes[0],t.nodes[1]]}),s.push({color:o.stringify(t.nodes[0]),colorStopLength:o.stringify(t.nodes[2]),colorHintBetween:[],nodes:[t.nodes[0],t.nodes[2]]})}}for(let e=0;e<s.length;e++){const o=s[e];o.color||(o.color=`color-mix(in ${t}, ${s[e-1].color} 50%, ${s[e+1].color} 50%)`,o.colorHintBetween=[s[e-1],s[e+1]])}return s}function includesGradientsFunction(e){return e.includes("in ")&&(e.includes("conic-gradient(")||e.includes("linear-gradient(")||e.includes("radial-gradient(")||e.includes("repeating-conic-gradient(")||e.includes("repeating-linear-gradient(")||e.includes("repeating-radial-gradient("))}function hasSupportsAtRuleAncestor(e){let o=e.parent;for(;o;)if("atrule"===o.type){if("supports"===o.name&&includesGradientsFunction(o.params))return!0;o=o.parent}else o=o.parent;return!1}const t=["shorter","longer","increasing","decreasing","specified"],basePlugin=e=>({postcssPlugin:"postcss-gradients-interpolation-method",Declaration(n,{result:r}){if(!includesGradientsFunction(n.value))return;if(hasSupportsAtRuleAncestor(n))return;let s;try{s=o(n.value)}catch(e){n.warn(r,`Failed to parse value '${n.value}' as a CSS gradient. Leaving the original value intact.`)}if(void 0===s)return;let i=!1;if(s.walk((e=>{if("function"!==e.type||"conic-gradient"!==(n=e.value)&&"linear-gradient"!==n&&"radial-gradient"!==n&&"repeating-conic-gradient"!==n&&"repeating-linear-gradient"!==n&&"repeating-radial-gradient"!==n)return;var n;const r=e.nodes.filter((e=>"comment"!==e.type&&"space"!==e.type)),s={interpolationArguments:[],argumentsRemainder:[],colorStops:[]};for(let e=0;e<r.length;e++){const n=r[e];if("div"===n.type&&","===n.value){const t=s.interpolationArguments.map((e=>o.stringify(e))).join(" "),n=colorStopList(r.slice(e),t);if(n){s.colorStops=n;break}t&&(i=!0);break}if("word"===n.type&&"in"===n.value&&r[e+1]){if(s.interpolationArguments.push(r[e+1]),e++,r[e+1]&&"word"===r[e+1].type&&t.includes(r[e+1].value)){s.interpolationArguments.push(r[e+1]),e++;continue}}else s.argumentsRemainder.push(n)}if(!s.interpolationArguments.length||!s.colorStops.length)return;e.nodes=[],s.argumentsRemainder.length&&(e.nodes.push(...s.argumentsRemainder.flatMap((e=>[e,{type:"space",value:" "}]))),e.nodes.push({type:"div",value:","}));const l=s.interpolationArguments.map((e=>o.stringify(e))).join(" ");for(let t=0;t<s.colorStops.length;t++){const n=s.colorStops[t],r=s.colorStops[t+1];if(r)if(n.color!==r.color)for(let t=0;t<10;t++){if(0===t){e.nodes.push(o(`${n.color} ${n.colorStopLength}`),{type:"div",value:","});continue}const s=`color-mix(in ${l}, ${n.color} ${100-10*t}%, ${r.color} ${10*t}%)`,i=`calc(${n.colorStopLength} + ((${r.colorStopLength} - ${n.colorStopLength}) * ${t/10}))`;e.nodes.push(o(`${s} ${i}`),{type:"div",value:","})}else e.nodes.push(o(`${n.color} ${n.colorStopLength}`),{type:"div",value:","});else e.nodes.push(o(`${n.color} ${n.colorStopLength}`))}})),i)return void n.warn(r,`Failed to parse value '${n.value}' as a CSS gradient with interpolation. Leaving the original value intact.`);const l=o.stringify(s);l!==n.value&&(e.preserve?n.cloneBefore({prop:n.prop,value:l}):n.value=l)}});basePlugin.postcss=!0;const postcssPlugin=o=>{const t=Object.assign({enableProgressiveCustomProperties:!0,preserve:!0},o);return t.enableProgressiveCustomProperties&&t.preserve?{postcssPlugin:"postcss-gradients-interpolation-method",plugins:[e(),basePlugin(t)]}:basePlugin(t)};postcssPlugin.postcss=!0;export{postcssPlugin as default};
1
+ import o from"@csstools/postcss-progressive-custom-properties";import{WhitespaceNode as e,TokenNode as t,FunctionNode as r,isCommentNode as n,isWhitespaceNode as s,isTokenNode as i,stringify as l,replaceComponentValues as a,parseCommaSeparatedListOfComponentValues as c,isFunctionNode as u}from"@csstools/css-parser-algorithms";import{TokenType as p,tokenize as v}from"@csstools/css-tokenizer";import{serializeP3 as f,color as h,colorDataFitsRGB_Gamut as d,serializeRGB as m}from"@csstools/css-color-parser";const g=/(repeating-)?(linear|radial|conic)-gradient\(/i,w=/^(repeating-)?(linear|radial|conic)-gradient$/i;function hasFallback(o){const e=o.parent;if(!e)return!1;const t=o.prop.toLowerCase(),r=e.index(o);for(let o=0;o<r;o++){const r=e.nodes[o];if("decl"===r.type&&r.prop.toLowerCase()===t)return!0}return!1}function hasSupportsAtRuleAncestor(o){let e=o.parent;for(;e;)if("atrule"===e.type){if("supports"===e.name&&g.test(e.params))return!0;e=e.parent}else e=e.parent;return!1}function interpolateColorsInColorStopsList(o,n,s){const i=[],l=[];for(let i=0;i<o.length-1;i++){const a=o[i],c=o[i+1];if(l.push(a),f(a.colorData).toString()!==f(c.colorData).toString()&&a.position.toString()!==c.position.toString())for(let o=1;o<=9;o++){const i=10*o;let u=[];s&&(u=[new e([[p.Whitespace," ",-1,-1,void 0]]),s,new e([[p.Whitespace," ",-1,-1,void 0]]),new t([p.Ident,"hue",-1,-1,{value:"hue"}])]);const v=new r([p.Function,"color-mix(",-1,-1,{value:"color-mix"}],[p.CloseParen,")",-1,-1,void 0],[new t([p.Ident,"in",-1,-1,{value:"in"}]),new e([[p.Whitespace," ",-1,-1,void 0]]),n,...u,new t([p.Comma,",",-1,-1,void 0]),new e([[p.Whitespace," ",-1,-1,void 0]]),a.color,new e([[p.Whitespace," ",-1,-1,void 0]]),new t([p.Percentage,100-i+"%",-1,-1,{value:100-i}]),new t([p.Comma,",",-1,-1,void 0]),new e([[p.Whitespace," ",-1,-1,void 0]]),c.color,new e([[p.Whitespace," ",-1,-1,void 0]]),new t([p.Percentage,`${i}%`,-1,-1,{value:i}])]),f=h(v);if(!f)return!1;l.push({colorData:f})}i===o.length-2&&l.push(c)}for(let o=0;o<l.length;o++)d(l[o].colorData)?l[o].color=m(l[o].colorData):l[o].color=f(l[o].colorData);for(let o=0;o<l.length;o++){const r=l[o];r.position?i.push(r.color,new e([[p.Whitespace," ",-1,-1,void 0]]),r.position):i.push(r.color),o!==l.length-1&&i.push(new t([p.Comma,",",-1,-1,void 0]),new e([[p.Whitespace," ",-1,-1,void 0]]))}return i}function parseColorStops(o){const e=[];let t={};for(let r=0;r<o.length;r++){const l=o[r];if(n(l)||s(l))continue;if(i(l)&&l.value[0]===p.Comma){if(t.color&&t.colorData&&t.positionA){e.push({color:t.color,colorData:t.colorData,position:t.positionA}),t.positionB&&e.push({color:t.color,colorData:t.colorData,position:t.positionB}),t={};continue}return!1}const a=h(l);if(a&&t.color)return!1;if(a)t.color=l,t.colorData=a;else if(t.positionA){if(!t.positionA||t.positionB)return!1;t.positionB=l}else t.positionA=l}return!(!t.color||!t.positionA)&&(t.color&&t.colorData&&t.positionA&&(e.push({color:t.color,colorData:t.colorData,position:t.positionA}),t.positionB&&e.push({color:t.color,colorData:t.colorData,position:t.positionB})),!(e.length<2)&&e)}const C=/^(srgb|srgb-linear|lab|oklab|xyz|xyz-d50|xyz-d65|hsl|hwb|lch|oklch)$/i,D=/^(hsl|hwb|lch|oklch)$/i,b=/^(shorter|longer|increasing|decreasing)$/i,A=/^in$/i,x=/^hue$/i;function modifyGradientFunctionComponentValues(o){const r=o.getName();if(!w.test(r))return!1;let l="srgb",a=null,c=null,u=null,v=null,f=null,h=[];{let e=0,t=o.value[e];for(;!i(t)||t.value[0]!==p.Ident||!A.test(t.value[4].value);){if(i(t)&&t.value[0]===p.Comma)return!1;e++,t=o.value[e]}for(a=t,e++,t=o.value[e];n(t)||s(t);)e++,t=o.value[e];if(i(t)&&t.value[0]===p.Ident&&C.test(t.value[4].value)){if(c)return!1;c=t,l=t.value[4].value,e++,t=o.value[e]}for(;n(t)||s(t);)e++,t=o.value[e];if(i(t)&&t.value[0]===p.Ident&&b.test(t.value[4].value)&&D.test(l)){if(u||!c)return!1;u=t,e++,t=o.value[e]}for(;n(t)||s(t);)e++,t=o.value[e];if(i(t)&&t.value[0]===p.Ident&&x.test(t.value[4].value)){if(v||!c||!u)return!1;v=t,e++,t=o.value[e]}for(;!i(t)||t.value[0]!==p.Comma;)e++,t=o.value[e];f=t,h=o.value.slice(e+1)}if(!c)return!1;if(u&&!v)return!1;if(v&&!u)return!1;const d=parseColorStops(h);if(!d)return!1;const m=interpolateColorsInColorStopsList(d,c,u);if(!m)return!1;const g=[...o.value.slice(0,o.value.indexOf(a)),...o.value.slice(o.value.indexOf(v||c)+1,o.value.indexOf(f))];return g.length>0&&g.some((o=>!n(o)&&!s(o)))&&g.push(new t([p.Comma,",",-1,-1,void 0]),new e([[p.Whitespace," ",-1,-1,void 0]])),[...g,...m]}const basePlugin=o=>({postcssPlugin:"postcss-gradients-interpolation-method",Declaration(e){if(!g.test(e.value))return;if(hasFallback(e))return;if(hasSupportsAtRuleAncestor(e))return;const t=l(a(c(v({css:e.value})),(o=>{if(!u(o))return;const e=modifyGradientFunctionComponentValues(o);e&&(o.value=e)})));t!==e.value&&(e.cloneBefore({value:t}),null!=o&&o.preserve||e.remove())}});basePlugin.postcss=!0;const postcssPlugin=e=>{const t=Object.assign({enableProgressiveCustomProperties:!0,preserve:!0},e);return t.enableProgressiveCustomProperties&&t.preserve?{postcssPlugin:"postcss-gradients-interpolation-method",plugins:[o(),basePlugin(t)]}:basePlugin(t)};postcssPlugin.postcss=!0;export{postcssPlugin as default};
@@ -1,2 +1,2 @@
1
- export declare function includesGradientsFunction(str: string): boolean;
2
- export declare function isGradientsFunctions(str: string): boolean;
1
+ export declare const gradientFunctionRegex: RegExp;
2
+ export declare const gradientNameRegex: RegExp;
@@ -0,0 +1,2 @@
1
+ import type { ComponentValue, FunctionNode } from '@csstools/css-parser-algorithms';
2
+ export declare function modifyGradientFunctionComponentValues(gradientFunction: FunctionNode): Array<ComponentValue> | false;
@@ -0,0 +1,3 @@
1
+ import type { ColorStop } from './color-stop-list';
2
+ import type { ComponentValue } from '@csstools/css-parser-algorithms';
3
+ export declare function parseColorStops(componentValues: Array<ComponentValue>): Array<ColorStop> | false;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@csstools/postcss-gradients-interpolation-method",
3
3
  "description": "Use interpolation methods in CSS gradient functions",
4
- "version": "2.0.0",
4
+ "version": "3.0.0",
5
5
  "author": "Jonathan Neal <jonathantneal@hotmail.com>",
6
6
  "license": "CC0-1.0",
7
7
  "funding": {
@@ -16,6 +16,7 @@
16
16
  "types": "dist/index.d.ts",
17
17
  "exports": {
18
18
  ".": {
19
+ "types": "./dist/index.d.ts",
19
20
  "import": "./dist/index.mjs",
20
21
  "require": "./dist/index.cjs",
21
22
  "default": "./dist/index.mjs"
@@ -28,23 +29,23 @@
28
29
  "dist"
29
30
  ],
30
31
  "dependencies": {
31
- "@csstools/postcss-progressive-custom-properties": "^2.0.0",
32
- "postcss-value-parser": "^4.2.0"
32
+ "@csstools/css-color-parser": "^1.0.0",
33
+ "@csstools/css-parser-algorithms": "^2.1.0",
34
+ "@csstools/css-tokenizer": "^2.1.0",
35
+ "@csstools/postcss-progressive-custom-properties": "^2.0.0"
33
36
  },
34
37
  "peerDependencies": {
35
38
  "postcss": "^8.4"
36
39
  },
40
+ "devDependencies": {
41
+ "@csstools/postcss-tape": "*"
42
+ },
37
43
  "scripts": {
38
- "prebuild": "npm run clean",
39
44
  "build": "rollup -c ../../rollup/default.mjs",
40
- "clean": "node -e \"fs.rmSync('./dist', { recursive: true, force: true }); fs.mkdirSync('./dist');\"",
41
45
  "docs": "node ../../.github/bin/generate-docs/install.mjs && node ../../.github/bin/generate-docs/readme.mjs",
42
- "lint": "npm run lint:eslint && npm run lint:package-json",
43
- "lint:eslint": "eslint ./src --ext .js --ext .ts --ext .mjs --no-error-on-unmatched-pattern",
44
- "lint:package-json": "node ../../.github/bin/format-package-json.mjs",
45
- "prepublishOnly": "npm run clean && npm run build && npm run test",
46
- "test": "node .tape.mjs && npm run test:exports",
47
- "test:exports": "node ./test/_import.mjs && node ./test/_require.cjs",
46
+ "lint": "node ../../.github/bin/format-package-json.mjs",
47
+ "prepublishOnly": "npm run build && npm run test",
48
+ "test": "node .tape.mjs && node ./test/_import.mjs && node ./test/_require.cjs",
48
49
  "test:rewrite-expects": "REWRITE_EXPECTS=true node .tape.mjs"
49
50
  },
50
51
  "homepage": "https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-gradients-interpolation-method#readme",
@@ -68,7 +69,7 @@
68
69
  "syntax"
69
70
  ],
70
71
  "csstools": {
71
- "cssdbId": "TODO",
72
+ "cssdbId": "gradients-interpolation-method",
72
73
  "exportName": "postcssGradientsInterpolationMethod",
73
74
  "humanReadableName": "PostCSS Gradients Interpolation Method",
74
75
  "specUrl": "https://drafts.csswg.org/css-images-4/#linear-gradients"