#955: CSS calc-size() function
Discussions
Discussed
May 1, 2024 (See Github)
We have several concerns with the design of this function:
-
If
calc-size()
returns a<length>
, what prevents authors from doing something likecalc( ( calc-size(min-content) + calc-size(max-content) ) / 2 )
which seems to be the very thing it was designed to prevent? If that is invalid, it seems like a footgun, since these lengths are often passed around in variables, so the error will not be immediately obvious. And if it’s invalid, it begs the question, how is this better than makingcalc(min-content + max-content / 2)
invalid whilecalc(min-content)
orcalc(max-content / 2)
are individually valid. -
We are concerned with adding a parallel function to CSS that is identical to
calc()
with the only exception being that it supports an intrinsic sizing keyword. We think that simply allowing keywords incalc()
would have been a better design, even if only one keyword type is supported and it becomes invalid when more than one are used (going from 0 to 1 is still an improvement!). This leaves the door open for supporting more keywords in the future, it means all other math functions that support<calc-sum>
(such asclamp()
) also support intrinsic sizing keywords, and does not increase the API surface authors need to learn (especially since we suspect it’s unlikely they would ever hit that restriction). -
Looking at the (great) compat analysis, it seems unclear whether this would be a net negative. As @dbaron points out, in most of these cases the result is either an improvement, or a very slight glitch. We are not sure that the reduction in ergonomics that something like
calc-size()
causes is clearly warranted from this data. But even it were, is introducing a newcalc()
function the best way to address it? E.g. there are other cases where transitions need to customize the interpolation on a value type-specific basis, e.g. color space for<color>
transitions, perhaps some kind of per-transition/animation opt-in would be more palatable? -
We have concerns about backward compatability. E.g. if an author specifies
height: calc-size(max-content)
(orheight: calc(max-content)
) then a UA that doesn't support the new behavior loses theheight
property, requiring the author to specifyheight
twice (or more:height: max-content; height: calc(5em * ?) /*approximate*/; height: calc-size(max-content);
) -
Using a
calc
a function to opt-in to animation behavior doesn't seem readable. A different author seeingheight: calc-size(max-content)
will not necessarily understand the purpose of the function and may want to remove it (resulting in justheight: max-content
), disabling the animation.
Response:
<blockquote>We think that calc-size()
is simply unreadable as a signal of intent. The purpose of the signal and how it is spelled don't align. That's a problem when it is a singular opt-in. Adding alternative opt-in mechanisms is - as you say - poor. If we were to add something to transition-behavior
, then that's extra noise. Adding redundant opt-in mechanisms (when this is already redundant) is probably worse.
We note that people have explicitly requested animation/transitions in the bustage cases you found. Your assertion is that it is too disruptive to deliver those animations, despite them being requested explicitly. We have to accept that there are cases where transitions were specified and JS was used to cover for it not working, so that the result ends up being terrible.
So really what we're talking about is how we're managing this compatibility problem. We'd like to see the opt-in being disconnected from the functionality so that there is some flexibility in how implementers approach the problem, rather than having it an intrinsic property that is therefore impossible to disengage. Maybe some browser would prefer to YOLO this one, which we might find to be OK. Though poor, a separate keyword (on transition-behavior
and friends) might be better, despite how ugly it is.
Comment by @martinthomson May 21, 2024 (See Github)
@hober, @LeaVerou, @plinss, and I discussed this and we have some brief feedback, some of which is really questions.
We have several concerns with the design of this function:
-
If
calc-size()
returns a<length>
, what prevents authors from doing something likecalc( ( calc-size(min-content) + calc-size(max-content) ) / 2 )
which seems to be the very thing it was designed to prevent? If that is invalid, it seems like a footgun, since these lengths are often passed around in variables, so the error will not be immediately obvious. And if it’s invalid, it begs the question, how is this better than makingcalc(min-content + max-content / 2)
invalid whilecalc(min-content / 2)
orcalc(max-content / 2)
are individually valid. -
We are concerned with adding a parallel function to CSS that is identical to
calc()
with the only exception being that it supports an intrinsic sizing keyword. We think that simply allowing keywords incalc()
would have been a better design, even if only one keyword type is supported and it becomes invalid when more than one are used (going from 0 to 1 is still an improvement, modulo the above concern). This leaves the door open for supporting more keywords in the future, it means all other math functions that support<calc-sum>
(such asclamp()
) also support intrinsic sizing keywords, and does not increase the API surface authors need to learn (especially since we suspect it’s unlikely they would ever hit that restriction). -
Looking at the (great) compat analysis, it seems unclear whether this would be a net negative. As @dbaron points out, in most of these cases the result is either an improvement, or a very slight glitch. We are not sure that the reduction in ergonomics that something like
calc-size()
causes is clearly warranted from this data. But even it were, is introducing a newcalc()
function the best way to address it? E.g. there are other cases where transitions need to customize the interpolation on a value type-specific basis, e.g. color space for<color>
transitions, perhaps some kind of per-transition/animation opt-in would be more palatable? -
We have concerns about backward compatability. E.g. if an author specifies
height: calc-size(max-content)
(orheight: calc(max-content)
) then a UA that doesn't support the new behavior loses theheight
property, requiring the author to specifyheight
twice (or more:height: max-content /* what is wanted, not animatable */; height: calc(5em * ?) /* approximate the size, but animatable (?) */; height: calc-size(max-content) /* this proposal */;
) -
Using a
calc
a function to opt-in to animation behavior doesn't seem readable. A different author seeingheight: calc-size(max-content)
will not necessarily understand the purpose of the function and may want to remove it to justheight: max-content
. The effect here of disabling the animation is not an obvious consequence of that change.
Comment by @dbaron May 21, 2024 (See Github)
-
I just answered the first part of your first question about why not to allow mixing in https://github.com/w3c/csswg-drafts/issues/626#issuecomment-2121525106 , since I saw it there first.
-
I think the second part of the first question and much of the second question (why not just use
calc()
and make the mixes syntactically invalid) is answered in https://github.com/w3c/csswg-drafts/issues/626#issuecomment-1863238679 and https://github.com/w3c/csswg-drafts/issues/626#issuecomment-1863371468 , which describe the original rationale for this choice. (I agree the this definitely has arguments on both sides, though.) However, I think the response to the first question (why we shouldn't support mixes) negates some of the arguments you make about supporting mixes eventually. -
I think that's a misreading of the compat analysis. I found three animations that were clearly worse and would probably be considered unacceptable regressions to the page designers, one that was different but not obviously worse or better, and one that was better. And that was using a sample that was extremely short on animations since it involved only page loading and not user interaction.
-
I think developers who pay attention to backwards-compatibility are familiar with the idea of repeating declarations with the one for older browsers first. That said, I suspect the reason you're raising this (that is, the only reason I see that this is different from adding any new CSS feature) is because of the connection to the next point -- that you'd like authors to use a different opt-in so that the backwards-compatibility concern (at least for CSS transitions rather than CSS animations) would be restricted to the animation itself and not the states before and after it. That's a fair point. (That said, the alternative opt-in mechanisms we discussed so far all have tricky issues as well, I think; see below.)
-
As a followup to the most recent CSSWG discussion, we opened https://github.com/w3c/csswg-drafts/issues/10294 on having an additional opt-in mechanism. (After all, there's no reason that we have to have only a single opt-in; it may well be reasonable to have more than one thing that opts you in to the "new" path.) I think there are a bunch of complex design questions about an alternative, such as:
- We want to avoid an opt-in that is too "global" because it doesn't work well for web content that mixes components built by different authors (some of which may need the new behavior and some of which may be incompatible with it)
- Adding to the existing
transition-behavior
shorthand is too tied to CSS transitions; if we did this we'd need a separate opt-in for CSS Animations and Web Animations - Having a separate non-inherited property makes the syntax somewhat disconnected from the animation that requires it; I think mode-switching properties in CSS that have effects on other properties are generally a design mistake, both because of action-at-a-distance (with declarations from different sources combining together) and because they're not tied to the CSS cascade's overriding behavior.
I think then there are two separate questions: do you think having the
calc-size()
opt-in is too unreadable to be the only opt-in, or do you also think it's too unreadable to be one of the choices for opting in? I'm not sure from your question whether you think only the first, or you think both.
Discussed
Jun 1, 2024 (See Github)
Answered David's question, will see how things play out.
Comment by @martinthomson Jun 18, 2024 (See Github)
do you think having the calc-size() opt-in is too unreadable to be the only opt-in, or do you also think it's too unreadable to be one of the choices for opting in?
Yeah, this is really the crux of the problem that we're also grappling with. We note that CSS met recently and resolved on a separate opt-in mechanism. The remaining question we have is whether the calc-size()
wrapper is necessary in light of that.
Comment by @dbaron Jun 19, 2024 (See Github)
My current thinking is that I'd like to do the following:
- support both
interpolate-size
(as resolved last week in https://github.com/w3c/csswg-drafts/issues/10294) andcalc-size()
syntax - when describing and documenting the animation use cases, recommend
interpolate-size
(and useinterpolate-size
as the primary name of the feature, e.g., updating the explainer, updating the chromestatus entry, using it primarily blog posts) - document that
calc-size()
is used to represent the intermediate values in animations with keyword endpoints.
I think there are two reasons to want to keep calc-size()
:
- it's architecturally not really possible (at least cleanly) to have CSS animations that produce intermediate values that aren't representable in CSS. (
mix()
provides a future mechanism that makes this at least more palatable than it is today albeit still not ideal, but it's not yet implemented anywhere as far as I know.) - The extensible web manifesto gives a number of reasons that we should bias towards exposing the mechanisms underlying things unless we have reasons not to do so; I don't think there's sufficient reason not to expose
calc-size()
here (even if there is good reason to suggest an alternative for a key set of use cases). (Along these lines, at the CSS face-to-face last week @kizu mentioned having non-animation use cases forcalc-size()
.)
Comment by @LeaVerou Jun 19, 2024 (See Github)
(Writing this comment on my own, but from what I recall about our consensus last time this was brought up)
I think there are two reasons to want to keep
calc-size()
:
- it's architecturally not really possible (at least cleanly) to have CSS animations that produce intermediate values that aren't representable in CSS. (
mix()
provides a future mechanism that makes this at least more palatable than it is today albeit still not ideal, but it's not yet implemented anywhere as far as I know.)- The extensible web manifesto gives a number of reasons that we should bias towards exposing the mechanisms underlying things unless we have reasons not to do so; I don't think there's sufficient reason not to expose
calc-size()
here (even if there is good reason to suggest an alternative for a key set of use cases). (Along these lines, at the CSS face-to-face last week @kizu mentioned having non-animation use cases forcalc-size()
.)
Both of these arguments make the point that intermediate values should be representable in CSS. However, no-one proposed they should not be! Our concern was about introducing a new function to do this whose only purpose seems to be implementation convenience. There does not seem to be any author-facing reason that justifies introducing a distinct calc-size()
syntax over simply using calc()
. We listed several issues earlier that make the DX calc-size()
quite confusing for authors. None of these issues are present when simply reusing calc()
for this.
We think that reusing calc()
even with constraints about the number of distinct intrinsic size keywords that can be valid within it would be a far better path forwards for authors: No new function to learn, and they would likely never even hit the limitation. The current state is that no intrinsic size keywords are allowed in calc()
, so allowing one but no more than that seems like a direct improvement, and leaves the path open for supporting more in the future.
From our perspective, it seems that the only argument against reusing calc()
with a limit of one intrinsic keyword max is theoretical purity (that the limitation should be clear from the syntax), which as you know, is at the bottom of the priority of constituencies.
Comment by @tabatkins Jun 21, 2024 (See Github)
First, I dispute that the signaling value of calc-size()
can be ignored. A calc-size()
is very specifically not a number and can't be used in places where numbers can; it's an intrinsic size usable only in a few specially-allowed places. This is distinct from other specialized uses of calc()
, like their use in RCS with channel keywords, where the value takes some context from its surroundings but is afterwards just an ordinary number.
This also comes with a usability benefit, in that because there are behavior differences in layout algorithms based on what sizing keywords you're using, it's of some value to authors to ensure that it's clear and obvious which keyword it is being adjusted. Having it pulled out as the first argument of calc-size()
accomplishes this.
Relatedly, it's just plain valuable to know that the value is an intrinsic size, of any sort; having that behavior switch hidden in an arbitrarily-complex calc()
isn't ideal. (It also would mean that we could no longer define that calc()
is a <length-percentage>
; it would only be so conditionally, based on the absence of such a keyword.)
Second, I explained why a separate function, and not just "keywords in calc()", was useful back in the CSSWG thread that introduced this, in direct response to your comments in that thread, @LeaVerou.
If I ignore the ones related to interpolation (largely moot now), and put the signaling value to the side as well, the big remaining reason from that thread is that percentages can have intrinsic behavior, but they already have well-defined and interoperable behavior in calc() which will not smoothly interpolate. That is, if 100%
ends up cyclic, interpolating from 100%
to 0
will "behave as auto" for the entire duration of the transition, and then suddenly snap to 0 at the end. With calc-size()
you can instead get smooth interpolation behavior from any starting size, regardless of what it is, simply by putting it as the first argument.
Discussed
Jul 1, 2024 (See Github)
Invited Tab et al to a breakout
Comment by @plinss Jul 17, 2024 (See Github)
@tabatkins can you join us for a call to talk about this in more detail? Possibly the week of the 29th?
Comment by @tabatkins Jul 17, 2024 (See Github)
Sure? I'm not sure what else I can communicate that hasn't already been, but that's fine.
I'm not available Monday all day, or Tuesday afternoon. The rest of the week is fine (PT working hours)
Comment by @LeaVerou Jul 17, 2024 (See Github)
@plinss that was meant to ping @dbaron as well, right?
Comment by @plinss Jul 17, 2024 (See Github)
Yes, @dbaron is always welcome.
We're at a F2F this week and will be off next week, and we're planning on rescheduling our regular breakouts, so we'll get back to you to schedule.
Comment by @dbaron Jul 18, 2024 (See Github)
The week of July 29th I can generally do 9:00-17:30 in America/New_York
(13:00-21:30 UTC), or usually to 18:00 local (22:00 UTC) if needed. I'd prefer to avoid 12:00-14:30 local (16:00-18:30 UTC) on Wednesday and to avoid 12:00-13:00 and 14:00-15:00 local (16:00-17:00 and 18:00-19:00 UTC) on Thursday. If needed I could also do other times within 6:30-22:00 local (10:30-02:00 UTC) if given a short and specific time window sufficiently in advance.
OpenedMay 14, 2024
こんにちは TAG-さん!
I'm requesting a TAG review of the CSS
calc-size()
function.The CSS calc-size() function is a CSS function similar to calc(), but that also supports operations on exactly one of the values auto, min-content, max-content, fit-content, stretch, or contain. This allows transitions and animations to and from these values (or mathematical functions of these values), as long as the calc-size() function is used on at least one of the endpoints of the transition or animation to opt in.
Further details: