How To Do CSS Transitions With Height: Auto
Creating a smooth expand/collapse animation seems easy. Surely you can set a transition on height: auto
, and it'll just work?
Sadly, the CSS gods aren't so kind.
Instead of the smooth open and close you wanted, your element flashes to its new height. No smooth animation.
As it turns out, animating on CSS auto
values doesn't work, and it's honestly a real shame. We need to use specific values, and that's not a luxury we always have.
Fortunately, there are several approaches we can take to animating auto dimensions in CSS.
Method 1: Use transform
Despite its rampant use, you should avoid CSS transitions on the height
or width
properties (among some others).
These properties affect the page layout. To see how other elements on the page are being affected during your animation, the browser completely recalculates your page's layout.
For every. single. frame.
This is where the CSS transform
property comes in. transform
causes your element to be animated like an image, and skip the layout recalculations. If you can use this approach, you should.
Unfortunately, there are two deal-breakers in using transform
to collapse your content:
- The content warps when it collapses.
- It leaves a gap where the content was.
Method 2: Animate max-height
/ max-width
A different approach to collapsing height
is to collapse max-height
in its place.
The upside of this approach is that it's straightforward. It also manages to avoid the weird warping effect of transform: scale
.
There are some downsides, though:
- We're animating on
height
, with all its layout-thrashing goodness - You need a non-auto value for
max-height
- Your
transition-timing
applies to themax-height
range, notheight
. This makes it nearly impossible to get a specific visual effect from your transitions.
Method 3: Calculate height
with JavaScript
A lengthier (but more precise) approach is to use JavaScript to calculate the animation start and end using the element's rendered height
.
Once we determine the element's actual height, we can inject it as inline CSS so the browser has something to transition between.
In terms of effect, this is probably the one you were going for, but with tradeoffs.
- We're still animating on
height
- We've introduced a fair chunk of code to address a very narrow problem
- There's tight coupling between your JavaScript and CSS
Method 4: Nuke from orbit with FLIP + Inverse Scaling
Remember Method 1 and all the problems that using transform
caused?
Well, it turns out you can fix them.
Content warping can be fixed by applying counter-scaling on your content. The FLIP technique also lets us move surrounding content in a performant way.
This looks much better than our initial transform
attempt, and solves the problems we were having. However:
- This solution is big and complex. We've introduced a lot of code now, and it's still a very narrow problem.
- Several moving parts need to stay synchronised
- We need a custom easing function. I've used
linear
for simplicity, but anything else gets gnarly. - Even more coupling than the
height
example.