Skip to content

Commit 71acf0f

Browse files
authored
css-color-parser : fix interpolation of longer hue (#1138)
1 parent f15642b commit 71acf0f

File tree

8 files changed

+76
-45
lines changed

8 files changed

+76
-45
lines changed

packages/css-color-parser/CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changes to CSS Color Parser
22

3+
### Unreleased (patch)
4+
5+
- Fix interpolation of `hue` when either or both components are `none` and `longer` is the interpolation method.
6+
37
### 1.3.2
48

59
_September 24, 2023_

packages/css-color-parser/dist/index.cjs

+1-1
Large diffs are not rendered by default.

packages/css-color-parser/dist/index.mjs

+1-1
Large diffs are not rendered by default.

packages/css-color-parser/src/functions/color-mix.ts

+58-35
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,15 @@ function colorMixRectangular(colorSpace: string, colors: ColorMixColors | false)
320320
a_channels = colorDataTo(a_color, outputColorNotation).channels;
321321
b_channels = colorDataTo(b_color, outputColorNotation).channels;
322322

323+
a_channels[0] = fillInMissingComponent(a_channels[0], b_channels[0]);
324+
b_channels[0] = fillInMissingComponent(b_channels[0], a_channels[0]);
325+
326+
a_channels[1] = fillInMissingComponent(a_channels[1], b_channels[1]);
327+
b_channels[1] = fillInMissingComponent(b_channels[1], a_channels[1]);
328+
329+
a_channels[2] = fillInMissingComponent(a_channels[2], b_channels[2]);
330+
b_channels[2] = fillInMissingComponent(b_channels[2], a_channels[2]);
331+
323332
a_channels[0] = premultiply(a_channels[0], a_alpha);
324333
a_channels[1] = premultiply(a_channels[1], a_alpha);
325334
a_channels[2] = premultiply(a_channels[2], a_alpha);
@@ -431,43 +440,57 @@ function colorMixPolar(colorSpace: string, hueInterpolationMethod: string, color
431440
break;
432441
}
433442

434-
if (!Number.isNaN(a_hue) && !Number.isNaN(b_hue)) {
435-
const angleDiff = b_hue - a_hue;
443+
a_hue = fillInMissingComponent(a_hue, b_hue);
444+
if (Number.isNaN(a_hue)) {
445+
a_hue = 0;
446+
}
436447

437-
switch (hueInterpolationMethod) {
438-
case 'shorter':
439-
if (angleDiff > 180) {
440-
a_hue += 360;
441-
} else if (angleDiff < -180) {
442-
b_hue += 360;
443-
}
448+
b_hue = fillInMissingComponent(b_hue, a_hue);
449+
if (Number.isNaN(b_hue)) {
450+
b_hue = 0;
451+
}
444452

445-
break;
446-
case 'longer':
447-
if (-180 < angleDiff && angleDiff < 180) {
448-
if (angleDiff > 0) {
449-
a_hue += 360;
450-
} else {
451-
b_hue += 360;
452-
}
453-
}
453+
a_first = fillInMissingComponent(a_first, b_first);
454+
b_first = fillInMissingComponent(b_first, a_first);
454455

455-
break;
456-
case 'increasing':
457-
if (angleDiff < 0) {
458-
b_hue += 360;
459-
}
456+
a_second = fillInMissingComponent(a_second, b_second);
457+
b_second = fillInMissingComponent(b_second, a_second);
458+
459+
const angleDiff = b_hue - a_hue;
460460

461-
break;
462-
case 'decreasing':
461+
switch (hueInterpolationMethod) {
462+
case 'shorter':
463+
if (angleDiff > 180) {
464+
a_hue += 360;
465+
} else if (angleDiff < -180) {
466+
b_hue += 360;
467+
}
468+
469+
break;
470+
case 'longer':
471+
if (-180 < angleDiff && angleDiff < 180) {
463472
if (angleDiff > 0) {
464473
a_hue += 360;
474+
} else {
475+
b_hue += 360;
465476
}
477+
}
466478

467-
break;
468-
default:
469-
throw new Error('Unknown hue interpolation method');
470-
}
479+
break;
480+
case 'increasing':
481+
if (angleDiff < 0) {
482+
b_hue += 360;
483+
}
484+
485+
break;
486+
case 'decreasing':
487+
if (angleDiff > 0) {
488+
a_hue += 360;
489+
}
490+
491+
break;
492+
default:
493+
throw new Error('Unknown hue interpolation method');
471494
}
472495

473496
a_first = premultiply(a_first, a_alpha);
@@ -511,15 +534,15 @@ function colorMixPolar(colorSpace: string, hueInterpolationMethod: string, color
511534
return colorData;
512535
}
513536

514-
function interpolate(start: number, end: number, p: number): number {
515-
if (Number.isNaN(start)) {
516-
return end;
537+
function fillInMissingComponent(a: number, b: number): number {
538+
if (Number.isNaN(a)) {
539+
return b;
517540
}
518541

519-
if (Number.isNaN(end)) {
520-
return start;
521-
}
542+
return a;
543+
}
522544

545+
function interpolate(start: number, end: number, p: number): number {
523546
return (start * p) + end * (1 - p);
524547
}
525548

packages/css-color-parser/test/basic/color-mix-function.mjs

+6-2
Original file line numberDiff line numberDiff line change
@@ -70,14 +70,18 @@ const tests = [
7070
['color-mix(in oklab, #09232c, white 50%)', 'rgb(123, 137, 142)'],
7171

7272
['color-mix(in hsl longer hue, hsl(90deg 50% 50%), hsl(0deg 50% 50%)', canonicalize('hsl(225deg 50% 50%)')],
73-
['color-mix(in hsl longer hue, hsl(90deg 50% 50%), hsl(none 50% 50%)', canonicalize('hsl(90deg 50% 50%)')],
73+
['color-mix(in hsl longer hue, hsl(90deg 50% 50%), hsl(none 50% 50%)', canonicalize('hsl(270deg 50% 50%)')],
7474
['color-mix(in hsl shorter hue, hsl(90deg 50% 50%), hsl(none 50% 50%)', canonicalize('hsl(90deg 50% 50%)')],
7575
['color-mix(in hsl increasing hue, hsl(90deg 50% 50%), hsl(none 50% 50%)', canonicalize('hsl(90deg 50% 50%)')],
7676
['color-mix(in hsl decreasing hue, hsl(90deg 50% 50%), hsl(none 50% 50%)', canonicalize('hsl(90deg 50% 50%)')],
77-
['color-mix(in hsl longer hue, hsl(none 50% 50%), hsl(90deg 50% 50%)', canonicalize('hsl(90deg 50% 50%)')],
77+
['color-mix(in hsl longer hue, hsl(none 50% 50%), hsl(90deg 50% 50%)', canonicalize('hsl(270deg 50% 50%)')],
7878
['color-mix(in hsl shorter hue, hsl(none 50% 50%), hsl(90deg 50% 50%)', canonicalize('hsl(90deg 50% 50%)')],
7979
['color-mix(in hsl increasing hue, hsl(none 50% 50%), hsl(90deg 50% 50%)', canonicalize('hsl(90deg 50% 50%)')],
8080
['color-mix(in hsl decreasing hue, hsl(none 50% 50%), hsl(90deg 50% 50%)', canonicalize('hsl(90deg 50% 50%)')],
81+
['color-mix(in hsl longer hue, hsl(none 50% 50%), hsl(none 50% 50%)', canonicalize('hsl(180deg 50% 50%)')],
82+
['color-mix(in hsl shorter hue, hsl(none 50% 50%), hsl(none 50% 50%)', canonicalize('hsl(0deg 50% 50%)')],
83+
['color-mix(in hsl increasing hue, hsl(none 50% 50%), hsl(none 50% 50%)', canonicalize('hsl(0deg 50% 50%)')],
84+
['color-mix(in hsl decreasing hue, hsl(none 50% 50%), hsl(none 50% 50%)', canonicalize('hsl(0deg 50% 50%)')],
8185
];
8286

8387
for (const test of tests) {

plugins/postcss-gradients-interpolation-method/test/basic.expect.css

+2-2
Original file line numberDiff line numberDiff line change
@@ -350,9 +350,9 @@
350350

351351
.patterns-2 {
352352
background:
353-
conic-gradient(at 20px calc(100% - 20px), rgba(0, 0, 0, 0) 270deg, rgba(192, 41, 66, 0.1), rgba(192, 41, 66, 0.2), rgba(192, 41, 66, 0.3), rgba(192, 41, 66, 0.4), rgba(192, 41, 66, 0.5), rgba(192, 41, 66, 0.6), rgba(192, 41, 66, 0.7), rgba(192, 41, 66, 0.8), rgba(192, 41, 66, 0.9), rgb(192, 41, 66) 0) calc(20px + 15px) 0,
353+
conic-gradient(at 20px calc(100% - 20px), rgba(0, 0, 0, 0) 270deg, rgba(192, 107, 41, 0.1), rgba(187, 192, 41, 0.2), rgba(96, 192, 41, 0.3), rgba(41, 192, 76, 0.4), rgba(41, 192, 167, 0.5), rgba(41, 126, 192, 0.6), rgba(46, 41, 192, 0.7), rgba(137, 41, 192, 0.8), rgba(192, 41, 157, 0.9), rgb(192, 41, 66) 0) calc(20px + 15px) 0,
354354
linear-gradient(#53777A 20px, #0000 0) 0 15px,
355-
conic-gradient(at 20px calc(100% - 20px), rgba(0, 0, 0, 0) 90deg, rgba(83, 119, 122, 0.1), rgba(83, 119, 122, 0.2), rgba(83, 119, 122, 0.3), rgba(83, 119, 122, 0.4), rgba(83, 119, 122, 0.5), rgba(83, 119, 122, 0.6), rgba(83, 119, 122, 0.7), rgba(83, 119, 122, 0.8), rgba(83, 119, 122, 0.9), rgb(83, 119, 122) 0, rgb(83, 96, 122), rgb(94, 83, 122), rgb(117, 83, 122), rgb(122, 83, 103), rgb(122, 86, 83), rgb(122, 109, 83), rgb(111, 122, 83), rgb(88, 122, 83), rgb(83, 122, 102), rgb(83, 119, 122) 180deg, rgb(79, 128, 116), rgb(76, 135, 101), rgb(72, 142, 79), rgb(86, 148, 68), rgb(114, 155, 64), rgb(149, 162, 59), rgb(170, 148, 55), rgb(177, 112, 50), rgb(184, 68, 46), rgb(192, 41, 66) 0),
355+
conic-gradient(at 20px calc(100% - 20px), rgba(0, 0, 0, 0) 90deg, rgba(83, 96, 122, 0.1), rgba(94, 83, 122, 0.2), rgba(117, 83, 122, 0.3), rgba(122, 83, 103, 0.4), rgba(122, 86, 83, 0.5), rgba(122, 109, 83, 0.6), rgba(111, 122, 83, 0.7), rgba(88, 122, 83, 0.8), rgba(83, 122, 102, 0.9), rgb(83, 119, 122) 0, rgb(83, 96, 122), rgb(94, 83, 122), rgb(117, 83, 122), rgb(122, 83, 103), rgb(122, 86, 83), rgb(122, 109, 83), rgb(111, 122, 83), rgb(88, 122, 83), rgb(83, 122, 102), rgb(83, 119, 122) 180deg, rgb(79, 128, 116), rgb(76, 135, 101), rgb(72, 142, 79), rgb(86, 148, 68), rgb(114, 155, 64), rgb(149, 162, 59), rgb(170, 148, 55), rgb(177, 112, 50), rgb(184, 68, 46), rgb(192, 41, 66) 0),
356356
#ECD078;
357357
background:
358358
conic-gradient(in hsl longer hue at 20px calc(100% - 20px), #0000 270deg, #C02942 0) calc(20px + 15px) 0,

plugins/postcss-gradients-interpolation-method/test/basic.preserve-false.expect.css

+2-2
Original file line numberDiff line numberDiff line change
@@ -283,9 +283,9 @@
283283

284284
.patterns-2 {
285285
background:
286-
conic-gradient(at 20px calc(100% - 20px), rgba(0, 0, 0, 0) 270deg, rgba(192, 41, 66, 0.1), rgba(192, 41, 66, 0.2), rgba(192, 41, 66, 0.3), rgba(192, 41, 66, 0.4), rgba(192, 41, 66, 0.5), rgba(192, 41, 66, 0.6), rgba(192, 41, 66, 0.7), rgba(192, 41, 66, 0.8), rgba(192, 41, 66, 0.9), rgb(192, 41, 66) 0) calc(20px + 15px) 0,
286+
conic-gradient(at 20px calc(100% - 20px), rgba(0, 0, 0, 0) 270deg, rgba(192, 107, 41, 0.1), rgba(187, 192, 41, 0.2), rgba(96, 192, 41, 0.3), rgba(41, 192, 76, 0.4), rgba(41, 192, 167, 0.5), rgba(41, 126, 192, 0.6), rgba(46, 41, 192, 0.7), rgba(137, 41, 192, 0.8), rgba(192, 41, 157, 0.9), rgb(192, 41, 66) 0) calc(20px + 15px) 0,
287287
linear-gradient(#53777A 20px, #0000 0) 0 15px,
288-
conic-gradient(at 20px calc(100% - 20px), rgba(0, 0, 0, 0) 90deg, rgba(83, 119, 122, 0.1), rgba(83, 119, 122, 0.2), rgba(83, 119, 122, 0.3), rgba(83, 119, 122, 0.4), rgba(83, 119, 122, 0.5), rgba(83, 119, 122, 0.6), rgba(83, 119, 122, 0.7), rgba(83, 119, 122, 0.8), rgba(83, 119, 122, 0.9), rgb(83, 119, 122) 0, rgb(83, 96, 122), rgb(94, 83, 122), rgb(117, 83, 122), rgb(122, 83, 103), rgb(122, 86, 83), rgb(122, 109, 83), rgb(111, 122, 83), rgb(88, 122, 83), rgb(83, 122, 102), rgb(83, 119, 122) 180deg, rgb(79, 128, 116), rgb(76, 135, 101), rgb(72, 142, 79), rgb(86, 148, 68), rgb(114, 155, 64), rgb(149, 162, 59), rgb(170, 148, 55), rgb(177, 112, 50), rgb(184, 68, 46), rgb(192, 41, 66) 0),
288+
conic-gradient(at 20px calc(100% - 20px), rgba(0, 0, 0, 0) 90deg, rgba(83, 96, 122, 0.1), rgba(94, 83, 122, 0.2), rgba(117, 83, 122, 0.3), rgba(122, 83, 103, 0.4), rgba(122, 86, 83, 0.5), rgba(122, 109, 83, 0.6), rgba(111, 122, 83, 0.7), rgba(88, 122, 83, 0.8), rgba(83, 122, 102, 0.9), rgb(83, 119, 122) 0, rgb(83, 96, 122), rgb(94, 83, 122), rgb(117, 83, 122), rgb(122, 83, 103), rgb(122, 86, 83), rgb(122, 109, 83), rgb(111, 122, 83), rgb(88, 122, 83), rgb(83, 122, 102), rgb(83, 119, 122) 180deg, rgb(79, 128, 116), rgb(76, 135, 101), rgb(72, 142, 79), rgb(86, 148, 68), rgb(114, 155, 64), rgb(149, 162, 59), rgb(170, 148, 55), rgb(177, 112, 50), rgb(184, 68, 46), rgb(192, 41, 66) 0),
289289
#ECD078;
290290
}
291291

plugins/postcss-gradients-interpolation-method/test/basic.with-cloned-rules.expect.css

+2-2
Original file line numberDiff line numberDiff line change
@@ -350,9 +350,9 @@
350350

351351
.patterns-2 {
352352
background:
353-
conic-gradient(at 20px calc(100% - 20px), rgba(0, 0, 0, 0) 270deg, rgba(192, 41, 66, 0.1), rgba(192, 41, 66, 0.2), rgba(192, 41, 66, 0.3), rgba(192, 41, 66, 0.4), rgba(192, 41, 66, 0.5), rgba(192, 41, 66, 0.6), rgba(192, 41, 66, 0.7), rgba(192, 41, 66, 0.8), rgba(192, 41, 66, 0.9), rgb(192, 41, 66) 0) calc(20px + 15px) 0,
353+
conic-gradient(at 20px calc(100% - 20px), rgba(0, 0, 0, 0) 270deg, rgba(192, 107, 41, 0.1), rgba(187, 192, 41, 0.2), rgba(96, 192, 41, 0.3), rgba(41, 192, 76, 0.4), rgba(41, 192, 167, 0.5), rgba(41, 126, 192, 0.6), rgba(46, 41, 192, 0.7), rgba(137, 41, 192, 0.8), rgba(192, 41, 157, 0.9), rgb(192, 41, 66) 0) calc(20px + 15px) 0,
354354
linear-gradient(#53777A 20px, #0000 0) 0 15px,
355-
conic-gradient(at 20px calc(100% - 20px), rgba(0, 0, 0, 0) 90deg, rgba(83, 119, 122, 0.1), rgba(83, 119, 122, 0.2), rgba(83, 119, 122, 0.3), rgba(83, 119, 122, 0.4), rgba(83, 119, 122, 0.5), rgba(83, 119, 122, 0.6), rgba(83, 119, 122, 0.7), rgba(83, 119, 122, 0.8), rgba(83, 119, 122, 0.9), rgb(83, 119, 122) 0, rgb(83, 96, 122), rgb(94, 83, 122), rgb(117, 83, 122), rgb(122, 83, 103), rgb(122, 86, 83), rgb(122, 109, 83), rgb(111, 122, 83), rgb(88, 122, 83), rgb(83, 122, 102), rgb(83, 119, 122) 180deg, rgb(79, 128, 116), rgb(76, 135, 101), rgb(72, 142, 79), rgb(86, 148, 68), rgb(114, 155, 64), rgb(149, 162, 59), rgb(170, 148, 55), rgb(177, 112, 50), rgb(184, 68, 46), rgb(192, 41, 66) 0),
355+
conic-gradient(at 20px calc(100% - 20px), rgba(0, 0, 0, 0) 90deg, rgba(83, 96, 122, 0.1), rgba(94, 83, 122, 0.2), rgba(117, 83, 122, 0.3), rgba(122, 83, 103, 0.4), rgba(122, 86, 83, 0.5), rgba(122, 109, 83, 0.6), rgba(111, 122, 83, 0.7), rgba(88, 122, 83, 0.8), rgba(83, 122, 102, 0.9), rgb(83, 119, 122) 0, rgb(83, 96, 122), rgb(94, 83, 122), rgb(117, 83, 122), rgb(122, 83, 103), rgb(122, 86, 83), rgb(122, 109, 83), rgb(111, 122, 83), rgb(88, 122, 83), rgb(83, 122, 102), rgb(83, 119, 122) 180deg, rgb(79, 128, 116), rgb(76, 135, 101), rgb(72, 142, 79), rgb(86, 148, 68), rgb(114, 155, 64), rgb(149, 162, 59), rgb(170, 148, 55), rgb(177, 112, 50), rgb(184, 68, 46), rgb(192, 41, 66) 0),
356356
#ECD078;
357357
background:
358358
conic-gradient(in hsl longer hue at 20px calc(100% - 20px), #0000 270deg, #C02942 0) calc(20px + 15px) 0,

0 commit comments

Comments
 (0)