Skip to content

[css-color] How to handle out-of-bounds legacy srgb values? #10087

Open
@Loirooriol

Description

@Loirooriol

Consider this testcase:

<!DOCTYPE html>
<style>
#a { width: 200px; height: 200px; animation: a 2s -1s paused linear(0, -1, 1) }
#b { width: 100px; height: 100px; animation: b 2s -1s paused linear }
#c { width: 100px; height: 100px; animation: c 2s -1s paused linear }
@keyframes a {
  from { background-color: rgb(75, 0, 0) }
  to { background-color: rgb(0, 255, 0) }
}
@keyframes b {
  from { background-color: inherit }
  to { background-color: rgb(0, 255, 0) }
}
@keyframes c {
  from { background-color: /* Set in JS */ }
  to { background-color: rgb(0, 255, 0) }
}
</style>
<div id="a">
  <div id="b"></div>
  <div id="c"></div>
</div>
<pre><script>
document.styleSheets[0].cssRules[5].cssRules[0].style.backgroundColor = getComputedStyle(a).backgroundColor;
document.writeln(getComputedStyle(a).backgroundColor);
document.writeln(getComputedStyle(b).backgroundColor);
document.writeln(getComputedStyle(c).backgroundColor);
</script>

The background of #a is the interpolation between rgb(75, 0, 0) and rgb(0, 255, 0). The easing function is linear(0, -1, 1), so at 50% the input progress value is -1. Both colors are legacy, so we interpolate in srgb instead of oklab, producing a color as such:

  • Color space: srgb
  • Legacy flag: yes
  • Red: 150
  • Green: -255
  • Blue: 0

But of course, G = -255 is out of bounds, so the painted background is rgb(150, 0, 0).

However, on Gecko and Blink, the computed value still has this G = -255, as we can observe in #b. The background color of #b is the 50% linear interpolation between the value inherited from #a and rgb(0, 255, 0). This serializes as rgb(75, 0, 0), which means that the inherited value has G = -255.

The problem is, what do we get if we serialize the computed value of #a? Since G = -255 is out-of-bounds, browsers just clamp and serialize as rgb(150, 0, 0).

But of course, when #c does the same as #b, but interpolating from the provided rgb(150, 0, 0), then it produces rgb(75, 128, 0).

WebKit seems to adjust the computed value of #a, because #b produces rgb(75, 128, 0) just like #c.

So, questions:

  • Why does color-mix() prevent me from picking a percentage outside of the 0-100% range, if I can do the mix with arbitrary values via animations/transitions (but it's much less convenient)?
  • Should Gecko and Blink adjust the computed value to avoid out-of-bounds problems?
  • If not, should we change the serialization so that it doesn't become a different color? Maybe serialize with color-mix()?

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    Status

    Wednesday morning

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions