@csstools/postcss-gradients-interpolation-method 2.0.1 → 3.0.1

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.1 (March 28, 2023)
4
+
5
+ - Skip gamut mapping for interpolation color hints.
6
+
7
+ ### 3.0.0 (March 25, 2023)
8
+
9
+ - Handle `color-mix()` internally with `@csstools/css-color-parser`
10
+
3
11
  ### 2.0.1 (January 28, 2023)
4
12
 
5
13
  - Improve `types` declaration in `package.json`
package/README.md CHANGED
@@ -1,53 +1,62 @@
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
12
  :root {
13
- --background: linear-gradient(in oklch 90deg, black 25%, blue 75%);
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
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%);
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
25
  }
26
26
 
27
- @supports (background: linear-gradient(in oklch, red 0%, red 0% 1%, red 2%)) {
27
+ @supports (background: linear-gradient(in oklch, red 0%, red 0% 1%, red 2%)) and (color: hsl(0 0% 0% / 0)) {
28
28
  :root {
29
- --background: linear-gradient(in oklch 90deg, black 25%, blue 75%);
29
+ --background-image: linear-gradient(in oklab, hsl(96, 42%, 24%) 0%, hsl(302, 67%, 25%) 100%);
30
30
  }
31
31
  }
32
32
  ```
33
33
 
34
- ## Warnings
34
+ ## Shortcomings
35
35
 
36
- ⚠️ This plugin assumes you have a separate plugin to transform `color-mix()` to something older browsers can understand.
36
+ ⚠️ Color stops with only a color or only an interpolation hint are not supported.
37
37
 
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.
38
+ For best results you should always provide at least the color and position for each color stop.
39
+ Double position color stops are supported.
40
40
 
41
- These are equivalent in PostCSS :
41
+ ```pcss
42
+ .foo {
43
+ /* Only a color: can't transform */
44
+ background-image: linear-gradient(in oklch, black 0%, green, blue 100%);
45
+
46
+ /* Only an interpolation hint: can't transform */
47
+ background-image: linear-gradient(in oklch, black 0%, 25%, blue 100%);
48
+ }
49
+ ```
50
+
51
+ ⚠️ Variable colors are also not supported.
52
+ We can not mix colors when the color is a variable.
42
53
 
43
54
  ```pcss
55
+ .foo {
44
56
  --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);
57
+ /* Color stop variable : can't transform */
58
+ background-image: linear-gradient(in oklch, black 0%, var(--red), blue 100%);
59
+ }
51
60
  ```
52
61
 
53
62
  ## Usage
@@ -85,36 +94,29 @@ instructions for:
85
94
  ### preserve
86
95
 
87
96
  The `preserve` option determines whether the original notation
88
- is preserved. By default, it is not preserved.
97
+ is preserved. By default, it is preserved.
89
98
 
90
99
  ```js
91
- postcssGradientsInterpolationMethod({ preserve: true })
100
+ postcssGradientsInterpolationMethod({ preserve: false })
92
101
  ```
93
102
 
94
103
  ```pcss
95
- .oklch {
96
- background: linear-gradient(in oklch 90deg, black 25% , blue 75%);
104
+ .example {
105
+ background-image: linear-gradient(in oklch, hsl(0deg 85% 75%) 0%, hsl(180deg 80% 65%) 100%);
97
106
  }
98
107
 
99
108
  :root {
100
- --background: linear-gradient(in oklch 90deg, black 25%, blue 75%);
109
+ --background-image: linear-gradient(in oklab, hsl(96, 42%, 24%) 0%, hsl(302, 67%, 25%) 100%);
101
110
  }
102
111
 
103
112
  /* becomes */
104
113
 
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%);
114
+ .example {
115
+ 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%);
108
116
  }
109
117
 
110
118
  :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
- }
119
+ --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%);
118
120
  }
119
121
  ```
120
122
 
@@ -130,31 +132,31 @@ postcssGradientsInterpolationMethod({ enableProgressiveCustomProperties: false }
130
132
  ```
131
133
 
132
134
  ```pcss
133
- .oklch {
134
- background: linear-gradient(in oklch 90deg, black 25% , blue 75%);
135
+ .example {
136
+ background-image: linear-gradient(in oklch, hsl(0deg 85% 75%) 0%, hsl(180deg 80% 65%) 100%);
135
137
  }
136
138
 
137
139
  :root {
138
- --background: linear-gradient(in oklch 90deg, black 25%, blue 75%);
140
+ --background-image: linear-gradient(in oklab, hsl(96, 42%, 24%) 0%, hsl(302, 67%, 25%) 100%);
139
141
  }
140
142
 
141
143
  /* becomes */
142
144
 
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%);
145
+ .example {
146
+ 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%);
147
+ background-image: linear-gradient(in oklch, hsl(0deg 85% 75%) 0%, hsl(180deg 80% 65%) 100%);
146
148
  }
147
149
 
148
150
  :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%);
151
+ --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%);
152
+ --background-image: linear-gradient(in oklab, hsl(96, 42%, 24%) 0%, hsl(302, 67%, 25%) 100%);
151
153
  }
152
154
  ```
153
155
 
154
156
  _Custom properties do not fallback to the previous declaration_
155
157
 
156
158
  [cli-url]: https://github.com/csstools/postcss-plugins/actions/workflows/test.yml?query=workflow/test
157
- [css-url]: https://cssdb.org/#TODO
159
+ [css-url]: https://cssdb.org/#gradients-interpolation-method
158
160
  [discord]: https://discord.gg/bUadyRwkJS
159
161
  [npm-url]: https://www.npmjs.com/package/@csstools/postcss-gradients-interpolation-method
160
162
 
@@ -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, wideGamut?: boolean): 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,r=!1){const a=[],l=[];for(let r=0;r<e.length-1;r++){const a=e[r],c=e[r+1];if(l.push(a),n.serializeP3(a.colorData,!1).toString()!==n.serializeP3(c.colorData,!1).toString()&&a.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]]),a.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;l.push({colorData:d})}r===e.length-2&&l.push(c)}for(let e=0;e<l.length;e++)r&&!n.colorDataFitsRGB_Gamut(l[e].colorData)?l[e].color=n.serializeP3(l[e].colorData,!1):l[e].color=n.serializeRGB(l[e].colorData,!1);for(let e=0;e<l.length;e++){const n=l[e];n.position?a.push(n.color,new o.WhitespaceNode([[t.TokenType.Whitespace," ",-1,-1,void 0]]),n.position):a.push(n.color),e!==l.length-1&&a.push(new o.TokenNode([t.TokenType.Comma,",",-1,-1,void 0]),new o.WhitespaceNode([[t.TokenType.Whitespace," ",-1,-1,void 0]]))}return a}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){if(s.color)return!1;s.color=a,s.colorData=l}else{if(!s.color)return!1;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,n=!1){const i=e.getName();if(!s.test(i))return!1;let u="srgb",d=null,T=null,f=null,h=null,v=null,m=[];{let n=0,i=e.value[n];for(;!o.isTokenNode(i)||i.value[0]!==t.TokenType.Ident||!c.test(i.value[4].value);){if(o.isTokenNode(i)&&i.value[0]===t.TokenType.Comma)return!1;n++,i=e.value[n]}for(d=i,n++,i=e.value[n];o.isCommentNode(i)||o.isWhitespaceNode(i);)n++,i=e.value[n];if(o.isTokenNode(i)&&i.value[0]===t.TokenType.Ident&&r.test(i.value[4].value)){if(T)return!1;T=i,u=i.value[4].value,n++,i=e.value[n]}for(;o.isCommentNode(i)||o.isWhitespaceNode(i);)n++,i=e.value[n];if(o.isTokenNode(i)&&i.value[0]===t.TokenType.Ident&&l.test(i.value[4].value)&&a.test(u)){if(f||!T)return!1;f=i,n++,i=e.value[n]}for(;o.isCommentNode(i)||o.isWhitespaceNode(i);)n++,i=e.value[n];if(o.isTokenNode(i)&&i.value[0]===t.TokenType.Ident&&p.test(i.value[4].value)){if(h||!T||!f)return!1;h=i,n++,i=e.value[n]}for(;!o.isTokenNode(i)||i.value[0]!==t.TokenType.Comma;)n++,i=e.value[n];v=i,m=e.value.slice(n+1)}if(!T)return!1;if(f&&!h)return!1;if(h&&!f)return!1;const k=parseColorStops(m);if(!k)return!1;const g=interpolateColorsInColorStopsList(k,T,f,n);if(!g)return!1;const N=trim([...e.value.slice(0,e.value.indexOf(d)),...e.value.slice(e.value.indexOf(h||T)+1,e.value.indexOf(v))]);return N.length>0&&N.some((e=>!o.isCommentNode(e)))&&N.push(new o.TokenNode([t.TokenType.Comma,",",-1,-1,void 0]),new o.WhitespaceNode([[t.TokenType.Whitespace," ",-1,-1,void 0]])),trim([...N,...trim(g)])}function trim(e){let t=0,n=e.length-1;for(let n=0;n<e.length;n++)if(!o.isWhitespaceNode(e[n])){t=n;break}for(let t=e.length-1;t>=0;t--)if(!o.isWhitespaceNode(e[t])){n=t;break}return e.slice(t,n+1)}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=t.tokenize({css:n.value}),r=o.stringify(o.replaceComponentValues(o.parseCommaSeparatedListOfComponentValues(s),(e=>{if(!o.isFunctionNode(e))return;const t=modifyGradientFunctionComponentValues(e);t&&(e.value=t)})));if(r===n.value)return;const a=o.stringify(o.replaceComponentValues(o.parseCommaSeparatedListOfComponentValues(t.cloneTokens(s)),(e=>{if(!o.isFunctionNode(e))return;const t=modifyGradientFunctionComponentValues(e,!0);t&&(e.value=t)})));n.cloneBefore({value:r}),r!=a&&n.cloneBefore({value:a}),null!=e&&e.preserve||n.remove()}});basePlugin.postcss=!0;const postcssPlugin=o=>{const t=Object.assign({enableProgressiveCustomProperties:!0,preserve:!0},o);return t.enableProgressiveCustomProperties?{postcssPlugin:"postcss-gradients-interpolation-method",plugins:[e(),basePlugin(t)]}:basePlugin(t)};postcssPlugin.postcss=!0,module.exports=postcssPlugin;
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 i,isTokenNode as s,stringify as l,replaceComponentValues as a,parseCommaSeparatedListOfComponentValues as c,isFunctionNode as u}from"@csstools/css-parser-algorithms";import{TokenType as p,tokenize as f,cloneTokens as v}from"@csstools/css-tokenizer";import{serializeP3 as h,color as d,colorDataFitsRGB_Gamut as m,serializeRGB as g}from"@csstools/css-color-parser";const w=/(repeating-)?(linear|radial|conic)-gradient\(/i,C=/^(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&&w.test(e.params))return!0;e=e.parent}else e=e.parent;return!1}function interpolateColorsInColorStopsList(o,n,i,s=!1){const l=[],a=[];for(let s=0;s<o.length-1;s++){const l=o[s],c=o[s+1];if(a.push(l),h(l.colorData,!1).toString()!==h(c.colorData,!1).toString()&&l.position.toString()!==c.position.toString())for(let o=1;o<=9;o++){const s=10*o;let u=[];i&&(u=[new e([[p.Whitespace," ",-1,-1,void 0]]),i,new e([[p.Whitespace," ",-1,-1,void 0]]),new t([p.Ident,"hue",-1,-1,{value:"hue"}])]);const f=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]]),l.color,new e([[p.Whitespace," ",-1,-1,void 0]]),new t([p.Percentage,100-s+"%",-1,-1,{value:100-s}]),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,`${s}%`,-1,-1,{value:s}])]),v=d(f);if(!v)return!1;a.push({colorData:v})}s===o.length-2&&a.push(c)}for(let o=0;o<a.length;o++)s&&!m(a[o].colorData)?a[o].color=h(a[o].colorData,!1):a[o].color=g(a[o].colorData,!1);for(let o=0;o<a.length;o++){const r=a[o];r.position?l.push(r.color,new e([[p.Whitespace," ",-1,-1,void 0]]),r.position):l.push(r.color),o!==a.length-1&&l.push(new t([p.Comma,",",-1,-1,void 0]),new e([[p.Whitespace," ",-1,-1,void 0]]))}return l}function parseColorStops(o){const e=[];let t={};for(let r=0;r<o.length;r++){const l=o[r];if(n(l)||i(l))continue;if(s(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=d(l);if(a){if(t.color)return!1;t.color=l,t.colorData=a}else{if(!t.color)return!1;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 D=/^(srgb|srgb-linear|lab|oklab|xyz|xyz-d50|xyz-d65|hsl|hwb|lch|oklch)$/i,b=/^(hsl|hwb|lch|oklch)$/i,A=/^(shorter|longer|increasing|decreasing)$/i,x=/^in$/i,S=/^hue$/i;function modifyGradientFunctionComponentValues(o,r=!1){const l=o.getName();if(!C.test(l))return!1;let a="srgb",c=null,u=null,f=null,v=null,h=null,d=[];{let e=0,t=o.value[e];for(;!s(t)||t.value[0]!==p.Ident||!x.test(t.value[4].value);){if(s(t)&&t.value[0]===p.Comma)return!1;e++,t=o.value[e]}for(c=t,e++,t=o.value[e];n(t)||i(t);)e++,t=o.value[e];if(s(t)&&t.value[0]===p.Ident&&D.test(t.value[4].value)){if(u)return!1;u=t,a=t.value[4].value,e++,t=o.value[e]}for(;n(t)||i(t);)e++,t=o.value[e];if(s(t)&&t.value[0]===p.Ident&&A.test(t.value[4].value)&&b.test(a)){if(f||!u)return!1;f=t,e++,t=o.value[e]}for(;n(t)||i(t);)e++,t=o.value[e];if(s(t)&&t.value[0]===p.Ident&&S.test(t.value[4].value)){if(v||!u||!f)return!1;v=t,e++,t=o.value[e]}for(;!s(t)||t.value[0]!==p.Comma;)e++,t=o.value[e];h=t,d=o.value.slice(e+1)}if(!u)return!1;if(f&&!v)return!1;if(v&&!f)return!1;const m=parseColorStops(d);if(!m)return!1;const g=interpolateColorsInColorStopsList(m,u,f,r);if(!g)return!1;const w=trim([...o.value.slice(0,o.value.indexOf(c)),...o.value.slice(o.value.indexOf(v||u)+1,o.value.indexOf(h))]);return w.length>0&&w.some((o=>!n(o)))&&w.push(new t([p.Comma,",",-1,-1,void 0]),new e([[p.Whitespace," ",-1,-1,void 0]])),trim([...w,...trim(g)])}function trim(o){let e=0,t=o.length-1;for(let t=0;t<o.length;t++)if(!i(o[t])){e=t;break}for(let e=o.length-1;e>=0;e--)if(!i(o[e])){t=e;break}return o.slice(e,t+1)}const basePlugin=o=>({postcssPlugin:"postcss-gradients-interpolation-method",Declaration(e){if(!w.test(e.value))return;if(hasFallback(e))return;if(hasSupportsAtRuleAncestor(e))return;const t=f({css:e.value}),r=l(a(c(t),(o=>{if(!u(o))return;const e=modifyGradientFunctionComponentValues(o);e&&(o.value=e)})));if(r===e.value)return;const n=l(a(c(v(t)),(o=>{if(!u(o))return;const e=modifyGradientFunctionComponentValues(o,!0);e&&(o.value=e)})));e.cloneBefore({value:r}),r!=n&&e.cloneBefore({value:n}),null!=o&&o.preserve||e.remove()}});basePlugin.postcss=!0;const postcssPlugin=e=>{const t=Object.assign({enableProgressiveCustomProperties:!0,preserve:!0},e);return t.enableProgressiveCustomProperties?{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, wideGamut?: boolean): 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.1",
4
+ "version": "3.0.1",
5
5
  "author": "Jonathan Neal <jonathantneal@hotmail.com>",
6
6
  "license": "CC0-1.0",
7
7
  "funding": {
@@ -29,23 +29,23 @@
29
29
  "dist"
30
30
  ],
31
31
  "dependencies": {
32
- "@csstools/postcss-progressive-custom-properties": "^2.0.0",
33
- "postcss-value-parser": "^4.2.0"
32
+ "@csstools/css-color-parser": "^1.1.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"
34
36
  },
35
37
  "peerDependencies": {
36
38
  "postcss": "^8.4"
37
39
  },
40
+ "devDependencies": {
41
+ "@csstools/postcss-tape": "*"
42
+ },
38
43
  "scripts": {
39
- "prebuild": "npm run clean",
40
44
  "build": "rollup -c ../../rollup/default.mjs",
41
- "clean": "node -e \"fs.rmSync('./dist', { recursive: true, force: true }); fs.mkdirSync('./dist');\"",
42
45
  "docs": "node ../../.github/bin/generate-docs/install.mjs && node ../../.github/bin/generate-docs/readme.mjs",
43
- "lint": "npm run lint:eslint && npm run lint:package-json",
44
- "lint:eslint": "eslint ./src --ext .js --ext .ts --ext .mjs --no-error-on-unmatched-pattern",
45
- "lint:package-json": "node ../../.github/bin/format-package-json.mjs",
46
- "prepublishOnly": "npm run clean && npm run build && npm run test",
47
- "test": "node .tape.mjs && npm run test:exports",
48
- "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",
49
49
  "test:rewrite-expects": "REWRITE_EXPECTS=true node .tape.mjs"
50
50
  },
51
51
  "homepage": "https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-gradients-interpolation-method#readme",
@@ -69,7 +69,7 @@
69
69
  "syntax"
70
70
  ],
71
71
  "csstools": {
72
- "cssdbId": "TODO",
72
+ "cssdbId": "gradients-interpolation-method",
73
73
  "exportName": "postcssGradientsInterpolationMethod",
74
74
  "humanReadableName": "PostCSS Gradients Interpolation Method",
75
75
  "specUrl": "https://drafts.csswg.org/css-images-4/#linear-gradients"