csswg-drafts
csswg-drafts copied to clipboard
[css-color-4] How to handle infinite values in color functions?
Unless otherwise specified, color channel values are not clamped, which applies to values produced by math functions, which can be infinite.
Infinite color channel values produce NaN
in most conversion functions.
Specifically...
-
color(srgb-linear) -> color(xyz-d65)
- when one or more channels are infinite and one or more are the opposite infinite
-
color(xyz-d50) -> color(xyz-d65)
-
color(xyz-d65) -> color(srgb-linear)
- when
x
andy
are the same infinite - when
x
/y
andz
are infinite
- when
-
color(xyz-d50) -> lab()
- when
x
andy
are the same infinite - when
y
andz
are the same infinite
- when
-
color(xyz-d65) -> color(xyz-d50)
- when
x
andy
are infinite - when
y
andz
are infinite - when
x
andz
are the same infinite
- when
-
color(xyz-d65) -> oklab()
-
oklab() -> color(xyz-d65)
-
rgb() -> hsl()
- when one or more channel is infinite
-
hsl() -> rgb()
- when
s
is infinite andl
is 0/100 - when
l
is infinite
- when
-
hwb() -> rgb()
- when
w
is infinite - when
b
is -Infinity
- when
-
lab() -> color(xyz-d50)
- when
l
is infinite anda
is the opposite infinite - when
l
andb
are the same infinite
- when
-
lch() -> lab()
-
oklch() -> oklab()
- when
c
is infinite andh
is 0
- when
This CodePen allows to play with conversion functions, which have comments for NaN
cases.
In [email protected]
, NaN
seems to be replaced by none
, which seems incorrect:
let color = new Color('hsl', [0, 0, Infinity])
color.to('srgb').toString(); // rgb(none none none)
Clarification on how to handle infinite values has already been asked in #8629, which was resolved with no further change because if you put an infinite calculation into an rgba(), the behavior is well-defined: clamp to the allowed range.
At least, this is not true for some channels of other color functions than rgb()
, and for relative colors.
Following this comment, I tried to guess the color resulting from a color function specified with one infinite channel value. Since I am personally only interested by serialized values, I only did it for hsl() -> rgb()
and hwb() -> rgb()
, but it should presumably be done for all conversions producing NaN
.
When converting hsl()
to rgb()
and saturation
is +Infinity
or -Infinity
: [...]
- when
l === 0
,r
,g
,b
, are0
- when
l < 0
, same as whenl > 0
but with inverted infinite signs - when
l > 0
:
h |
r |
g |
b |
---|---|---|---|
330 < h < 30 |
+Infinity |
-Infinity |
-Infinity |
h === 30 |
+Infinity |
depends on l |
-Infinity |
30 < h < 90 |
+Infinity |
+Infinity |
-Infinity |
h === 90 |
depends on l |
+Infinity |
-Infinity |
90 < h < 150 |
-Infinity |
+Infinity |
-Infinity |
h === 150 |
-Infinity |
+Infinity |
depends on l |
150 < h < 210 |
-Infinity |
+Infinity |
+Infinity |
h === 210 |
-Infinity |
depends on l |
+Infinity |
210 < h < 270 |
-Infinity |
-Infinity |
+Infinity |
h === 270 |
depends on l |
-Infinity |
+Infinity |
270 < h < 330 |
+Infinity |
-Infinity |
+Infinity |
h === 330 |
+Infinity |
-Infinity |
depends on l |
When converting hsl()
to rgb()
and l === +Infinity
:
- when
-100 <= saturation <= 100
,r
,g
,b
, are+Infinity
- when
saturation < -100
, same assaturation > 100
but with inverted infinite signs - when
saturation > 100
,r
,g
,b
, are+Infinity
/-Infinity
depending onsaturation
andh
- there are 6 intervals defined by
h
- the exact
h
interval values depend onsaturation
- for each sibling intervals, one of
r
/g
/b
has a different sign
- there are 6 intervals defined by
When converting hsl()
to rgb()
and l === -Infinity
:
- when
-100 <= saturation <= 100
,r
,g
,b
, are-Infinity
- when
saturation < -100
orsaturation > 100
, same as whenlightness === +Infinity
but with different intervals
Let's skip hwb() -> rgb()
because it becomes more complicated when more than one channel value is infinite.
For example, when a === 0
, z
tends towards +Infinity
when l
tends towards +Infinity
, but z
tends towards -Infinity
when b
also tends towards +Infinity
. Should z
be 0
? Should some "precedence" between channel values be defined?
Note that channel values do not always tend linearly towards an infinite value:
-
rgb(from hsl(0 -99 calc(infinity)) r g b)
would resolve tocolor(srgb calc(infinity) calc(infinity) calc(infinity))
-
rgb(from hsl(0 -101 calc(infinity)) r g b)
would resolve tocolor(srgb calc(infinity) calc(-infinity) calc(-infinity))
Am I missing something?