Re: [csswg-drafts] [css-transitions] Transition to height (or width) "auto" (#626)

Oooh, indeed, that's a great use-case.

Okay, after thinking about it more, I'm completely convinced this is the way to go. A little more detail:

# Proposal

* `calc-size()` is a math function, but does not have a resolved type (it's an intrinsic length, which is only allowed by special permission in certain places; we'll explicitly add it to property grammars).
* The first arg (its **basis**) can be:
 *  any intrinsic sizing keyword; the value must be valid in the property calc-size() is used in.
 * a calc-size(), to generalize for var() usage. During simplification you just collapse it down. Aka `calc-size(calc-size(fit-content, size + 20px), size * .5)` simplifies to `calc-size(fit-content, (size + 20px) * .5)`.
 * the keyword `any`, to generalize for interpolation with unknown values. It disallows use of the `size` keyword in the calculation. So `calc-size(any, 0px)` is valid, but `calc-size(any, size * .5)` is invalid.
 * a `<length-percentage>`, for both var() and interpolation generalization. This doesn't simplify; the `size` keyword just resolves to whatever the `<length-percentage>` does. If, for example, percentages are cyclic in the context and "behave as auto", then `size` is the length you'll get from the "behaves as auto" behavior.
  * See the "how percentages work" section later in this post for some explanation here.
* The second arg (its **calculation**) is a calculation that has access to the `size` calc keyword, which resolves to the length indicated by the first argument. 
 * If percentages are used in the calculation, and percentages aren't resolved against a definite size in this context, they resolve to zero in the calculation. Again, see "how percentages work" later for details.
* For the purposes of anything that cares whether a property is intrinsic/definite/etc, a calc-size() function behaves like its basis would.
 * In particular, this implies that `calc-size(50%, size)` is definite if `50%` would be definite in that context.

## Interpolation

* You can interpolate two calc-size() functions if, after simplification:
 * both basises are the same intrinsic keyword. The result has the same basis.
 * both basises are `<length-percentage>`s. The result's basis is the interpolation of the two input basises.
 * one basis is `any`. The result has the other input's basis.
* The calculations are always interpolated normally.
* You can (potentially) interpolate a calc-size() with any other sizing value; it "upgrades" to `calc-size(<value>, size)`, and interpolates accordingly if allowed by the first bullet point.

## How percentages work

I gave %s distinct behavior when they're used in the basis vs in the calculation. There's a good reason, and as far as I can tell it's necessary to satisfy some reasonable constraints.

Namely, I want to ensure that, in *any possible circumstance*, it's always possible to:

1. take an existing length in a custom property, and smoothly animate between multiples of it, like `@keyframes foo { from { height: calc-size(var(--foo), size * .8); } to { height: calc-size(var(--foo), size * .2); }}`
2. smoothly animate to zero (or any definite length), like `@keyframes foo { to { height: calc-size(any, 0); }}`.

This has some implications:

1. `height: 50%;` might be resolved against an indefinite size, and "behave as auto". I need to be able to represent the halfway point between whatever that is, and zero, so animation works correctly.
2. But I don't want to, and can't in the general case, resolve such a thing against another sizing keyword, as that would mean invoking two different intrinsic sizing algorithms. So `height: calc-size(fit-content, size + 20%);` needs to do *something* special that does not invoke "behaves as auto" percentage sizing.
 * And it can't just make the whole thing "behave as auto" either. That defeats the "animate between multiples" use-case, as it means that `calc-size(calc-size(fit-content, size + 20%), size + 50px)` doesn't work - the inner calc-size() would resolve to a "behaves as auto" size, then the outer calc-size() would... also do so, rather than being 50px larger. 

So percentages have two fundamentally opposed requirements for their behavior. The only way to resolve that is to give them different behaviors in the basis vs the calculation. The "smoothly animate to zero" behavior can be accommodated by taking a percentage in the basis, and having it "behave as auto" when necessary. The "animate between multiples" behavior (and the related "stack a calc-size() in a calc-size()" case) can be handled by resolving cyclic %s to *some* definite value (rather than infecting the whole expression), and the only reasonable arbitrary value is 0.

This makes the following cases work reasonably:

* animate from `calc-size(any, 0px)` to `50%`: whether the % is cyclic or not, it'll smoothly animate.
* `--foo: calc-size(fit-content, size + 20%); height: calc-size(var(--foo), size + 50px);`. whether the % is cyclic or not, this is identical to writing `height: calc-size(fit-content, size + 20% + 50px);`

But it also has a few unfortunate implications:

* There is a meaningful difference between `calc-size(50%, size)` and `calc-size(any, 50%)`: if the % is definite, they're identical, but if the % is cyclic, the first resolves to the "behaves as auto" size while the second resolves to zero. 
* the upgrade rules for interpolating a calc-size() with a length-percentage have to upgrade the latter to `calc-size(<len-per>, size)` rather than `calc-size(any, <len-per>)`, so it can smoothly interpolate to zero even when the percentage is cyclic. This means you cannot interpolate `calc-size(fit-content, size)` with `50%`; you have to write the second value as `calc-size(any, 50%)` explicitly.

I think this design is the best compromise between these conflicts; everything else appears to have significantly worse behavior in some reasonable cases, and don't satisfy my two requirements from the start of this section.

-- 
GitHub Notification of comment by tabatkins
Please view or discuss this issue at https://github.com/w3c/csswg-drafts/issues/626#issuecomment-1800254442 using your GitHub account


-- 
Sent via github-notify-ml as configured in https://github.com/w3c/github-notify-ml-config

Received on Tuesday, 7 November 2023 22:02:44 UTC