Open LeaVerou opened 5 months ago
I can see advantages (more generally useful) and disadvantages (cumbersome syntax) to the second option. I'm not sure which is the better option, 1 or 2.
The functionality seems very useful, clearly.
All the examples omit the optional percentage for stop position; it would be good to add an example that explicitly uses stop positioning.
All the examples omit the optional percentage for stop position; it would be good to add an example that explicitly uses stop positioning.
The success-failure scale does.
As we noticed privately, I posted essentially the exact same idea just a few hours prior to yours (decent chance we overlapped in authoring the texts!) in https://github.com/w3c/csswg-drafts/issues/9992#issuecomment-1979797500.
Notable differences are:
If stops can have an implicit position, then you have to rely on numeric precision to allow overriding to work. Like, given 0 red, orange, yellow, 100 green
, if you wanted to override the orange
stop you'd have to write calc(100 / 3) salmon
and hope it worked.
As I wrote in my reply today:
progress()
makes it easy to apply it externally. I think that's more of a nice to have, and not that important to address. It also seems to be introducing a new alternative color stop syntax, when we already have a color stop syntax for exactly this very thing, which also means it's much harder to generate a gradient from the color scale (either as actual output, or for debugging)I'm sorry, I'm not sure I understand what most of these replies have to do with what I was saying. I suspect you're reading some additional stuff I didn't say into my post.
The design system user doing the overriding, e.g. the red 900 tint is too dark for my liking, I’ll make it a bit lighter. That is far lower priority.
In #9992 this seemed to be a relatively important part of the proposals for you. Why is it so much lower priority here?
I don't see why we'd bake the specific tint levels into the ramp. [...]
I'm not sure what this entire paragraph is in reference to.
From experience, having to adjust the other colors when inserting a color is incredibly annoying and one of the things I was trying to avoid with this proposal.
I'm not sure how what I said in my most recent reply has any bearing on this. Can you elaborate on what problem you're seeing here?
I do agree that having to match the exact percentage by precision is annoying, but we should not let complex cases get in the way of common ones. We can continue to discuss how to best design a syntax that allows overriding without this problem.
As I said, the end user being able to override a specific color choice from a design system's color scale seemed to be an important thing in your comments in #9992. What's different about this situation, where the color scale is encapsulated into a value rather than being implicit across multiple custom properties?
I was reminded about this when I came across https://noahliebman.net/2024/04/recursion-in-the-stylesheet/ . These are the lengths authors have to go to today to achieve this.
Here’s my attempt at a green-yellow-orange-red scale without a preprocessor: https://codepen.io/leaverou/pen/ZENWMZr?editors=1100 Yay quadratic manual work!
After thinking about it some more, I don’t think overriding individual colors is something that should drive the design of this feature, and certainly not something that warrants complicating the base case. Authors can always override the individual colors used to generate the scale, and design systems can make sure to include enough extension points if they want to enable this.
I think we should keep the MVP as simple as possible. This means:
color-mix()
. Having two functions puts us in the awkward position of having to specify what color-scale()
returns.linear-gradient(to right, ...)
and see exactly the same colors you’d get from the scale.Something like this:
<color-scale()> = color-scale ( at <percentage> <color-interpolation-method>?, <abstract-color-stop-list> )
<abstract-color-stop-list> = <abstract-color-stop> , [ <abstract-color-hint>? , <abstract-color-stop> ]#
<abstract-color-stop> = <color> <percentage>{0,2}
Which would look like this:
--scale: in oklch, var(--color-green), var(--color-yellow) 40%, var(--color-orange) 60%, var(--color-red));
background: color-scale(at var(--progress) var(--scale));
I’m in two minds about whether the position you are selecting should be part of the preamble like above, slash separated, or even a separate argument.
One consideration is that while colors are possibly the most pressing need, length scales are also in wide usage (example). Whatever syntax we come up with should be consistent across different types of scales.
I think the syntax above does lend itself nicely to calc-scale()
(except hints can't be allowed there as there's no way to tell if they are hints or actual arguments).
During the discussion in #9992 it occurred to me that one of the things that could really help simplify the color-related design systems use cases would be a way to define a gradient line and pick a color on it.
Why?
color-mix()
is not very friendly to that. Super common example: the failure-success scale of red, orange, yellow, green. Yelp ratings are a popular application (and no, this is not just a simple polar interpolation between the endpoints):linear-gradient()
.Syntax
Option 1: Single function for both defining the scale, and selecting a color
Example usage:
This is basically modeled after
linear-gradient()
with the non relevant parts removed (lengths,to <whatever>
, angles). It could also allow<1d-image>
/stripes()
to facilitate discrete scales.The reason the percentage is separated from the rest with a
/
is to facilitate storing the actual scale part into variables and passing them around without having to worry about whether you need to specify a comma or not (depending on whether the scale has a<color-interpolation-method>
).Pros:
Option 2: Separate scale definition and color selection
This syntax decouples the scale from the color selection. It seems more conceptually sound, but also too many parens.
Example:
the parens could be reduced if it would be possible to define tokens like:
Example:
but I suspect @tabatkins will have a good reason to rule that out 😁
We could also make it a variant of
color-mix()
:Example:
Though since conceptually we're not mixing anything, I don't think this is worth it.
More Examples
Yellow tints that skew oranger when darker
Option 1:
Transparent variations of a base color
Option 1:
Option 2:
Success/failure scales
This is super common to communicate varying levels of success/failure. There are two main forms: red - orange - yellow - green - dark green, or red - white - green. E.g. see screenshot from Coda’s conditional formatting:
Especially the red - orange - yellow - green scales almost always require manual correction, and cannot be done with a single 2 color interpolation (yes not even in polar spaces). With
color-scale()
they can be as simple as: