You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[css-color-5] calc(infinity) in relative color and interpolation #14088
changed the title [-][css-color-5] `calc(infinity)` in relative color[/-][+][css-color-5] `calc(infinity)` in relative color and interpolation[/+]on Jun 27, 2026
The second step would be converting color(srgb {max-finite-value} 0.2 0.3) to linear-srgb by applying the inverse gamma function on each component. In WebKit, that function looks something like:
float toLinearSRGB(float c)
{
auto sign = std::signbit(c) ? -1.0f : 1.0f;
c = std::abs(c);
if (c <= 0.04045f)
return c / 12.92f * sign;
return std::pow((c + 0.055f) / 1.055f, 2.4f) * sign;
}
For that first component that started as calc(Infinity) and is now {max-finite-value} (or FLT_MAX in WebKit), the call to toLinearSRGB will return INFINITY.
So, the question becomes, does that INFINITY also get clamped to {max-finite-value}? Or should the rest of the color conversion math continue with the infinity in place?
@romainmenke I am curious how your implementation deals with this? How do you avoid saturating to +infinity during conversion to display-p3?
@romainmenke I am curious how your implementation deals with this? How do you avoid saturating to +infinity during conversion to display-p3?
I use 2_147_483_647 instead of the maximum float value in JavaScript.
My thinking at the time was that this would avoid getting another infinity after conversion while still producing a value that is excessively large for any component.
So, the question becomes, does that INFINITY also get clamped to {max-finite-value}? Or should the rest of the color conversion math continue with the infinity in place?
I also see some parallels with powerless components/none in that maybe these should carry over and be re-inserted as {max-finite-value} or calc(Infinity) in analogous components.
It is indeed up to the implementation (in general, intentionally undefined, because we don't specify exactly what supported value ranges are) how precisely to treat infinity. The spec only mandates that math functions clamp to the allowed range, whatever that is in the given calculation context. There is intentionally zero difference between calc(infinity) and 1e1000; both are too large to actually be stored, and will instead be treated as some maximum value.
(We keep getting these sort of questions about calc(infinity) in various contexts. What needs to be fixed in the spec to make it clearer that it's exactly identical to just using an incredibly large value, and thus already has well-defined behavior?)
I agree that the spec isn't unclear on calc(infinity)
But leaving it up to implementations to pick a value, makes it harder to have WPT tests and the lack of WPT tests leads to differences in implementations 🤷
My initial question:
what is the computed value and serialization?
was also asked because I wanted to add tests for values like: color(from color(srgb calc(Infinity) 0.2 0.3) display-p3 r g b).
But maybe we can test that implementations do the right thing without checking for a specific computed value?
But leaving it up to implementations to pick a value, makes it harder to have WPT tests and the lack of WPT tests leads to differences in implementations 🤷
Sure, but again, this is exactly the same problem as using an ordinary 1e1000 value, as in color(from color(srgb 1e1000 0.2 0.3) display-p3 r g b). We can't strictly specify behavior without incidentally strictly specifying the internal numeric representation, which we've previously rejected doing.
But maybe we can test that implementations do the right thing without checking for a specific computed value?
You could, for example, check that using infinity and using a very large number give the same result, whatever it is.
🤔 I don't know if/how that would work.
On my end for example 1e1000 already parses as a number that is Infinity in JavaScript.
So comparing that to calc(Infinity) would obviously yield the same result, but not for the right reasons.
Any bugs that exist against calc(Infinity) would also exist for 1e1000.
Maybe this is different in browser implementations?
Activity
romainmenke commentedon Jun 27, 2026
This is also observable with interpolation.
in xyz in Firefox:
https://codepen.io/romainmenke/pen/myRMRQj
The examples have a control gradient with a reasonable but large value, followed by a gradient with
calc(Infinity)in the same channeland in Safari:
in lch in Firefox:
https://codepen.io/romainmenke/pen/ZYLJLqQ
Cylindrical polar color models do better here because they have more clamping.
hueis always normalized to[0, 360]andlightnessto[0, 100](chrome just fails to render things with infinities, so unknown what their outcome would look like)
[-][css-color-5] `calc(infinity)` in relative color[/-][+][css-color-5] `calc(infinity)` in relative color and interpolation[/+]svgeesus commentedon Jun 27, 2026
Tagging @tabatkins who I think introduced this "coerce to plus infinity" concept. Also tagging @LeaVerou for comment.
Loirooriol commentedon Jun 27, 2026
Why not? Isn't that exposed in
width: calc(1px / 0)and so on?weinig commentedon Jun 27, 2026
I think the underlying question here is actually how should color conversions work in the face of maximum-finite-value component values.
For example, in your example above:
to resolve this, the origin color,
color(srgb calc(Infinity) 0.2 0.3), must be converted todisplay-p3so it can be used by the relative color syntax.But, is that color conversion well defined?
The first step seems well defined, and its evaluating the calc expression, turning
color(srgb calc(Infinity) 0.2 0.3)intocolor(srgb {max-finite-value} 0.2 0.3)("Infinities do not escape a top-level calculation; they’re clamped to the minimum or maximum value allowed in the context, as defined in § 10.12 Range Checking." - https://drafts.csswg.org/css-values-4/#css-infinity)The second step would be converting
color(srgb {max-finite-value} 0.2 0.3)to linear-srgb by applying the inverse gamma function on each component. In WebKit, that function looks something like:For that first component that started as
calc(Infinity)and is now{max-finite-value}(or FLT_MAX in WebKit), the call to toLinearSRGB will return INFINITY.So, the question becomes, does that INFINITY also get clamped to
{max-finite-value}? Or should the rest of the color conversion math continue with the infinity in place?@romainmenke I am curious how your implementation deals with this? How do you avoid saturating to +infinity during conversion to display-p3?
romainmenke commentedon Jun 27, 2026
I use
2_147_483_647instead of the maximum float value in JavaScript.My thinking at the time was that this would avoid getting another infinity after conversion while still producing a value that is excessively large for any component.
I also see some parallels with powerless components/none in that maybe these should carry over and be re-inserted as
{max-finite-value}orcalc(Infinity)in analogous components.svgeesus commentedon Jun 29, 2026
That would be my assumption, yes
tabatkins commentedon Jun 29, 2026
It is indeed up to the implementation (in general, intentionally undefined, because we don't specify exactly what supported value ranges are) how precisely to treat infinity. The spec only mandates that math functions clamp to the allowed range, whatever that is in the given calculation context. There is intentionally zero difference between
calc(infinity)and1e1000; both are too large to actually be stored, and will instead be treated as some maximum value.(We keep getting these sort of questions about
calc(infinity)in various contexts. What needs to be fixed in the spec to make it clearer that it's exactly identical to just using an incredibly large value, and thus already has well-defined behavior?)romainmenke commentedon Jun 29, 2026
I agree that the spec isn't unclear on
calc(infinity)But leaving it up to implementations to pick a value, makes it harder to have WPT tests and the lack of WPT tests leads to differences in implementations 🤷
My initial question:
was also asked because I wanted to add tests for values like:
color(from color(srgb calc(Infinity) 0.2 0.3) display-p3 r g b).But maybe we can test that implementations do the right thing without checking for a specific computed value?
tabatkins commentedon Jun 29, 2026
Sure, but again, this is exactly the same problem as using an ordinary
1e1000value, as incolor(from color(srgb 1e1000 0.2 0.3) display-p3 r g b). We can't strictly specify behavior without incidentally strictly specifying the internal numeric representation, which we've previously rejected doing.You could, for example, check that using infinity and using a very large number give the same result, whatever it is.
romainmenke commentedon Jun 30, 2026
🤔 I don't know if/how that would work.
On my end for example
1e1000already parses as a number that isInfinityin JavaScript.So comparing that to
calc(Infinity)would obviously yield the same result, but not for the right reasons.Any bugs that exist against
calc(Infinity)would also exist for1e1000.Maybe this is different in browser implementations?
romainmenke commentedon Sep 4, 2026
Maybe tests could check a series of asserts instead of actual channel values?
For example:
r > gr > bg > rg > br == gr == bg == rg == bAnd that a test passes if the computed value passes or fails the same asserts as a reference value?
Maybe a couple of special cases like
is blackoris whiteto catch cases where all channels equal each other and would pass the asserts listed above.