- From: Tab Atkins Jr. via GitHub <sysbot+gh@w3.org>
- Date: Sun, 16 Feb 2025 17:50:14 +0000
- To: public-css-archive@w3.org
After talking thru the problem space and existing functions again, @fantasai and I have come up with an amended proposal for the `*-mix()` family of functions. As the `*-mix()` stuff hasn't been implemented yet, ripping it all out should be fine. ^_^ The fundamental disconnect is that there are two distinct notions of "mixing" being employed in these conversations, which look basically identical when you only have two values but are cleary distinct when you have 3+: are you "mixing" multiple values into one according to varying percentages, or are you "interpolating" (we'll use the term "mapping") between multiple breakpoints, with only two values being mixed at any given time? These two require completely different approaches when there are 3+ values. You *can* emulate either behavior with the other, but it's somewhere between annoying and untenably complicated. # Mixing Functions The existing `color-mix()` and `cross-fade()` functions are the former: an unbounded number of values all combined with varying weights. As these are already implemented, we chose to leave them alone, and let the more general category of "functions like this" be called `*-mix()`. The general `*-mix()` grammars will follow the pattern: ``` foo-mix( <foo-specific-options>, [ <output-value> && <percentage>? ]# ) ``` `<foo-specific-option>` are things like `<color-interpolation-method>` for `color-mix()`. Not all functions will have that. The `<output-value>` is specific to the function too; for `color-mix()` it's `<color>`. If the value type isn't distinguishable from `<percentage>` (like in `calc-mix()`), then we remove the `&&`, requiring the percentage to be second. If any percentages are missing, they equally distribute the leftover percentage needed to add to 100% (floored at 0%); if the they add up to more than 100%, we rescale them to equal 100%; if they add to less than 100% we imply an additional "neutral" value with the remaining %. (For `color-mix()` it's `transparent`; for `cross-fade()` it's a transparent image with no natural dimensions; for `calc-mix()` it would be zero in the appropriate unit; etc) # Mapping Functions For the "multiple breakpoints" case, we propose to introduce a `*-map()` family of functions. The existing [gradient functions](https://drafts.csswg.org/css-images-4/#color-stop-syntax) and [`linear()`](https://drafts.csswg.org/css-easing-2/) provide examples of this concept, but to accomodate more arbitrary values we have to generalize it a bit: ``` foo-map(<progress-and-top-level-options>, <stop>, [ [ <interpolation-options>, ]? <stop> ]#?) ``` expanding these out it looks like this: ``` foo-map(<progress-source> && [by <easing-function>]? && [each <easing-function> ]? && <foo-specific-option>?, <progress-value>{1,2} : <output-value> , [ [ <easing-function> || <foo-specific-option> , ]? <progress-value>{1,2} : <output-value> , ]#? ) ``` where for the top-level options: * `<foo-specific-option>` is same as for `*-mix()`: `<color-interpolation-method>` for `color-map()`, etc * `<progress-source>` is a `<number>` or dimension, or a literal percentage (treated as an equivalent number) * `by <easing-function>` eases raw progress-source value directly, using the first/last stops to define the [0,1] range it's eased over (`linear`, if omitted) * `each <easing-function>` gives a default for the easing function between stops (`linear`, if omitted) so you don't have to repeat it each time and for the stop list: * an `<easing-function>` between stops gives the easing behavior just between those stops (and can have function-specific options, too, like a `longer hue` just between two stops while the rest use the default) * `<progress-value>` is of the same type as the `<progress-source>`. Literal percentages are allowed (`100%` treated the same as `1`). * two `<progress-value>`s in one clause are same as in gradients or linear(): they provide two stops with the same output value, creating a stretch with a constant value To map across media/container queries, we introduce new media/container functions that return lengths/etc (in addition to the previously adopted `media-progress()`/etc, which turn the length/etc into a progress number 0-1): * E.g. `media(<media-feature>)`, `container(<container-feature>)` * These allow using the start/end/stop values directly in `*-map()`'s stop lists, instead of requiring conversion to percentages up front. Some use cases will prefer using the feature output directly, while others might prefer conversion to percentages using the `-progress()` variants. For example, both of these are valid/allowed: ``` color: color-map(media-progress(width, 0px, 2000px), 0%: red, 100%: blue); /* allows putting the percentage scale into a variable, but awkward to read/write literally, especially with 3+ stops */ color: color-map(media(width), 0px: red, 2000px: blue); /* easier to read/write literally, but can't extract the scale into a variable */ ``` Regarding the generic whole-value `mix()` function, we're proposing to: * Rename it to `map()`, and give it the general `*-map()` syntax. * Retain the alternative syntax `<progress-source> of <'animation-name'>` for `map()`, which takes the progress/output clauses from @keyframes (and synergizes nicely with #10000) -- GitHub Notification of comment by tabatkins Please view or discuss this issue at https://github.com/w3c/csswg-drafts/issues/6245#issuecomment-2661545284 using your GitHub account -- Sent via github-notify-ml as configured in https://github.com/w3c/github-notify-ml-config
Received on Sunday, 16 February 2025 17:50:15 UTC