Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[css-color-5] What should the behavior of the CSS Color 5 color functions be when passed currentcolor as <color> #6168

Closed
weinig opened this issue Mar 31, 2021 · 23 comments

Comments

@weinig
Copy link
Contributor

weinig commented Mar 31, 2021

What should the behavior (both rendered result and computed value) be for the CSS Color 5 color functions (e.g. color-mix(), color-contrast(), color-adjust() and the relative color syntax forms) when one of the passed in values is currentcolor (https://drafts.csswg.org/css-color-4/#currentcolor-color)?

For rendering, I assume we just need to delay computation of the color functions until use time when currentcolor is used and use the resolved value, but it would be good to clarify that in the spec if it is the case.

For computed value I am not quite sure since none of the color functions currently have anything about their computed value's specified. Should:

background-color: color-mix(in lch, currentcolor 40%, palegoldenrod)

have a computed value of color-mix(in lch, currentcolor 40%, palegoldenrod)?

In my current implementation, I am eagerly computing the color-mix(), so in all cases that don't involve currentcolor it computes to the resolved color, but this is just an arbitrary choice I made, and I do not think should motivate the direction of the spec.

@emilio
Copy link
Collaborator

emilio commented Mar 31, 2021

FWIW, in Gecko we resolve all colors at computed value time to a mix of currentcolor and a foreground color (like this). That allows us to resolve the functions at computed-value time. So my implementation of color-mix() also computes to the resolved color.

I think that's generally preferable as I'd rather not do a lot of color mixing during rendering for every element if we can do it once and let it inherit through.

@weinig
Copy link
Contributor Author

weinig commented Mar 31, 2021

@emilio, I can't quite understand what you are proposing here. What would the computed and used values of something like the following be:

background-color: color-mix(in lch, currentcolor 40%, palegoldenrod)

@tabatkins
Copy link
Member

Yeah, I'm a little confused too, particularly with the other resolutions about currentcolor varying per-fragment.

Ignoring any discussion that's gone on in this thread, tho, my intuition is that we'd use the same logic (and can just copy over the text with light modifications) as the math functions - mixing functions resolve as far as possible at computed-value time, and resolve the rest of the way at used-value time if needed.

@weinig
Copy link
Contributor Author

weinig commented Mar 31, 2021

@tabatkins that makes sense to me.

@weinig
Copy link
Contributor Author

weinig commented Mar 31, 2021

(sidenote, @emilio, is the behavior of using part of using part of currentcolor and part of color for a final color value specified anywhere? I don't believe that is something we do in WebKit, and would interested to find out what that is all about).

@fantasai fantasai added the css-color-5 Color modification label Mar 31, 2021
@robertwbradford
Copy link

I was wondering a similar thing. For example, we are maintaining a component library. In one of the components, we would like one of its elements to have a semi-transparent background color based on the currentcolor. Something like the following:

<div class="wrapper">
  <div class="my-component">
    <p>Some text</p>
    <div class="my-component__semi-transparent-thing"></div>
  </div>
</div>
.wrapper {
  background: darkslateblue;
  color: white;
}

.my-component__semi-transparent-thing {
  background-color: rgb(from currentcolor r g b / 50%);
}

We would like to use currentcolor because from the component's perspective, it doesn't necessarily know its context. Of course we could use a CSS custom property, but with currentcolor working so well elsewhere, it would be nice to use it here too.

@emilio
Copy link
Collaborator

emilio commented Aug 12, 2021

Sorry I missed this ping before.

@weinig @tabatkins resolving these at computed value time would break currentcolor when inheriting.

My point is that something like color-mix(in lch, currentcolor 40%, palegoldenrod) should somehow preserve the currentcolor component in the computed value, otherwise descendants that change color will get the wrong color.

Here's a test-case that shows what I meant. The expected output is greenish, but if you resolve currentColor at computed-value time, then you will get red.

I think Firefox's behavior is correct here (WebKit doesn't support color-mix(..., currentColor, ...) afaict)?

<!doctype html>
<style>
  div {
    width: 100px;
    height: 100px;
  }
  .outer {
    background-color: color-mix(in srgb, currentcolor 90%, red 10%);
    color: red;
  }
  .inner {
    background-color: inherit;
    color: green;
  }
</style>
<div class="outer">
  <div class="inner"></div>
</div>

@weinig
Copy link
Contributor Author

weinig commented Aug 12, 2021

I think Firefox's behavior is correct here (WebKit doesn't support color-mix(..., currentColor, ...) afaict)?

Correct, in WebKit we currently just don't accept currentColor in the CSS 5 color functions at all. I was waiting for the resolution of this issue before going forward with it.

I agree it should follow normal currentcolor inheritance rules for rendering (I say as much in the first comment).

What I am not sure is what the computed value should be. If it should preserve the complete "color-mix(..., currentColor, ...)" that would be fine with me, it just needs to be spec'd.

@emilio
Copy link
Collaborator

emilio commented Aug 12, 2021

I think the computed value of this is not currently visible, because getComputedStyle actually resolves currentColor, right? I guess it could be observable in typed-om?

@tabatkins
Copy link
Member

@weinig @tabatkins resolving these at computed value time would break currentcolor when inheriting.

Right, which is why we didn't say to do that. ^_^

As far as I can tell, we're agreeing - resolve the function as far as possible at computed-value time (so you're left with just the parts that depend on currentcolor, plus any additional fully-resolved bits that'll combine with it), then finish resolving, if necessary, at used-value time. Just like math functions, like I said ^_^


Yeah, I think gCS() returns used values for all properties with colors (but I'm not 100% certain on that), but Typed OM should be returning the actual computed value.

I think I'm fine with just returning the fully unresolved representation when a currentcolor is present? Trying to figure out exactly how much simplification can be done, like we do with math functions, sounds unfun, even if the UA has done that work under the covers to avoid duplicate work later.

@svgeesus
Copy link
Contributor

svgeesus commented Sep 1, 2021

Typed OM should be returning the actual computed value

Yes

I'm fine with just returning the fully unresolved representation when a currentcolor is present?

That seems way simpler to specify. It isn't just currentcolor right, also amount of the un-deprecated system colors too?

@tabatkins
Copy link
Member

Yup, they also persist thru to used-value time for the same reason as currentcolor.

@svgeesus
Copy link
Contributor

svgeesus commented May 31, 2022

That seems way simpler to specify. It isn't just currentcolor right, also amount of the un-deprecated system colors too?

That seems way simpler to specify. It now is just currentcolor due to the resolution of

svgeesus added a commit that referenced this issue May 31, 2022
…tColor; add resolved section for color-mix(). See #6168
@svgeesus
Copy link
Contributor

@weinig wrote:

What I am not sure is what the computed value should be. If it should preserve the complete "color-mix(..., currentColor, ...)" that would be fine with me, it just needs to be spec'd.

I just clarified that in the spec, following the suggestions from yourself, @emilio and @tabatkins :

The computed value is the specified ''color-contrast()'' function
with each <<color>> parameter resolved according to [[css-color-4#resolving-color-values]],
and the keywords ''AA'', ''AA-large'', ''AAA'', ''AAA-large'' replaced with their corresponding numeric value.

If all <<color>> parameters resolve
to the corresponding colors in their respective color spaces,
the used value is the winning color
resolved according to [[css-color-4#resolving-color-values]].
Otherwise (if ''currentColor'' was used in the function),
the used value is the same as the computed value
thus preserving inheritance into child elements.

I also changed the example, to now do what the text says.

I also added a new section on resolving color-mix(), including what to do with currentColor:

The computed value is the specified ''color-mix()'' function
with each <<color>> parameter resolved according to [[css-color-4#resolving-color-values]].

If all <<color>> parameters resolve
to the corresponding colors in their respective color spaces,
the used value is the mixed color,
in the specified mixing color space,
resolved according to [[css-color-4#resolving-color-values]].
Otherwise (if ''currentColor'' was used in the function),
the used value is the same as the computed value
thus preserving inheritance into child elements.

@emilio
Copy link
Collaborator

emilio commented May 31, 2022

@svgeesus I think you need to replace used value in there by computed value? Otherwise it's confusing. That is:

If all <<color>> parameters resolve to the corresponding colors in their respective color spaces, the computed value is...

I think in order to deal with nested color-mix() you need to also move color-mix() to the "resolving color values" section, or somehow hook into that.

Also, it's a bit unfortunate that the "resolving color values" section is called like that, because CSS has the concept of "resolved value", which is not necessarily what that is describing, but that's another matter :-)

@emilio
Copy link
Collaborator

emilio commented May 31, 2022

But anyways, great we are in agreement!

@svgeesus
Copy link
Contributor

svgeesus commented Jun 1, 2022

@emilio so, the computed value isn't the function but is the mixed result (unless currentColor is involved)?

I think in order to deal with nested color-mix() you need to also move color-mix() to the "resolving color values" section, or somehow hook into that.

That is where the text I added above is located: 7. Resolving <color> Values

Also, it's a bit unfortunate that the "resolving color values" section is called like that, because CSS has the concept of "resolved value", which is not necessarily what that is describing, but that's another matter :-)

I named it that because of the similar section in CSS Color 4, which has been there a while: [4.7. Resolving Values](https://drafts.csswg.org/css-color-4/#resolving-color-values).

What should these sections be called?

@svgeesus
Copy link
Contributor

svgeesus commented Jun 1, 2022

To have a concrete example to discuss (initial value of text-emphasis-color is currentColor)

div { color: black; }
p { color: color-mix(in oklab, currentColor 60%, white 40%); }
em { text-emphasis: dot; }
strong { color: red; }
<div><p><em>Some <strong>really</strong> emphasized text.</em></p></div>

so on the p and em, the used value of color will be the result of color-mix(in oklab, black 60%, white 40%); which is rgb(28.06% 28.06% 28.06%)

and on the strong it will be the result of color-mix(in oklab, red 60%, white 40%); which is rgb(100% 55.11% 48.29%) right?

CodePen by @LeaVerou to calculate color-mix(), using Mavo and color.js.

@svgeesus
Copy link
Contributor

svgeesus commented Jun 1, 2022

image

@css-meeting-bot
Copy link
Member

The CSS Working Group just discussed [css-color-5] What should the behavior of the CSS Color 5 color functions be when passed currentcolor as <color>, and agreed to the following:

  • RESOLVED: specified style maintains calculations, computed style computes everything as far as possible (but maintains currentColor's identity), resolved color resolves through to absolute color
The full IRC log of that discussion <fantasai> Topic: [css-color-5] What should the behavior of the CSS Color 5 color functions be when passed currentcolor as <color>
<fantasai> github: https://github.com//issues/6168
<astearns> ack chris
<fantasai> chris: I think we solved this in the issue, iterated the text with emilio
<fantasai> emilio: for context with the group, it behaves like calc() - doesn't go away in specified style
<fantasai> emilio: goes away in computed style if possible, but preserves currentColor
<fantasai> emilio: but this is only observable in typedOM because getComptedStyle returns resolved colors
<fantasai> RESOLVED: specified style maintains calculations, computed style computes everything as far as possible (but maintains currentColor's identity), resolved color resolves through to absolute color
<fantasai> chris: I believe this also resolves item 5 on the agenda,
<fantasai> https://github.com//issues/7302

@mysteryDate
Copy link

mysteryDate commented Oct 10, 2022

From this conversation, for serialization I'm assuming that:

const div  = document.createElement('div');
document.body.appendChild(div);

div.style.color = "red";
div.style.backgroundColor = "color-mix(in srgb, currentColor, magenta)";

div.style.getPropertyValue("color")); // 'red'
div.style.getPropertyValue("background-color")); // 'color-mix(in srgb, currentColor, magenta)'

getComputedStyle(div)['color']; // 'rgb(255, 0, 0)'
getComputedStyle(div)['backgroundColor']; // 'rgb(255, 0, 127)'

Is that all correct? If so, we are missing tests in the color-mix-valid and color-mix-invalid wpt tests. I'm happy to add these tests if I'm correct in my assumptions.

@emilio
Copy link
Collaborator

emilio commented Oct 10, 2022

@mysteryDate I don't recall whether we decided that color-mix serialized to rgb or to color() when computed, but I think it might be the later.

@svgeesus
Copy link
Contributor

I don't recall whether we decided that color-mix serialized to rgb or to color() when computed, but I think it might be the later.

Yes, this is described here so the computed value is color(srgb r g b)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

8 participants