Open johannesodland opened 2 years ago
mix()
doesn't clamp (#6701), so this probably shouldn't either.
And could also work with calculation values other than lengths.
I guess this issue would be solved if we could have custom mathematical functions
Having the ability to reproduce it by hand doesn't necessarily preclude us adding it in natively, if it's sufficiently useful and the manual reproduction is sufficiently annoying. I think this qualifies.
I agree this should be easier.
Some thoughts that might help with naming/syntax:
Both mix()
and the proposed progress()
are basically linear range mapping operations. To take the example above:
--progress: progress(100vw, 375px, 1920px);
font-size: mix(var(--progress), 24px, 32px);
progress(100vw, 375px, 1920px)
maps the [375px, 1920px] range to the [0, 1] range, and mix(var(--progress), 24px, 32px)
maps the [0, 1] range to the [24px, 32px] range.
If we have ad hoc functions like that, I think it should somehow be obvious from naming that one is the inverse of the other: mix()
interpolates (takes a percentage and gets you the corresponding value), and progress()
does the opposite (takes a value and gets you the corresponding percentage). While I like progress()
as a name, with the proposed naming they seem like entirely separate things. Perhaps mix-reverse()
or mix-inv()
or something? (tempted to suggest xim()
and duck 🤣)
But what if we exposed the actual range mapping operation? We could have a map()
function that takes two ranges and a value and maps the value from one range to the other. This would basically give us the result above in one fell swoop, without the intermediate percentage:
font-size: map(100vw in 375px to 1920px into 24px to 32px);
or
font-size: map(100vw in [375px, 1920px] to [24px, 32px]);
or something (syntax TBB). Perhaps we could even have shortcuts to facilitate the common case where you want percentages as input or output.
Note that these could also be stored in variables to be reused for every conversion:
:root {
--page-widths: [375px, 1920px];
}
/* ... */
font-size: map(100vw in var(--page-widths) to [24px, 32px]);
or even:
:root {
--page-width-progress: 100vw in [375px, 1920px];
}
/* ... */
font-size: map(var(--page-width-progress) to [24px, 32px]);
While I like
progress()
as a name, with the proposed naming they seem like entirely separate things. Perhapsmix-reverse()
ormix-inv()
or something?
I agree that the name should reflect that this function is the inverse of mix()
, like asin()
is the inverse of sin()
.
mix-inv()
or inverse-mix()
or something similar would be great 👍
(tempted to suggest
xim()
and duck 🤣)
🤣
But what if we exposed the actual range mapping operation? We could have a
map()
function that takes two ranges and a value and maps the value from one range to the other. This would basically give us the result above in one fell swoop, without the intermediate percentage:...
font-size: map(100vw in [375px, 1920px] to [24px, 32px]);
A range mapping function would be great!
At the moment mix()
does linear interpolation, and map()
would do linear range mapping.
But; there's an open issue on adding easing functions to calc (https://github.com/w3c/csswg-drafts/issues/6697).
Easing could let us do something like
--progress: inverse-mix(100vw, 375px, 1920px);
font-size: mix(var(--progress) ease-in-out, 1rem, 1.25rem)
It would be great to be able to use easing functions in map as well, so the syntax should have room for extending in the future:
map(100vw in [375px, 1920px] to [24px, 32px] ease-in-out)
Also, while a map()
function would be great, separate mix()
and inverse-mix()
functions would be composable with other functions.
--progress: inverse-mix(100vw, 375px, 1920px); font-size: mix(var(--progress) ease-in-out, 1rem, 1.25rem)
I seem to have provided an example where inverse-mix
would not necessarily be the inverse of mix()
🤦😅
Just some notes:
progress()
or whatever it's called should probably provide the input progress value, typically in [0, 1], but actually [-∞, ∞].mix()
is top-level. Something else will be needed for component values, see #6700. I think progress()
should also be for component values, so names like inverse-mix()
can be misleading.progress()
can only be the inverse when the easing function is injective (e.g. linear). With the ability to specify the easing function, if it's not injective, we can't have a global inverse. It's not clear which of the possible input progress values should be returned. Also, easing functions may not be surjective (e.g. steps()
), so what should progress()
return for values outside the domain? I think it may be better to just avoid easing functions in progress()
and always use linear.Yes, just determining the interpolation progress within a range, in a linear fashion, seems sufficient, and avoids all the complexities and incongruities of trying to invert an easing function, which as you note is not in general an invertable function!
It might be useful to provide a way to invert the easily-invertible easing functions that we have. I believe the inverse of a cubic-bezier(x1, y1, x2, y2)
, where 0≤y1
≤1 and 0≤y2
≤1, is cubic-bezier(y1, x1, y2, x2)
.
Note that cubic-bezier(1,0,0,1)
in only invertible in [0,1]. I think y1 > 0
and y2 < 1
are also needed for the general case.
There are discussions in https://github.com/w3c/csswg-drafts/issues/6245 for alternative ways of interpolating values between breakpoints. I still think a ‘progress’ or ‘position-of’ function would be useful for individual properties and more advanced calculations.
Seems like progress()
is in the css-values-5 ED now: https://drafts.csswg.org/css-values-5/#progress-func
In range input, progress has different calculation modes. In the following example, the right edge of progress is always in the middle of the thumb.
This prevents the rounded corner radius from changing and overflowing when dragged to the far left.
https://github.com/w3c/csswg-drafts/assets/2784308/f366da0f-346a-49df-a9de-57a32759c7df
With clamp()
, you can see that the width of progress is not growing evenly, and I want the tooltips above to always be centered horizontally with the thumb.
.slider-progress {
--p1: calc((var(--range) - var(--range-min)) / (var(--range-max) - var(--range-min)));
--w1: clamp(28px, calc(100cqi * var(--p1)), calc(100cqi - 28px));
}
https://github.com/w3c/csswg-drafts/assets/2784308/5b865be7-e6ad-420c-9cbf-d87ea8e0f2a5
My question is, how can I implement such an algorithm using clamp()
or progress()
? I think this is the scenario that progress()
should consider.
@fantasai @mirisuzanne Could this issue be closed now that progress()
is already specced in css-values-5?
While it's in the Editor's Draft, I don't see any official resolution from the WG. We would need to get that approval before we publish a new Working Draft (and close the issue). Seems the last time we brought it up (in #6245) was just to introduce the proposal, without asking for a resolution. If we think we're ready for a resolution, we could bring one of these issues back to the group.
@andruud or @danielsakhapov - I think you were exploring a prototype at one point, and had some questions that we might want to address? Are those documented as issues anywhere?
@mirisuzanne I don't think we've found anything bad, apart from the fact that browsers will have a lot of work to implement it for non-length accepting properties, since none of them (my estimation) are ready to deal with cases like - number-producing math function with relative units dependency (I guess you can see it now with sign() function here). But that's not a spec problem, just a delay for ship. I've already started fixing it for Chrome.
@mirisuzanne I think we should probably change this part:
The computed value of a
and have it compute to the actual (numeric) progress of the timeline instead (post-easing). We generally resolve things at the earliest point they can be resolved, and I'm not sure what we gain from delaying the computation here except increased implementation difficulty.
The proposal is to add a new function to css-values-4 that calculates the progress between two
<length>
values in percent. The new function could be used together with the newmix
function to improve readability of fluid sizes.The syntax of the progress functions could be as follows:
All arguments are
<length>
values, while the return value would be a<percentage>
value representing the progress of the input between the min and max values. Progress would be clamped between0%
and100%
.The function could then be used together with
mix()
to calculate fluid sizes:This would work well with container query units as well
The proposed function is not new functionality, but would be syntactic sugar for
Name to be bikeshedded.
Motivation
Fluid typography has been around for years, but the css necessary to achieve it is hard to create and read.
The
clamp()
function makes it possible to create fluid typography with less bloat: Example copied from article above:It is still complicated to calculate the slope and intercept values necessary to create a fluid type that scales from a minimum to a maximum viewport size. It is not possible understand the min and max viewport sizes from reading the notation.
Preprocessors or other tools can be used to calculate the values, but this does not improve the readability.
The new mix funcion could let us interpolate between two font-sizes, as long as we have a progress argument: