[css-color][css-transitions] Interpolation between currentcolor keyword and actual color value

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
6 messages Options
Reply | Threaded
Open this post in threaded view
|

[css-color][css-transitions] Interpolation between currentcolor keyword and actual color value

Xidorn Quan-2
Hi,

When we are implementing -webkit-text-fill-color, we found an issue with
transition due to we always computing "currentcolor" to an actual color
value for interpolation rather than keeping it an computed value. [1]

This raises a question about how "currentcolor" should be interpolated
with an actual color value, given it is a computed value? The spec
doesn't say anything about this.

A simple example code is:
@keyframes t {
  0% {
    color: #f00;
    -webkit-text-fill-color: currentcolor;
  }
  100% {
    color: #0f0;
    -webkit-text-fill-color: #00f;
  }
}

This code works in Firefox Nightly and all other major browser engines.
And there are three different behaviors:
1. WebKit interpolates between #f00 (color at 0%) and #00f
(text-fill-color at 100%), and Gecko currently has the same behavior,
though somehow more buggy.
2. Blink treats currentcolor and #00f as uninterpolatable, so in the
front 50%, it changes between #f00 and #808000, and then it becomes a
solid #00f.
3. Edge treats currentcolor of text-fill-color at 0% as white somehow,
and interpolates that with #00f.

Edge's behavior doesn't make sense at all, and Blink's behavior looks
conforming given the spec doesn't say anything currently, but it seems
WebKit's behavior is the most sensible one in the three.

Furthermore, if currentcolor is a computed value, the most ideally
effect is probably making currentcolor follows the value of color at
every moment, so at 50% of the transition, the used value of
text-fill-color should be ((#f00 + #0f0) / 2 + #00f) / 2, rather than
WebKit's behavior which is (#f00 + #00f) / 2.

However, that could add much complexity to implementation. If there is
no actual usecase, it is probably not worth that effort. Also if we
choose this hard way, it could be a breaking change when switch
currentcolor to a computed value for some cases.

So I propose we can probably say something like, when one value is
currentcolor and the other value is an actual color, treat currentcolor
as its current used value at the given moment in advance.

What do you think?


[1] https://bugzilla.mozilla.org/show_bug.cgi?id=1260543

- Xidorn


Reply | Threaded
Open this post in threaded view
|

Re: [css-color][css-transitions] Interpolation between currentcolor keyword and actual color value

Tab Atkins Jr.
On Sun, Apr 17, 2016 at 6:05 PM, Xidorn Quan <[hidden email]> wrote:

> When we are implementing -webkit-text-fill-color, we found an issue with
> transition due to we always computing "currentcolor" to an actual color
> value for interpolation rather than keeping it an computed value. [1]
>
> This raises a question about how "currentcolor" should be interpolated
> with an actual color value, given it is a computed value? The spec
> doesn't say anything about this.
>
> A simple example code is:
> @keyframes t {
>   0% {
>     color: #f00;
>     -webkit-text-fill-color: currentcolor;
>   }
>   100% {
>     color: #0f0;
>     -webkit-text-fill-color: #00f;
>   }
> }
>
> This code works in Firefox Nightly and all other major browser engines.
> And there are three different behaviors:
> 1. WebKit interpolates between #f00 (color at 0%) and #00f
> (text-fill-color at 100%), and Gecko currently has the same behavior,
> though somehow more buggy.
> 2. Blink treats currentcolor and #00f as uninterpolatable, so in the
> front 50%, it changes between #f00 and #808000, and then it becomes a
> solid #00f.
> 3. Edge treats currentcolor of text-fill-color at 0% as white somehow,
> and interpolates that with #00f.
>
> Edge's behavior doesn't make sense at all, and Blink's behavior looks
> conforming given the spec doesn't say anything currently, but it seems
> WebKit's behavior is the most sensible one in the three.
>
> Furthermore, if currentcolor is a computed value, the most ideally
> effect is probably making currentcolor follows the value of color at
> every moment, so at 50% of the transition, the used value of
> text-fill-color should be ((#f00 + #0f0) / 2 + #00f) / 2, rather than
> WebKit's behavior which is (#f00 + #00f) / 2.
>
> However, that could add much complexity to implementation. If there is
> no actual usecase, it is probably not worth that effort. Also if we
> choose this hard way, it could be a breaking change when switch
> currentcolor to a computed value for some cases.
>
> So I propose we can probably say something like, when one value is
> currentcolor and the other value is an actual color, treat currentcolor
> as its current used value at the given moment in advance.
>
> What do you think?
>
>
> [1] https://bugzilla.mozilla.org/show_bug.cgi?id=1260543

Blink's behavior is currently the spec-compliant one, unfortunately.
currentcolor doesn't simplify at computed-value time, and so you can't
represent the intermediate color.

To fix this well, we need to add some way to represent the
intermediate value - this means adding a color-blending function.  I
have a bad version of that in Colors 4 currently, but need to fix it
up to work better.  Once we have that, you can have the intermediate
color be rgb-blend(currentcolor, #00f 50%) or whatever (stealing
syntax from cross-fade()), and transitions will be possible.  This
will produce the behavior you say is "ideal" but "complex".  However,
it's probably not that complex if it's just the result of evaluating
the preceding expression, is it?

I'm not aware of any plans to "switch currentcolor to a computed value
for some cases" - can you elaborate?

~TJ

Reply | Threaded
Open this post in threaded view
|

Re: [css-color][css-transitions] Interpolation between currentcolor keyword and actual color value

L. David Baron
On Monday 2016-04-18 14:43 -0700, Tab Atkins Jr. wrote:

> On Sun, Apr 17, 2016 at 6:05 PM, Xidorn Quan <[hidden email]> wrote:
> > When we are implementing -webkit-text-fill-color, we found an issue with
> > transition due to we always computing "currentcolor" to an actual color
> > value for interpolation rather than keeping it an computed value. [1]
> >
> > This raises a question about how "currentcolor" should be interpolated
> > with an actual color value, given it is a computed value? The spec
> > doesn't say anything about this.
> >
> > A simple example code is:
> > @keyframes t {
> >   0% {
> >     color: #f00;
> >     -webkit-text-fill-color: currentcolor;
> >   }
> >   100% {
> >     color: #0f0;
> >     -webkit-text-fill-color: #00f;
> >   }
> > }
> >
> > This code works in Firefox Nightly and all other major browser engines.
> > And there are three different behaviors:
> > 1. WebKit interpolates between #f00 (color at 0%) and #00f
> > (text-fill-color at 100%), and Gecko currently has the same behavior,
> > though somehow more buggy.
> > 2. Blink treats currentcolor and #00f as uninterpolatable, so in the
> > front 50%, it changes between #f00 and #808000, and then it becomes a
> > solid #00f.
> > 3. Edge treats currentcolor of text-fill-color at 0% as white somehow,
> > and interpolates that with #00f.
> >
> > Edge's behavior doesn't make sense at all, and Blink's behavior looks
> > conforming given the spec doesn't say anything currently, but it seems
> > WebKit's behavior is the most sensible one in the three.
> >
> > Furthermore, if currentcolor is a computed value, the most ideally
> > effect is probably making currentcolor follows the value of color at
> > every moment, so at 50% of the transition, the used value of
> > text-fill-color should be ((#f00 + #0f0) / 2 + #00f) / 2, rather than
> > WebKit's behavior which is (#f00 + #00f) / 2.
> >
> > However, that could add much complexity to implementation. If there is
> > no actual usecase, it is probably not worth that effort. Also if we
> > choose this hard way, it could be a breaking change when switch
> > currentcolor to a computed value for some cases.
> >
> > So I propose we can probably say something like, when one value is
> > currentcolor and the other value is an actual color, treat currentcolor
> > as its current used value at the given moment in advance.
> >
> > What do you think?
> >
> >
> > [1] https://bugzilla.mozilla.org/show_bug.cgi?id=1260543
>
> Blink's behavior is currently the spec-compliant one, unfortunately.
> currentcolor doesn't simplify at computed-value time, and so you can't
> represent the intermediate color.
>
> To fix this well, we need to add some way to represent the
> intermediate value - this means adding a color-blending function.  I
> have a bad version of that in Colors 4 currently, but need to fix it
> up to work better.  Once we have that, you can have the intermediate
> color be rgb-blend(currentcolor, #00f 50%) or whatever (stealing
> syntax from cross-fade()), and transitions will be possible.  This
> will produce the behavior you say is "ideal" but "complex".  However,
> it's probably not that complex if it's just the result of evaluating
> the preceding expression, is it?
>
> I'm not aware of any plans to "switch currentcolor to a computed value
> for some cases" - can you elaborate?
We've agreed to switch currentcolor to being a computed value.  We
errata'd css-color-3 to do that:
https://www.w3.org/Style/2011/REC-css3-color-20110607-errata.html#s.4.5
and made it such in css-color-4:
https://drafts.csswg.org/css-color-4/#currentcolor-color

In our implementation (and I would think in others too) this needs
to be implemented per-property.  So we have some properties handling
currentcolor the new way (as a computed value) and others falling
back to the old way (which doesn't need to be done per-property and
is thus within the generic color handling).  

We've had to do things the new way for properties where currentcolor
is the initial value; text-emphasis-color and
-webkit-text-fill-color depend on the change.  (Though for some
legacy properties we don't handle currentcolor this way, but instead
have a separate -moz-use-text-color that predates currentcolor.)

But for most of these properties we have code to treat the computed
value as being a color for animation purposes so that it does get
animated.  To fix the above compatibility bug Xidorn just removed
that code for text-emphasis-color and -webkit-text-fill-color so
they don't animate between currentcolor and a non-currentcolor
value, because of compatibility problems with
-webkit-text-fill-color, in
https://bugzilla.mozilla.org/show_bug.cgi?id=1260543 .

I'd been hesitant to make the change for other properties because it
would break animations that authors expect to work.  But now that I
realize those animations don't work for -webkit-text-fill-color, I
suspect they might all not work in Blink / WebKit, and there's no
compatibility risk, although I think this needs to be tested
per-property (which I haven't done).

But I'm also hesitant to change other properties, or to leave things
in this situation for a long time, because I'm worried about the Web
becoming dependent on animations *not* working (as happened with
-webkit-text-fill-color).

I'm also hesitant to leave things in this situation for a long time
because I'm worried that the Web might become dependent on having
some properties behave in one way and some properties behave in the
other way, either for animation or non-animation reasons.  Right now
Gecko does do animations from a currentcolor to non-currentcolor
value for things like border-color.  Essentially, depending on the
property, we now have four different behaviors for currentcolor:

1. resolved to a color at computed-value time (fill, stroke,
   gradient color stops, shadow colors, background-color,
   flood-color, lighting-color, stop-color)

2. resolved to a color at computed-value time when specified
   explicitly, but at used-value time when it's the initial value
   (via -moz-use-text-color, which predates currentcolor), and
   animated as though it was resolved to a color at computed-value
   time either way (border-color, outline-color,
   column-rule-color)

3. resolved to a color at used-value time, but animated as though
   it was resolved to a color at computed-value time
   (text-decoration-color)

4. resolved to a color at used-value time, and not animated between
   currentcolor and color values (text-emphasis-color,
   -webkit-text-fill-color)

The spec currently requires (4), but we're hoping in the long term
to make it something more like but slightly better than (3), where
the animation treats currentcolor as a computed value so it doesn't
interpolate changes from currentcolor to currentcolor when color
changes (which is the problem that caused bug 1260543, because that
created a -webkit-text-fill-color value that inherited to a
descendant that specified color, and then overrode that color) but
that it does animate between currentcolor and real color values.

My main worries about compatibility risk are for the properties
currently in group (2) and for fill and stroke in group (1), since
if we change them to group (4) we might never be able to change them
back, because people will depend on them not animating.

I haven't tested what other implementations do here; there are a
*lot* of cases to test.  I'm curious if other implementors can
explain what state their implementations are in here (perhaps a
better state for future risk).  But my general concern here is that
the group is making potentially-breaking changes, and then adding
dependencies on those changes, before implementations have had a
chance to test that they're actually safe changes to make.

-David

--
𝄞   L. David Baron                         http://dbaron.org/   𝄂
𝄢   Mozilla                          https://www.mozilla.org/   𝄂
             Before I built a wall I'd ask to know
             What I was walling in or walling out,
             And to whom I was like to give offense.
               - Robert Frost, Mending Wall (1914)

signature.asc (836 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: [css-color][css-transitions] Interpolation between currentcolor keyword and actual color value

Tab Atkins Jr.
On Fri, Apr 22, 2016 at 10:48 AM, L. David Baron <[hidden email]> wrote:
> On Monday 2016-04-18 14:43 -0700, Tab Atkins Jr. wrote:
>> I'm not aware of any plans to "switch currentcolor to a computed value
>> for some cases" - can you elaborate?
>
> We've agreed to switch currentcolor to being a computed value.  We
> errata'd css-color-3 to do that:
> https://www.w3.org/Style/2011/REC-css3-color-20110607-errata.html#s.4.5
> and made it such in css-color-4:
> https://drafts.csswg.org/css-color-4/#currentcolor-color

Ah, Xidorn's wording confused me!  In particular, if currentcolor is
turned into an RGBA at computed-value time, then there's never a
problem at all and animations work exactly like animating between px
and em does; the entire issue is that we've specced it to not become
RGBA until used-value time, which instead puts us in a "px vs %"
situation, and we need a calc() equivalent for colors to represent the
intermediate animated value.

(I think you and he use the term "computed value" to mean something
different than I do - I'd call "1em" a "computed value", because it's
absolutized at computed-value time, while "width: 10%" is a "used
value".)

> In our implementation (and I would think in others too) this needs
> to be implemented per-property.  So we have some properties handling
> currentcolor the new way (as a computed value) and others falling
> back to the old way (which doesn't need to be done per-property and
> is thus within the generic color handling).
>
> We've had to do things the new way for properties where currentcolor
> is the initial value; text-emphasis-color and
> -webkit-text-fill-color depend on the change.  (Though for some
> legacy properties we don't handle currentcolor this way, but instead
> have a separate -moz-use-text-color that predates currentcolor.)
>
> But for most of these properties we have code to treat the computed
> value as being a color for animation purposes so that it does get
> animated.  To fix the above compatibility bug Xidorn just removed
> that code for text-emphasis-color and -webkit-text-fill-color so
> they don't animate between currentcolor and a non-currentcolor
> value, because of compatibility problems with
> -webkit-text-fill-color, in
> https://bugzilla.mozilla.org/show_bug.cgi?id=1260543 .
>
> I'd been hesitant to make the change for other properties because it
> would break animations that authors expect to work.  But now that I
> realize those animations don't work for -webkit-text-fill-color, I
> suspect they might all not work in Blink / WebKit, and there's no
> compatibility risk, although I think this needs to be tested
> per-property (which I haven't done).

We're also inconsistent right now:

<!DOCTYPE html>
<div class=foo>foo</div>
<style>
.foo { width: 100px; height: 100px; border: thick dotted; font-size:
70px; animation: foo 2s infinite alternate; }

@keyframes foo {
  from { color: white; background-color: white;
-webkit-text-fill-color: currentcolor; border-color: currentcolor;}
  to { color: black; background-color: lime; -webkit-text-fill-color:
lime; border-color: lime; }
}
</style>

As you can see, border-color treats "currentcolor" as turning into
RGBA at computed-value time; as a result, it constantly tracks the
'color' value and updates its animation, just like an animation using
both "em" values and font-size would.

On the other hand, -webkit-text-fill-color treats it as turning into
RGBA at used-value time, so it's unanimatable and instead gets the
"flip at 50% behavior - in the first half of the animation it holds
the value "currentcolor" (and thus tracks 'color' as it animates from
white to black), and in the second half it holds the value "lime".
(You can see it just after the 50% transition, as the bg is still in
the middle of its white=>lime transition and is thus paler.)

If we were completely consistent, both would act as
-webkit-text-fill-color does here.  If we added a blend() function and
used it for interpolation, the reverse would happen in practice; both
would act like border-color does here.  Decide which one you want, and
we can make sure it happens.

> The spec currently requires (4), but we're hoping in the long term
> to make it something more like but slightly better than (3), where
> the animation treats currentcolor as a computed value so it doesn't
> interpolate changes from currentcolor to currentcolor when color
> changes (which is the problem that caused bug 1260543, because that
> created a -webkit-text-fill-color value that inherited to a
> descendant that specified color, and then overrode that color) but
> that it does animate between currentcolor and real color values.

This is solved by a blend() function, as I outlined above;
"currentcolor" to "currentcolor" is no change and so no transition
occurs, but "currentcolor" to "red" is animated with
"blend(currentcolor, red X%)" (even if the currentcolor was, at the
time, red).

> My main worries about compatibility risk are for the properties
> currently in group (2) and for fill and stroke in group (1), since
> if we change them to group (4) we might never be able to change them
> back, because people will depend on them not animating.
>
> I haven't tested what other implementations do here; there are a
> *lot* of cases to test.  I'm curious if other implementors can
> explain what state their implementations are in here (perhaps a

> But my general concern here is that
> the group is making potentially-breaking changes, and then adding
> dependencies on those changes, before implementations have had a
> chance to test that they're actually safe changes to make.

The currentcolor change was finally resolved on in the May 2012
Hamburg F2F, just short of *4 years ago*; it was added to the errata
some time after that.  Implementations have definitely had their
chance to check that this is safe.

Since they've instead drug their feet on making the change, it's
possible we might only *now* discover that it's unsafe, but I don't
think it's reasonable to blame the *CSSWG* for that. ^_^

(I lived in fear for several years about the "make every property
animatable, with a 50% flip if nothing better is defined" thing, but
it looks like we've been doing it right for a while, so it's probably
safe.  Whew!)

~TJ

Reply | Threaded
Open this post in threaded view
|

making incompatible changes (was Re: [css-color][css-transitions] Interpolation between currentcolor keyword and actual color value)

L. David Baron
On Friday 2016-04-22 12:25 -0700, Tab Atkins Jr. wrote:
> The currentcolor change was finally resolved on in the May 2012
> Hamburg F2F, just short of *4 years ago*; it was added to the errata
> some time after that.  Implementations have definitely had their
> chance to check that this is safe.
>
> Since they've instead drug their feet on making the change, it's
> possible we might only *now* discover that it's unsafe, but I don't
> think it's reasonable to blame the *CSSWG* for that. ^_^

I think that's because the way we've made incompatible changes was
not acceptable.

I think we should only agree to make incompatible changes
conditional on getting positive feedback within a relatively short
time period, such that the change reverts if we don't get such
feedback.  This will avoid getting into such an inconsistent state
where we spec things that depend on a change that is incompatible
with reality.

I don't think it's OK for the group to presume that giving such
feedback is a priority for implementations.

-David

--
𝄞   L. David Baron                         http://dbaron.org/   𝄂
𝄢   Mozilla                          https://www.mozilla.org/   𝄂
             Before I built a wall I'd ask to know
             What I was walling in or walling out,
             And to whom I was like to give offense.
               - Robert Frost, Mending Wall (1914)

signature.asc (836 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: [css-color][css-transitions] Interpolation between currentcolor keyword and actual color value

L. David Baron
In reply to this post by Tab Atkins Jr.
On Friday 2016-04-22 12:25 -0700, Tab Atkins Jr. wrote:

> On Fri, Apr 22, 2016 at 10:48 AM, L. David Baron <[hidden email]> wrote:
> > On Monday 2016-04-18 14:43 -0700, Tab Atkins Jr. wrote:
> >> I'm not aware of any plans to "switch currentcolor to a computed value
> >> for some cases" - can you elaborate?
> >
> > We've agreed to switch currentcolor to being a computed value.  We
> > errata'd css-color-3 to do that:
> > https://www.w3.org/Style/2011/REC-css3-color-20110607-errata.html#s.4.5
> > and made it such in css-color-4:
> > https://drafts.csswg.org/css-color-4/#currentcolor-color
>
> Ah, Xidorn's wording confused me!  In particular, if currentcolor is
> turned into an RGBA at computed-value time, then there's never a
> problem at all and animations work exactly like animating between px
> and em does; the entire issue is that we've specced it to not become
> RGBA until used-value time, which instead puts us in a "px vs %"
> situation, and we need a calc() equivalent for colors to represent the
> intermediate animated value.
>
> (I think you and he use the term "computed value" to mean something
> different than I do - I'd call "1em" a "computed value", because it's
> absolutized at computed-value time, while "width: 10%" is a "used
> value".)
I think I'm using the phrase "computed value" in its CSS meaning
("thing that is a CSS computed value"), whereas you're using it in
its English meaning to mean "thing that is computed as part of the
process of making a CSS computed value".

I think your way is confusing. :-)

> If we were completely consistent, both would act as
> -webkit-text-fill-color does here.  If we added a blend() function and
> used it for interpolation, the reverse would happen in practice; both
> would act like border-color does here.  Decide which one you want, and
> we can make sure it happens.

I'm guessing it is probably better (and generally more compatible)
to use blend(), although there are many cases I haven't tested.

(The thing that is known to cause compatibility problems is
triggering transitions for -webkit-text-fill-color when the value
changes from currentColor to currentColor (which are potentially
different values because color changes).  Treating currentColor as a
computed value consistently, including for animations, fixes this.)

> > The spec currently requires (4), but we're hoping in the long term
> > to make it something more like but slightly better than (3), where
> > the animation treats currentcolor as a computed value so it doesn't
> > interpolate changes from currentcolor to currentcolor when color
> > changes (which is the problem that caused bug 1260543, because that
> > created a -webkit-text-fill-color value that inherited to a
> > descendant that specified color, and then overrode that color) but
> > that it does animate between currentcolor and real color values.
>
> This is solved by a blend() function, as I outlined above;
> "currentcolor" to "currentcolor" is no change and so no transition
> occurs, but "currentcolor" to "red" is animated with
> "blend(currentcolor, red X%)" (even if the currentcolor was, at the
> time, red).
I think this makes getting blend() stable a priority, since I think
having blend() may block getting the currentcolor change fully
implemented.

-David

--
𝄞   L. David Baron                         http://dbaron.org/   𝄂
𝄢   Mozilla                          https://www.mozilla.org/   𝄂
             Before I built a wall I'd ask to know
             What I was walling in or walling out,
             And to whom I was like to give offense.
               - Robert Frost, Mending Wall (1914)

signature.asc (836 bytes) Download Attachment