w3c / csswg-drafts

CSS Working Group Editor Drafts
https://drafts.csswg.org/
Other
4.42k stars 649 forks source link

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

Open keithjgrant opened 7 years ago

keithjgrant commented 7 years ago

(Edited: current resolved-on proposal is at https://github.com/w3c/csswg-drafts/issues/626#issuecomment-1800254442)

https://drafts.csswg.org/css-transitions/#animtype-rect

It would be incredibly useful to be able to transition to (or from) height: auto. As it stands now, you need to jump through a bunch of hoops in JavaScript to simulate it. This is used frequently for transitioning in alert boxes, opening accordions, etc, but it seems silly that JavaScript is required.

I’m sure there must be discussion about this in the old mailing list, but I’d love to be privy to it here.

AmeliaBR commented 7 years ago

Adding here what I replied to @keithjgrant via Twitter:

In order to interpolate to/from an auto or other keyword value, you need to have a way of describing the intermediary state. For lengths, intermediary states can usually be described via a calc() function. 10% of the transition from 50px to 20em is calc(0.9*50px + 0.1*20em). So the most generic approach to supporting transitions with keywords would be to allow the keywords to be used in calc(), with a real-time substitution of the current computed value that the keyword would create.

Sure, there are edge cases where the performance would inevitably be horrible (e.g., if you are transitioning to/from auto at the same time you are also changing things that would cause the current computed value of auto to change), but that shouldn't be reason to prevent its use in more sensible cases.

nico3333fr commented 7 years ago

@keithjgrant yes, this is a real problem.

For the moment, we set up transitions on max-height value (using magic numbers for values, max-height = enough_and_pray_the_content_is_not_that_tall), and if you want to do it including accessibility pov, you have to transition on visibility (as display is not possible, cf this accordion https://a11y.nicolas-hoffmann.net/accordion/?animated=1 ) with a delay... really complicated for something that should be simple. :)

rickyp-ms commented 7 years ago

Looking around the web, you can easily see that this is a constant problem for developers. Velocity.js even notes this specific missing feature in its first paragraph of why it exists (https://fabric.io/blog/introducing-the-velocityreact-library). I can probably give a ton of references (on request) of developers rendering an element, measuring it, setting it's height to 0 then animating to that specific height. Even JQueryUI does a similar measurement technique just to get this to work and it's been around for almost a decade now.

What can we do as the larger software development community to petition this change and make sure it gets in on the next iteration?

Do we need to have more people come here and upvote this bug? Do we need to email a specific individual? Let us know so we can let you know what we need most.

k2snowman69 commented 7 years ago

I too am curious what we (the web programming community in general) can do to petition a change in the spec... I've got a few issues I know I could get quite a few people backing.

birtles commented 7 years ago

I caught up with @dbaron about this recently and it seems a likely way forward is to make 'auto' into a computed value. As well as enabling animation with auto, that would also make it possible to write expressions like calc(auto + 10px). While that might not be trivial to implement, since there is interest from authors' and, I believe, browser vendors, it seems promising.

(One way to push this forward would be to submit a patch to one of the open-source browsers for this. Once you've proven the technical feasibility of this, it's easier to argue for it to be added to the spec.)

dbaron commented 7 years ago

auto is already a computed value; the thing that needs to happen is adding support for auto to calc().

k2snowman69 commented 7 years ago

Awesome I'll start looking into it using Firefox since I think theirs would be the easiest source code to get a hold of. That said, it may take me a while due to lack of cpu resources (my computer is a 2 core 2.4ghz processor... it's going to take a while to build).

nilssolanki commented 7 years ago

Thank you very much for bringing this up. We currently work around this by reading out an elements scrollHeight and transitioning to that height, only to revert the element height to auto on transitionend. So making this a platform feature would be fantastic.

craigfrancis commented 7 years ago

It has been suggested that we use max-height: max-content.

But trying to get the browsers to implement either has been very difficult, as the CSS animations are typically done between 2 known values, which is why animation usually needs to be done with something like max-height: 500em.

As an aside (and for some of the background on this), I've been trying to get browsers to support auto-height <iframes> and <textarea>s, which will work in pretty much the same way:

https://github.com/craigfrancis/iframe-height

https://discourse.wicg.io/t/feature-request-animating-max-height-height-based-on-content/1403

Jakobud commented 7 years ago

+1. Not being able to transition on height: auto and display: <anything!> (You can't even do a transition-delay on display) has always been a big pain.

k2snowman69 commented 7 years ago

Just FYI I'm currently out of the country and I'm also checking with my legal department to see if this is something I'm allowed to do due to where I work (non-compete and helping competitor issues). I'll be back in town on the 20th.

@nilssolanki That's the same trick we're using and it's just annoying.

@Jakobud I would say animating display is a bit out of the bounds of this request and may also have a few edge cases that would be interesting (animating inline to block?).

k2snowman69 commented 7 years ago

My legal department has come back to me informing me that I'm unable to currently work on this request due to non-compete legal stuff. That said, I'll have to (frustratingly) recuse myself of any code writing for this feature.

Just to note, I also asked a google groups question here if anyone is interested in taking this up. It should give a basic guide to how I was planning on tackling the problem.

https://groups.google.com/forum/#!searchin/mozilla.dev.tech.layout/auto%7Csort:relevance/mozilla.dev.tech.layout/fFuJqwXvu2I/6_mt7HmhDAAJ

danegraphics commented 7 years ago

Just want to make sure this is still happening.

This is actually rather important because, yes, we can precalculate a value for height and other properties using javascript and things like document.getElementById(elementID).scrollHeight+'px' , but sometimes we need the property to have an auto value after the animation/transition so that we can make changes inside and around the element that the element will respond to appropriately.

For example, I have an element that transitions from a height of 0 to a height of 'auto'. After the transition is completed I want the height of the element to be able to adjust correctly to the changing elements inside of it without me having to recalculate it myself.

So if it really is just adding auto compatibility to the calc() function, as @dbaron has mentioned, is someone working on that, and if not, how would one do so?

k2snowman69 commented 7 years ago

To my knowledge no one has picked up this work and I'm still barred due to legal constraints. But if you download the source code and read the conversation here: https://groups.google.com/forum/#!searchin/mozilla.dev.tech.layout/auto%7Csort:relevance/mozilla.dev.tech.layout/fFuJqwXvu2I/6_mt7HmhDAAJ

It should give you a few locations to pinpoint where you would need to make the changes you need. Not sure if those are all of them but it should give you a starting point for learning the code base.

Good luck, I hope you do take this up because it would be an amazing addition!

tabatkins commented 7 years ago

I'd have to review to make sure that 'auto' doesn't have any special behaviors that make it incompatible with explicit lengths in some situations. That is, any place where the layout system does special things if the width/height is "auto", such that replacing the "auto" with its equivalent absolute length would give a different result.

craigfrancis commented 7 years ago

I can't remember the source, but it was recommended we use height: max-content rather than auto... this might have started from you TJ.

Where it would be nice if we could use this keyword for iframe's and textarea's as well.

danegraphics commented 7 years ago

@k2snowman69 I would love to take this on and get it done. My question is what all would I need to change? I understand that there are the specification pages that explain how the transitions should be interpolated, and that those should be changed if this is added, but is there also some actual coded function or set of functions that would need to be modified? If so, where can I find them?

@tabatkins Do you know of any situations where that would be the case? (that auto acts differently than it's explicitly stated px size?)

jcrben commented 7 years ago

Linked to this discussion in an answer at Stackoverflow where I point out that transition to height: max-content does seem to work in Chrome: https://stackoverflow.com/a/45203565/4200039 nevermind, deleted the answer as I think I got confused somehow - doesn't seem to work

How can I transition height: 0; to height: auto; using CSS? is currently the top-voted question in the css-transitions tag, with nearly 5 times as many votes as the third-highest.

Also, what's the story with bugzilla these days as far as CSS? There's this bug - pointed here.

danegraphics commented 7 years ago

While height is easy to get to the calculated auto through max-content, it would be best if this were to work for multiple properties such as width (there is no setting comparable to auto) and others. It is impossible to use javascript to get width to work properly without manually doing the math to remove the L&R margins, padding, and border from the 100% calculation, and other properties have similar setting problems.

Using the pre-calculated auto value as the target setting for each property that has an 'auto' option, while actually setting it to 'auto', would be the best method from what I can see.

PS: The data from Stackoverflow suggest that this is the most requested feature for the next CSS update. Using Stackoverflow's highly voted issues would probably be a good place to look for other features that might be good to include in the next version.

jonjohnjohnson commented 6 years ago

I used to really really want this feature, but over the years have found myself being content with transitioning a transform in many cases, while having a parent element set in inaccessible ways...

.parent {
  pointer-events: none;
  visibility: none;
  overflow: hidden;
}
.element {
  pointer-events: auto;
  visibility: visible;
  transition: transform ease .375s;
  will-change: transform;
}
.transformed .element {
  transform: translateY(-100%);
}

Here, the -100% seamlessly takes care of the auto height and transitions a texture over possibly invaliding layout during a height transition. Not saying this is the silver bullet for the issue, but offering what I've found to be a more performant option for most cases. And furthermore, if we did get the ability to transition to/from auto, I'd rarely use it over this approach. Though, this obviously doesn't handle the case where you'd want flow content to move along with the "height" transition, which again, is not as performant.

steveaylwin commented 6 years ago

@jonjohnjohnson do you have a codepen/jsfiddle example of this anywhere and if not would you mind posting an example here? Many thanks

jonjohnjohnson commented 6 years ago

@cohsteve Quick -> https://jsfiddle.net/rwozw4bs/

Also, read up on FLIP animation methodology to see how you *could approach animating transforms while simulating the movement of flow content. https://aerotwist.com/blog/flip-your-animations/

keithjgrant commented 6 years ago

@jonjohnjohnson A transform effect works for absolutely positioned elements. But if the element is statically positioned, it still takes up space in document flow when scaled down to height 0.

Transitioning max-height was also mentioned above, but this too has a problem: the timing function is unreliable, since part of the transition will continue well after the max-height has reached the actual height of the element.

jonjohnjohnson commented 6 years ago

@keithjgrant correct. I find the gains from transforming a texture and not invalidating layout worthwhile, as opposed to the *few times I find myself wanting to animate within a flow.

danegraphics commented 6 years ago

Just making sure that this project hasn't been abandoned.

@dbaron, I see you are also the one assigned to the Mozilla ticket as well? (https://bugzilla.mozilla.org/show_bug.cgi?id=571344)

(I don't mean to be so pokey about this. I just keep running into designs where having this would immediately make the desired-but-impossible possible. So I hope that a fix for this bug will come soon.)

RemiKalbe commented 5 years ago

How is this, after so many years, still not solved?

drhodius commented 5 years ago

Bump

eoghanmurray commented 4 years ago

I've written a polyfill which converts the max-height + overflow:hidden hack-around for this issue into a more precise height transition. https://github.com/eoghanmurray/height-transition-polyfill

The basic idea is to measure the height at the start of the transition and assign it via style attribute. For the reverse transition from 0px to auto, because of the overflow:hidden, the scrollHeight attribute gives a good approximation of a target height for transitioning. (all these extras are removed after transition completes).

Transitioning from/to auto doesn't appear to cause any performance or other difficulties for the overflow:hidden case, as the internal element layout doesn't need to be recalculated (so e.g. height can't affect width).

I believe this special case (transitioning width/height with overflow:hidden to/from auto) represents 95% of the usecases [citation needed!], and should be a lot simpler to implement/standardize vs. the more general to/from auto/max-content. Is there an argument to be made for focusing on this first? (i.e. wish this case was standardized 10 years ago!)

SetTrend commented 4 years ago

I'd like to add that transitions/animations on auto valued properties should also be triggered when content changes:


<!DOCTYPE html>
<html>

<head>
  <meta charset="UTF-8">
  <title>Replaced content transition</title>

  <style type="text/css">
    div
    {
      border: 2px solid #caa;
      background-color: antiquewhite;
      padding: 1em;
      margin: 2em auto;
      width: 80%;

      transition: height 1s;
    }
  </style>
</head>

<body onload="setTimeout(() => document.getElementById('replaceMe').src='b.png', 2000)">
  <div><img id="replaceMe" alt="" src="a.png" /></div>
</body>

</html>
laukstein commented 4 years ago

@SetTrend, the proposal sounds good, but I think it is too complicated. Think ... when having auto height, from what height expected to began the transition when loaded page of whapper containing only text? Form 0 or should transition be ignored in first paint? Then when screen width changed and text wrapps to more or less lines expanding or collapsing its wrapper size - would you expect transition to be applied for wrapper height? Maybe transition should apply when fixed height size changes to "auto", and when opposite then on initial paint to ignore the transition, otherwise would look weird like in my bottom link.

See this Chrome bug https://lab.laukstein.com/bug/input having since beginning of CSS3 transitions and imagine how wrong it can get if the spec proposal gets wrong, or browsers gets it wrong.

Loirooriol commented 4 years ago

@SetTrend Interpolations work on the computed value. If you interpolate from auto to auto then it won't be smooth. This already happens if you have height: 100%; transition: height 1s and you change the height of the containing block. Then the used value of height: 100% changes, but it's a sudden change since both the 100% starting point and the 100% end point are updated.

SetTrend commented 4 years ago

@laukstein / @Loirooriol: Good thoughts.

First of all, I would suggest to have transitions and animations always be based on the computed* device pixels value, regardless of the CSS length currently being applicable to an element. Doing so, we get an abstraction layer between how a width or height is declared and what it really is on the device.

Then, in the event of (a) content changing, (b) styles getting dynamically applied or (c) transitions/animations being triggered, I would - figuratively speaking - draw the page having transitions/animations disabled. Then I'd get the computed device pixels values for all elements properties, and store those "target" device pixels values as shadow properties along with those elements. Next, I would re-enable transitions/animations and have them animate all elements whose current (probably in-transition) computed device pixels value deviate from the newly computed "target" device pixels value accordingly.

I once wrote a JavaScript function that works exactly like this (using hidden shadow elements, so running transitions on the target elements won't get interrupted) and, from my observation, it works like a charm so far.

I'm not sure, though, if it was necessary to re-evaluate descendant elements having width/height transitions applied while an anchestor element is being in transition, or to just have descendants strictly animate to their "target" size once it has been computed, regardless of its anchestor's in-transition dimensions.

@laukstein : Given my above explanation I would suggest to use the computed "target" device pixels value as an immediate starting point for any initial transition/animation.

* I wrote about "computed values" but I'm actually referring to "used values".

laukstein commented 4 years ago

@SetTrend, let's say your transition wrapper contains a text and now is added an image into it expanding its height with transition. Because now the wrapper size and its content size differs till transition end, would you expect always to flicker it with scrollbar till transition end? - know that having scrollbar will mean less width and some text wrapped to more lines, while when transition ended you would expect no more scrollbar and text wrapped normally - no extra line wrapping due to scrollbar - that saying it will flicker delivering bad UX. The workaround would be to add overflow: hidden for the transition wrapper but that sound already bad and as a limitation imagining to apply it to all places using this type of transition.

SetTrend commented 4 years ago

@laukstein : Yes, absolutely. That's the intrinsic nature of a transition: Intermediate state. It's up to the web page designer to circumvent and handle such condition in a sensitive manner.

danegraphics commented 4 years ago

Another year, another checkup.

I'm surprised that this bug still hasn't been fixed or even committed to. At this point pretty much everyone agrees that transitioning to auto is a necessary feature. Until this gets fixed, we're stuck with a bunch of ugly workarounds, each of which have a catch.

The question now is what will it take to get it implemented and who is in charge of taking those steps?

Heck, if it's something I can do, I'll do it. But I'm here because I don't know where to start and I'm assuming someone here does.

So, what's it gonna take?

jchiem commented 4 years ago

Hi, what I'm about to mention isn't exactly a height: auto solution but it doesn't rely on max-height being set to a exceptionally high value which will allow an element to expand to any size, instead it relies on max-height: 100% through a flex element.

While I was working on a component library we had the need for an "Expander" component and I managed to build something that expands to "100% height" of the element but with max-height: 100%;.

I'm wondering is this a bug that may be "fixed" in browsers in the future, or is it somewhat safe to assume that since this is working due to the flex specifications it's safe to use?

I believe it works by the fact that display: flex; elements are aware of their own size and as such can adjust to their "max height". Somehow it only works if it's not a direct element child of the flex container though.

https://codepen.io/jchiem/pen/vYGXxbq

eoghanmurray commented 4 years ago

@jchiem that looks like a great solution, and you should also post it here: https://stackoverflow.com/questions/3508605/how-can-i-transition-height-0-to-height-auto-using-css It's certainly the best CSS solution I've seen. Incidentally, the nested child is likely needed as the first child (.expandercontainer2 in the non-working example) is rendered as a flex-item, so it's dimensions are being controlled by the flexbox model, rather than preserving intrinsic dimensions like the .expandercontent element does. Of course we still need the spec updated!

mobinakhalilzade commented 3 years ago

I really wonder this issue opened in 2016 and still hasn't fixed.

danegraphics commented 3 years ago

It's still a big issue it seems. Even after all these years, there is still no good solution: https://stackoverflow.com/questions/3508605/how-can-i-transition-height-0-to-height-auto-using-css

benbucksch commented 3 years ago

Could you please give this problem some love? This is making CSS transitions useless in 90% of the cases where we want to use them.

zoonman commented 3 years ago

This bug https://bugzilla.mozilla.org/show_bug.cgi?id=571344 is 11 years old! And this functionality is still required. Maybe a decade ago it was computationally hard task but nowadays even phones use GPU acceleration.

rene78 commented 3 years ago

https://codepen.io/jchiem/pen/vYGXxbq

The workaround from @jchiem is pretty good, but the smooth appearance/disappearance of the content only works within the container of the component. Everything below is moved instantaneously (Example: https://rene78.github.io/faq-accordion/)

jstsch commented 3 years ago

Almost 15 years ago, we were solving this with jQuery.slideUp() / jQuery.slideDown(). It's a bit sad that this solution seems to still be the state of the art :smile:

As a web dev, the expected result is that simply having height: 0height: auto be animatable using a CSS transition, and working the exact same way as if the CSS was height: 0height: 400px, if the calculated height of the element was 400px. The browser would only have to calculate this pixel value once, at start of transition?

Currently, I'm seeing awful max-height hacks which have inconsistent animation timing or worse, chopped off content due to a too low value for max-height.

SetTrend commented 3 years ago

Just out of curiosity ...

I assess that we all agree on this being useful, and I assess that we all agree that the browser implementation of this feature request is a snap ... Then what's keeping it from being approved?

birtles commented 3 years ago

This requires allowing calc() to accept auto (so you can represent intermediate values) which may not ending being trivial. e.g. see https://github.com/w3c/csswg-drafts/issues/626#issuecomment-311221589

If someone has the time to implement that in one of the engines, it would be a help.

danegraphics commented 3 years ago

It looks like we've got a stupid standoff between the browsers and the spec.

The browser people will wait for the spec to specify it first, but here in the suggestion for addition to the spec, the spec designers are saying to implement it in the browser first.

Somebody do something please, instead of waiting on the other person to do it.

Just add to the spec that "calc(auto)" should calculate the value of "auto" for the property.

Please. It's been so long.

Loirooriol commented 3 years ago

Just add to the spec that "calc(auto)" should calculate the value of "auto" for the property.

It's not that easy. auto has several special behaviors, for example, it enables justify/align-self: stretch. What happens if you have height: calc(auto + 5px); align-self: stretch? Should the box not be stretched, since height is not exactly auto? Should the box be stretched to fill the container, because it includes auto? Should the box be first stretched to fill the container, and then grow 5 extra pixels?

And that's just one case, several specs should be revisited and every interaction with auto should define how combinations with auto behave.

jstsch commented 3 years ago

Perhaps I'm missing the obvious, but do we really need to solve general case for calc(auto), if the goal is to just enable transitions from some value to auto? Can't simply the computed pixel value be used in the animation engine? I understand that enabling calc(auto) is an elegant solution, but it seems also full of tricky edge cases. Right now it's super confusing that height: 0height: auto does not animate.

Loirooriol commented 3 years ago

Yes, calc(auto) is needed because auto computes to auto (it's not resolved into a length until used-value time), and transitions interpolate computed values.

benbucksch commented 3 years ago

Can't simply the computed pixel value be used in the animation engine?

This, exactly.

Right now, the workaround we all have to do, is to use JS to measure the height that the element should have, then set that value explicitly using el.style.height.

Why can't the browser engine do the same that we do, to make this work? If JS can do it, the browser engine can definitely do it.

I can see why calc(auto) might be difficult, because calc() can do arbitrary expressions, and additionally auto might change during the animation. But that's not what we need. We just need auto. The standard, default case is not working right now.

I understand it might be difficult to solve every little edge case. But we're talking about the 99% common case here. The most obvious common case does not work, making CSS animations pretty useless in half of the cases where we'd want to use them. Instead of trying to solve all egde cases, and then do nothing, why not solve the common case first, and leave the edge cases for later, rather than leaving us all in the cold?

Why force all of us to do the size calculation manually in JS? If we can do it in JS, the browser engine can definitely do the same in the engine. In this case, it is warranted, because this is such an important case - the default case.