color-mix
takes two colors and one mandatory percentage (the percentage for the other color is 100-that percentage).
From the Toronto minutes:
RESOLVED: Add color-mix(), try to align with cross-fade()
Looking at the definition for cross-fade
I notice:
This issue is to record the discrepancy. Do we want to allow multiple colors? Do we want to allow percent to be omitted (defaults to equal mix of each color?)
Yes, definitely, we should align with cross-fade()'s N-ary mixing. Otherwise mixing multiple colors requires doing some non-trivial math to reverse the desired final percentages into binary mixing amounts.
I have no opinion on "omit %s, assume equal" as a use-case, but feel strongly that we should align with cross-fade(), which allows exactly that.
Interesting to mix multiple colors! I like this idea as it relates to thinking of color like painting.
Should the syntax then be like: color-mix(red 40%, blue 20%, pink 40%)
?
What happens if these colors do not add up to 100%?
I think it would also be nice to have the option of omiting the percentage to denote an equal distribution, i.e:
color-mix(red, blue)
would mix red and blue by 50% eachcolor-mix(red, blue, pink)
would mix each by 33.333% eachTo add on, if you only have one percentage, it could look like this:
color-mix(red 40%, blue)
-- a 40% red and 60% bluecolor-mix(red, blue, pink 20%)
-- a 40% red, 40% blue, 20% pinkShould the syntax then be like: color-mix(red 40%, blue 20%, pink 40%)?
Yeah as soon as I started thinking of multiple colors, it seemed like the percentages or other modifiers needed to be grouped closely with the colors they were affecting.
I think it would also be nice to have the option of omiting the percentage to denote an equal distribution
I agree.
It should work just like cross-fade():
mix(foo 50%, bar, baz)
would be equivalent to mix(foo 50%, bar 25%, baz 25%)
).Specifically, the grammar should be:
color-mix( <<colorspace>>?, [ <<color-adjuster>>? && <<color>> ]# )
<<color-adjuster>> = <<percentage>> | hue(<<percentage>>) | ...
I moved the colorspace to the front, as that's more standard practice when a function takes both options and a list (see the shape or gradient functions).
The color-adjusters used must be consistent within a specific colorspace; you can't adjust "hue" and "red" at the same time. The colorspace in play is inferred from the adjusters used, defaulting to lch if possible. A plain % is shorthand for applying all the relevant adjusters from that colorspace.
N-ary mixing is then applied per-adjuster, using the same logic as cross-fade for omitted adjusters (taking the leftover %, and distributing it equally among the unspecified items).
Ah, sorry, actually the omitter logic is a touch more complicated - if you've specified any channel adjuster for a color, the omitted adjusters default to 0%. You only distribute leftover % if there's no adjuster at all.
That is, if we write color-mix(canvas, var(--bg-color) a(100%))
to apply the variable's alpha to the canvas
color, we want the rest of the var's adjusters to default to 0% (so you use canvas
for all the actual color channels).
I plan to try to integrate @tabatkins suggestion in the next week, so if anyone has comments r corrections against the proposal now is a good time.
it's awesome / lovely 👍
@svgeesus Would be good to get these edits in (and WD updated), given Blink already sent out intent to prototype.
@fantasai Hue interpolation becomes very complicated with more than 2 colors, so the change is non trivial. However, Blink only intends to prototype this in srgb anyway, and the syntax can always be extended to support multiple colors.
I will get in the syntax edit of allowing a percentage per color that makes the syntax extensible, but extending it to multiple colors is not something we can do yet without discussion.
As @svgeesus pointed out in #5277, before we dive into the specifics of how to make this work, we should first see if there are any actual use cases for multiple colors. @una @argyleink what use cases did you have in mind?
? mix against white then add some complimentary color for additional contrast
? lightening || darkening then cooling || warming
? fun color-mix(var(--c1), var(--c2), var(--c3))
Just a thought that might simplify implementation:
What if color-mix
only took 2 colors and a percentage for the mixing?
i.e. if color-mix(red 60%, blue)
means 60% red and 40% blue, then:
color-mix(color-mix(red 60%, blue), pink 40%)
means 60% of the total is a mixture of 60% red and 40% blue, the rest of 40% pink. Overall values would be: 36% red, 24% blue, 40% pink.
Most helpful comment
Specifically, the grammar should be:
I moved the colorspace to the front, as that's more standard practice when a function takes both options and a list (see the shape or gradient functions).
The color-adjusters used must be consistent within a specific colorspace; you can't adjust "hue" and "red" at the same time. The colorspace in play is inferred from the adjusters used, defaulting to lch if possible. A plain % is shorthand for applying all the relevant adjusters from that colorspace.
N-ary mixing is then applied per-adjuster, using the same logic as cross-fade for omitted adjusters (taking the leftover %, and distributing it equally among the unspecified items).