Skip to content

Commit 131f669

Browse files
authored
split color conversion for RCS from interpolation (#1860)
1 parent 0526181 commit 131f669

12 files changed

Lines changed: 450 additions & 304 deletions

File tree

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

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

packages/css-color-parser/src/color-data.ts

Lines changed: 308 additions & 195 deletions
Large diffs are not rendered by default.

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

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import type { Color } from '@csstools/color-helpers';
55
import { ColorNotation } from '../color-notation';
66
import { isTokenIdent, isTokenNumeric, isTokenPercentage } from '@csstools/css-tokenizer';
77
import { calcFromComponentValues } from '@csstools/css-calc';
8-
import { colorDataTo, SyntaxFlag } from '../color-data';
8+
import { colorDataToForInterpolation, SyntaxFlag } from '../color-data';
99
import { isFunctionNode, isTokenNode, isWhiteSpaceOrCommentNode } from '@csstools/css-parser-algorithms';
1010
import { toLowerCaseAZ } from '../util/to-lower-case-a-z';
1111
import { mathFunctionNames } from '@csstools/css-calc';
@@ -279,7 +279,7 @@ function colorMixRectangular(colorSpace: string, items: Array<ColorMixEntry> | f
279279

280280
// 3. If items is length 1, set color to the color of that sole item, converted to the specified interpolation <color-space>.
281281
if (normalizedItems.length === 1) {
282-
const color = colorDataTo(normalizedItems[0].color, outputColorNotation);
282+
const color = colorDataToForInterpolation(normalizedItems[0].color, outputColorNotation);
283283
color.colorNotation = outputColorNotation;
284284
color.syntaxFlags.add(SyntaxFlag.ColorMixVariadic);
285285
color.syntaxFlags.add(SyntaxFlag.ColorMix);
@@ -369,8 +369,8 @@ function colorMixRectangularPair(colorNotation: ColorNotation, a_color: ColorDat
369369
a_alpha = Number.isNaN(a_alpha) ? b_alpha : a_alpha;
370370
b_alpha = Number.isNaN(b_alpha) ? a_alpha : b_alpha;
371371

372-
const a_channels = colorDataTo(a_color, colorNotation).channels;
373-
const b_channels = colorDataTo(b_color, colorNotation).channels;
372+
const a_channels = colorDataToForInterpolation(a_color, colorNotation).channels;
373+
const b_channels = colorDataToForInterpolation(b_color, colorNotation).channels;
374374

375375
a_channels[0] = fillInMissingComponent(a_channels[0], b_channels[0]);
376376
b_channels[0] = fillInMissingComponent(b_channels[0], a_channels[0]);
@@ -448,7 +448,7 @@ function colorMixPolar(colorSpace: string, hueInterpolationMethod: string, items
448448

449449
// 3. If items is length 1, set color to the color of that sole item, converted to the specified interpolation <color-space>.
450450
if (normalizedItems.length === 1) {
451-
const color = colorDataTo(normalizedItems[0].color, outputColorNotation);
451+
const color = colorDataToForInterpolation(normalizedItems[0].color, outputColorNotation);
452452
color.colorNotation = outputColorNotation;
453453
color.syntaxFlags.add(SyntaxFlag.ColorMixVariadic);
454454
color.syntaxFlags.add(SyntaxFlag.ColorMix);
@@ -547,8 +547,8 @@ function colorMixPolarPair(colorNotation: ColorNotation, hueInterpolationMethod:
547547
a_alpha = Number.isNaN(a_alpha) ? b_alpha : a_alpha;
548548
b_alpha = Number.isNaN(b_alpha) ? a_alpha : b_alpha;
549549

550-
const a_channels = colorDataTo(a_color, colorNotation).channels;
551-
const b_channels = colorDataTo(b_color, colorNotation).channels;
550+
const a_channels = colorDataToForInterpolation(a_color, colorNotation).channels;
551+
const b_channels = colorDataToForInterpolation(b_color, colorNotation).channels;
552552

553553
switch (colorNotation) {
554554
case ColorNotation.HSL:

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { ColorData} from '../color-data';
2-
import { SyntaxFlag, colorDataTo, noneToZeroInRelativeColorDataChannels, normalizeRelativeColorDataChannels } from '../color-data';
2+
import { SyntaxFlag, colorDataToForRelativeColorSyntax, noneToZeroInRelativeColorDataChannels, normalizeRelativeColorDataChannels } from '../color-data';
33
import type { ColorParser } from '../color-parser';
44
import { ColorNotation } from '../color-notation';
55
import type { ComponentValue, FunctionNode} from '@csstools/css-parser-algorithms';
@@ -106,7 +106,7 @@ export function color(colorFunctionNode: FunctionNode, colorParser: ColorParser)
106106
colorData.colorNotation = colorSpaceNameToColorNotation(colorSpace);
107107

108108
if (relativeToColor) {
109-
relativeToColor = colorDataTo(relativeToColor, colorData.colorNotation);
109+
relativeToColor = colorDataToForRelativeColorSyntax(relativeToColor, colorData.colorNotation);
110110
relativeColorChannels = normalizeRelativeColorDataChannels(relativeToColor);
111111
relativeColorChannelsWithoutNone = noneToZeroInRelativeColorDataChannels(relativeColorChannels);
112112
}

packages/css-color-parser/src/functions/three-channel-space-separated.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import type { ColorData} from '../color-data';
2-
import { noneToZeroInRelativeColorDataChannels, normalizeRelativeColorDataChannels } from '../color-data';
2+
import { colorDataToForRelativeColorSyntax, noneToZeroInRelativeColorDataChannels, normalizeRelativeColorDataChannels } from '../color-data';
33
import type { ComponentValue, FunctionNode } from '@csstools/css-parser-algorithms';
44
import type { TokenNumber} from '@csstools/css-tokenizer';
55
import { isTokenDelim, isTokenIdent, isTokenNumber, isTokenNumeric } from '@csstools/css-tokenizer';
66
import type { ColorNotation } from '../color-notation';
7-
import { SyntaxFlag, colorDataTo } from '../color-data';
7+
import { SyntaxFlag } from '../color-data';
88
import { calcFromComponentValues } from '@csstools/css-calc';
99
import { isCommentNode, isFunctionNode, isTokenNode, isWhitespaceNode } from '@csstools/css-parser-algorithms';
1010
import type { normalizeChannelValuesFn } from './normalize-channel-values';
@@ -122,7 +122,7 @@ export function threeChannelSpaceSeparated(
122122

123123
colorData.syntaxFlags.add(SyntaxFlag.RelativeColorSyntax);
124124

125-
relativeToColor = colorDataTo(relativeToColor, colorNotation);
125+
relativeToColor = colorDataToForRelativeColorSyntax(relativeToColor, colorNotation);
126126
relativeColorChannels = normalizeRelativeColorDataChannels(relativeToColor);
127127
relativeColorChannelsWithoutNone = noneToZeroInRelativeColorDataChannels(relativeColorChannels);
128128

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ assert.deepStrictEqual(
259259
color(parse('lch(from hwb(50deg none none) l c h)')),
260260
{
261261
colorNotation: 'lch',
262-
channels: [NaN, NaN, NaN],
262+
channels: [NaN, NaN, 87.26522367839932],
263263
alpha: 1,
264264
syntaxFlags: new Set(['relative-color-syntax', 'has-number-values']),
265265
},

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,15 @@ import { serialize_OKLCH_data } from '../util/serialize.mjs';
55

66
const tests = [
77
['color-mix(in oklch, oklch(100% 0% 60deg), oklch(50% 50% 0deg))', 'oklch(0.75 0.1 0)'],
8+
['color-mix(in oklch, oklch(100% 0% none), oklch(50% 50% 0deg))', 'oklch(0.75 0.1 0)'],
89
['color-mix(in oklch, rgb(255, 255, 255), rgb(180, 6, 95))', 'oklch(0.75031 0.10016 359.858)'],
910
['color-mix(in lch, oklch(75% 0% 60deg), oklch(75% 50% 0deg))', 'oklch(0.74979 0.09824 0.10588)'],
1011
['color-mix(in oklch, oklch(100% 0% none), oklch(50% 50% 0deg))', 'oklch(0.75 0.1 0)'],
1112
['color-mix(in oklch, oklch(100% none 60deg), oklch(50% 50% 0deg))', 'oklch(0.75 0.2 0)'],
1213

1314
// Analogous sets
1415
['color-mix(in oklch, rgb(none none none), oklch(0.5 0.2 50))', 'oklch(0.5 0.2 50)'],
15-
['color-mix(in oklch, hwb(100deg none none), oklch(0.5 0.2 50deg))', 'oklch(0.5 0.2 50)'],
16+
['color-mix(in oklch, hwb(100deg none none), oklch(0.5 0.2 50deg))', 'oklch(0.5 0.2 94.8876)'],
1617
['color-mix(in oklch, lab(100 none none), oklch(0.5 0.2 50deg))', 'oklch(0.75 0.2 50)'],
1718
];
1819

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

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ const tests = [
114114
['color-mix(in hsl increasing hue, hsl(180deg 50% 50%), hsl(180deg 50% 50%))', canonicalize('hsl(180deg 50% 50%)')],
115115
['color-mix(in hsl decreasing hue, hsl(180deg 50% 50%), hsl(180deg 50% 50%))', canonicalize('hsl(180deg 50% 50%)')],
116116

117+
['color-mix(in hsl longer hue, hsl(50deg 0% 50%), hsl(50deg 0% 50%))', canonicalize('hsl(230deg 0% 50%)')],
117118
['color-mix(in hsl, color-mix(in hsl longer hue, hsl(50deg 0% 50%), hsl(50deg 0% 50%)), hsl(180deg 100% 50%))', canonicalize('hsl(180deg 50% 50%)')],
118119

119120
['color-mix(in hsl, hsl(30deg 40% 80% / 25%) 0%, hsl(90deg none none / none))', canonicalize('hsl(30deg 40% 80% / 25%)')],
@@ -188,6 +189,15 @@ for (const test of tests) {
188189
);
189190
}
190191

192+
assert.deepStrictEqual(
193+
color(parse('color-mix(in lch, lch(50% 50% 180deg), lch(100% 0% 0deg))')),
194+
{
195+
colorNotation: 'lch',
196+
channels: [75, 37.5, 180],
197+
alpha: 1,
198+
syntaxFlags: new Set(['color-mix']),
199+
},
200+
);
191201

192202
assert.deepStrictEqual(
193203
color(parse('color-mix(in hsl, red 0%, blue 0%)')),
@@ -233,7 +243,7 @@ assert.deepStrictEqual(
233243
color(parse('oklch(from lch(100 0 0deg) l c h)')),
234244
{
235245
colorNotation: 'oklch',
236-
channels: [1, 4.996003610813204e-16, Number.NaN],
246+
channels: [1, 0, Number.NaN],
237247
alpha: 1,
238248
syntaxFlags: new Set(['relative-color-syntax', 'has-number-values']),
239249
},
@@ -243,7 +253,7 @@ assert.deepStrictEqual(
243253
color(parse('lch(from oklch(100 0 0deg) l c h)')),
244254
{
245255
colorNotation: 'lch',
246-
channels: [100, 1.1102230246251565e-13, Number.NaN],
256+
channels: [100, 0, Number.NaN],
247257
alpha: 1,
248258
syntaxFlags: new Set(['relative-color-syntax', 'has-number-values']),
249259
},
@@ -253,7 +263,7 @@ assert.deepStrictEqual(
253263
color(parse('color-mix(in oklch, lch(100 0 40deg), lch(100 0 60deg))')),
254264
{
255265
colorNotation: 'oklch',
256-
channels: [1.0000000000000002, 4.996003610813204e-16, Number.NaN],
266+
channels: [1.0000000000000002, 0, Number.NaN],
257267
alpha: 1,
258268
syntaxFlags: new Set(['color-mix']),
259269
},

packages/css-color-parser/test/basic/none-exhaustive.mjs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,31 +4,31 @@ import { computedValue, reducePrecisionWholeValue } from '../util/serialize.mjs'
44
{
55
const tests = [
66
// https://github.com/w3c/csswg-drafts/issues/14095
7-
['hsl(from hsl(180 none 50%) h s l)', 'hsl(none none 50%)'],
8-
['hsl(from hsl(180 0 50%) h s l)', 'hsl(none 0% 50%)'],
7+
['hsl(from hsl(180 none 50%) h s l)', 'hsl(180 none 50%)'],
8+
['hsl(from hsl(180 0 50%) h s l)', 'color(srgb 0.5 0.5 0.5)'],
99
['hsl(from lch(20 none 180) h s l)', 'hsl(none none 18.93757452%)'],
1010
['hsl(from lch(20 0 180) h s l)', 'hsl(none 0% 18.93757452%)'],
1111

1212
// https://github.com/w3c/csswg-drafts/issues/14100
1313
['lch(from orchid l 0 h)', 'lch(62.75256542 0 326.96909222)'],
14-
['lch(from lch(from orchid l 0 h) l c h)', 'lch(62.75256542 0 none)'],
14+
['lch(from lch(from orchid l 0 h) l c h)', 'lch(62.75256542 0 326.96909222)'],
1515
['color-mix(in lch, lch(from orchid l 0 h))', 'lch(62.75256542 0 none)'],
1616

1717
// exhaustive tests
1818
// HSL
1919
['hsl(from hsl(none 50% 50%) h s l)', 'hsl(none 50% 50%)'],
20-
['hsl(from hsl(180 none 50%) h s l)', 'hsl(none none 50%)'],
20+
['hsl(from hsl(180 none 50%) h s l)', 'hsl(180 none 50%)'],
2121
['hsl(from hsl(180 50% none) h s l)', 'hsl(180 50% none)'],
2222
['hsl(from hsl(none none 50%) h s l)', 'hsl(none none 50%)'],
23-
['hsl(from hsl(180 none none) h s l)', 'hsl(none none none)'],
23+
['hsl(from hsl(180 none none) h s l)', 'hsl(180 none none)'],
2424
['hsl(from hsl(none 50% none) h s l)', 'hsl(none 50% none)'],
2525
['hsl(from hsl(none none none) h s l)', 'hsl(none none none)'],
2626

2727
['hsl(from hwb(none 25% 25%) h s l)', 'hsl(none 50% 50%)'],
2828
['hsl(from hwb(180 none 25%) h s l)', 'color(srgb 0 0.75 0.75)'],
2929
['hsl(from hwb(180 25% none) h s l)', 'color(srgb 0.25 1 1)'],
3030
['hsl(from hwb(none none 25%) h s l)', 'hsl(none 100% 37.5%)'],
31-
['hsl(from hwb(180 none none) h s l)', 'hsl(none none none)'],
31+
['hsl(from hwb(180 none none) h s l)', 'hsl(180 none none)'],
3232
['hsl(from hwb(none 25% none) h s l)', 'hsl(none 100% 62.5%)'],
3333
['hsl(from hwb(none none none) h s l)', 'hsl(none none none)'],
3434

@@ -132,14 +132,14 @@ import { computedValue, reducePrecisionWholeValue } from '../util/serialize.mjs'
132132
['lch(from hwb(180 none 25%) l c h)', 'lch(69.91337803 42.54334477 196.45478916)'],
133133
['lch(from hwb(180 25% none) l c h)', 'lch(91.18116399 49.29673753 196.65803824)'],
134134
['lch(from hwb(none none 25%) l c h)', 'lch(40.61501478 86.05118759 none)'],
135-
['lch(from hwb(180 none none) l c h)', 'lch(none none none)'],
135+
['lch(from hwb(180 none none) l c h)', 'lch(none none 196.45478916)'],
136136
['lch(from hwb(none 25% none) l c h)', 'lch(58.23109529 85.49243054 none)'],
137137
['lch(from hwb(none none none) l c h)', 'lch(none none none)'],
138138

139139
['lch(from lch(none 20 180) l c h)', 'lch(none 20 180)'],
140-
['lch(from lch(20 none 180) l c h)', 'lch(20 none none)'],
140+
['lch(from lch(20 none 180) l c h)', 'lch(20 none 180)'],
141141
['lch(from lch(20 20 none) l c h)', 'lch(20 20 none)'],
142-
['lch(from lch(none none 180) l c h)', 'lch(none none none)'],
142+
['lch(from lch(none none 180) l c h)', 'lch(none none 180)'],
143143
['lch(from lch(20 none none) l c h)', 'lch(20 none none)'],
144144
['lch(from lch(none 20 none) l c h)', 'lch(none 20 none)'],
145145
['lch(from lch(none none none) l c h)', 'lch(none none none)'],
@@ -184,7 +184,7 @@ import { computedValue, reducePrecisionWholeValue } from '../util/serialize.mjs'
184184
['oklch(from hwb(180 none 25%) l c h)', 'oklch(0.72924735 0.12448121 194.7689599)'],
185185
['oklch(from hwb(180 25% none) l c h)', 'oklch(0.91079253 0.14429702 194.91785641)'],
186186
['oklch(from hwb(none none 25%) l c h)', 'oklch(0.50578216 0.20754918 none)'],
187-
['oklch(from hwb(180 none none) l c h)', 'oklch(none none none)'],
187+
['oklch(from hwb(180 none none) l c h)', 'oklch(none none 194.7689599)'],
188188
['oklch(from hwb(none 25% none) l c h)', 'oklch(0.65951623 0.22690049 none)'],
189189
['oklch(from hwb(none none none) l c h)', 'oklch(none none none)'],
190190

@@ -197,9 +197,9 @@ import { computedValue, reducePrecisionWholeValue } from '../util/serialize.mjs'
197197
['oklch(from lch(none none none) l c h)', 'oklch(none none none)'],
198198

199199
['oklch(from oklch(none 0.2 180) l c h)', 'oklch(none 0.2 180)'],
200-
['oklch(from oklch(0.2 none 180) l c h)', 'oklch(0.2 none none)'],
200+
['oklch(from oklch(0.2 none 180) l c h)', 'oklch(0.2 none 180)'],
201201
['oklch(from oklch(0.2 0.2 none) l c h)', 'oklch(0.2 0.2 none)'],
202-
['oklch(from oklch(none none 180) l c h)', 'oklch(none none none)'],
202+
['oklch(from oklch(none none 180) l c h)', 'oklch(none none 180)'],
203203
['oklch(from oklch(0.2 none none) l c h)', 'oklch(0.2 none none)'],
204204
['oklch(from oklch(none 0.2 none) l c h)', 'oklch(none 0.2 none)'],
205205
['oklch(from oklch(none none none) l c h)', 'oklch(none none none)'],

0 commit comments

Comments
 (0)