Skip to content

[css-color][css-animations] Is it specified anywhere that a color animation at time=0 exactly match the from color's computed value? #13269

Open

Description

@weinig

In the WPT test http://wpt.live/css/css-animations/animate-with-background-color-oklch-002.html, the first subtest is testing an animation from background: #ff0000 to background-color: oklch(45% 0.2 264) at a few different time steps as described by this list:

  const bg_color_legacy_rgb_to_oklch = [
    { at: 0, value: 'rgb(255, 0, 0)' },
    { at: 0.25, value: 'oklab(0.583475 0.163433 0.0446685)' },
    { at: 0.5, value: 'oklab(0.538983 0.101987 -0.0365225)' },
    { at: 0.75, value: 'oklab(0.494492 0.0405407 -0.117713)' },
    { at: 1, value: 'oklab(0.45 -0.0209057 -0.198904)' }
  ];

Generally, when computing the value of a color animation at a particular time step, it is going to have a computed value that is serialized as OkLab color, since that is the interpolation color space.

This test however, assumes that at time=0, the computed value should be precise value of the from color, which has a computed value using the legacy RGB serialization.

Is this behavior, of acting like no interpolation happed at all, specified anywhere?

(cc @birtles, @dbaron, @graouts, @svgeesus).

Activity

  1. jfkthame commented on Dec 25, 2025

    @jfkthame
    Contributor

    I don't think this is specific to time=0; in a little local testing with Chrome, it also seems to happen at other times that exactly match one of the specified color-stops in the animation.

  2. weinig commented on Dec 25, 2025

    @weinig
    ContributorAuthor

    Yeah, I kinda knew it was broader, but struggled to find a succinct way to ask the question for the general case.

  3. svgeesus commented on Dec 26, 2025

    @svgeesus
    Collaborator

    It seems like in the general case, the animations spec should be clear whether you get the specified or computed value

    • when it has to be computed because it isn't one of the stops
    • when it exactly matches a stop (it could still return the computed value, of course, if the spec says so)

    @birtles ?

  4. birtles commented on Dec 27, 2025

    @birtles
    Contributor

    Is this behavior, of acting like no interpolation happed at all, specified anywhere?

    Yes, it falls out of the interpolation procedure which has:

    Given two property values VA and VB, produces an intermediate value Vresult at a distance of p along the interval between VA and VB such that p = 0 produces VA and p = 1 produces VB.

    It seems like in the general case, the animations spec should be clear whether you get the specified or computed value

    • when it has to be computed because it isn't one of the stops
    • when it exactly matches a stop (it could still return the computed value, of course, if the spec says so)

    It always uses computed values as per https://drafts.csswg.org/web-animations-1/#calculating-computed-keyframes specifically where it has:

    Compute a property value using the value specified on keyframe as the value, and the target element as the element; then add the property and resulting value to computed keyframe.

    It's just that rgb(255, 0, 0) is a computed value, but it's never interpolated with anything because it's at the endpoint of the interpolation interval.

  5. Loirooriol commented on Dec 27, 2025

    @Loirooriol
    Contributor

    Yeah this is not specific of colors at all, e.g. animating font-style: normal to oblique 50deg is like

    • At 0%, normal (not oblique 0deg!)
    • At 1%, oblique 0.5deg
    • At 50%, oblique 25deg
    • At 100%, oblique 50deg
  6. weinig commented on Dec 28, 2025

    @weinig
    ContributorAuthor

    Is this behavior, of acting like no interpolation happed at all, specified anywhere?
    Yes, it falls out of the interpolation procedure which has:

    Given two property values VA and VB, produces an intermediate value Vresult at a distance of p along the interval between VA and VB such that p = 0 produces VA and p = 1 produces VB.

    @birtles Thanks! Ok, I can totally see how that would mean that at time=0, and time=1 should return the identical serialization, since it calls out "p = 0 produces VA and p = 1 produces VB". Great!

    What about at other explicit stops. For example, what would you expect the computed value serialization be for the following:

      @keyframes bg-color-legacy-rgb {
        from {
          background-color: red;
        }
        50% {
          background-color: lime;
        }
        to {
          background-color: blue;
        }
      }

    Not sure where in the spec the interpolation procedure is invoked, so I can't tell if the interval (p=0 to p=1) is exactly time=0, to time=1, or if it is percentage of the current keyframe (I kinda assume animations would invoke it per-keyframe).

    So (and I will make a new WPT test with these answers), what should the computed value serialization be for that animation above at various timesteps:

    I think I know these first three:

    • At time=0, we are saying the computed style serialization would be rgb(255, 0, 0).
    • At time=1, we are saying the computed style serialization would be rgb(0, 0, 255).
    • At time=0.25 (or any non-explicit stop), we are saying the computed style serialization would be some oklab() value.

    But what about time=0.5? Should it be rgb(0, 255, 0) or oklab(87% -0.23 0.18)?

  7. birtles commented on Dec 29, 2025

    @birtles
    Contributor

    Sorry, away from my computer for a bit but the interpolation behavior applies to any interpolation interval and the process of computing a keyframe's effect value will set up an interpolation interval between the closest keyframes so not necessarily time=0/1.

  8. added
    spec-test-mismatchIssues about tests that seem to assume things that contradict the spec or are not specified at all
    on Jan 7, 2026
  9. moved this from Agenda+ FTF to Tuesday afternoon in CSSWG spring 2026 meeting agendaon Mar 26, 2026
  10. svgeesus commented on Mar 31, 2026

    @svgeesus
    Collaborator

    But what about time=0.5? Should it be rgb(0, 255, 0) or oklab(87% -0.23 0.18)?

    My understanding is that a multi-stop animation is a chain of interpolations (here, between two colors) and that at these stops, there is no interpolation needed to deduce the value. So here, rgb(0, 255, 0) is the answer.

  11. css-meeting-bot commented on Mar 31, 2026

    @css-meeting-bot
    Member

    The CSS Working Group just discussed [css-color][css-animations] Is it specified anywhere that a color animation at time=0 exactly match the from color's computed value?, and agreed to the following:

    • RESOLVED: Update the animations spec with an example
    The full IRC log of that discussion <fantasai> ChrisL: If you're not interpolating, then you don't do any color interpolation, so the color shouldn't change.
    <emilio> ChrisL: I don't think it's explicit but I think it should, I think the birtles mentioned it falls out of the spec
    <flackr> q+
    <emilio> ChrisL: in the start and end you get the computed value
    <emilio> ack flackr
    <emilio> flackr: that's right
    <emilio> ... there are cases where before / after the keyframe you have a different form because your interpolation dependends the other keyframe
    <emilio> ... so before a keyframe could have a different shape than after
    <emilio> ... so makes sense that at the keyframe you get the explicit keyframe behavior
    <TabAtkins> A -> X and X -> B might interpret X differently in the two cases
    <TabAtkins> so we should preserve the X value as specified so it can properly reinterpret
    <emilio> astearns: so the test is correct and we just need to be explicit about why
    <emilio> ChrisL: so we should add more tests
    <emilio> astearns: maybe adding a color example?
    <emilio> PROPOSED: Update the spec with an example
    <emilio> PROPOSED: Update the animations spec with an example
    <emilio> RESOLVED: Update the animations spec with an example
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions