CSS still can’t animate to auto
dimensions.
.dropdown {
transition: 0.2s;
height: 0;
}
.dropdown.open {
/* the height will change, but it won't animate. */
height: auto;
}
There is JavaScript trickery you can try. Brandon Smith outlined several techniques here a little while back. My mind always goes to this solution just because it’s so simple:
.dropdown {
transition: 0.2s;
max-height: 0;
}
.dropdown.open {
/* 🎉 */
max-height: 400px;
}
Now we have this 400px
magic number which is really not ideal. But the fact that this works and is so simple makes it extremely appealing that I use it production all the time.
But the magic number isn’t the only problem. Another problem is scrollbars.
When we set max-height: 0;
, we also need overflow: hidden;
to make sure the dropdown is actually hidden when it is closed. When the dropdown is open, we should probably be using overflow: auto;
so that we don’t accidentally cut off content in case the natural height of the dropdown is taller than the max-height
after it expands. The use of overflow: auto;
solves that problem while introducing another: during the expansion, our dropdown will always have scrollbars for at least part of the expansion, even if the final expansion height doesn’t need them. That’s awkward!
CSS trickery to the rescue.
We can still use overflow: auto;
on the expanded state — we’ll just override it during the animation. As we learned in the great CSS specificity battle, @keyframes
have an amazing ability to override anything while they are active. Let’s use them not to animate the opening, but just for this scrollbar-hiding functionality:
.dropdown {
max-height: 0;
overflow: hidden;
transition: max-height 1.2s ease-in-out;
}
.dropdown.open {
overflow: auto;
max-height: 400px;
animation: hide-scroll 1.2s backwards;
}
@keyframes hide-scroll {
from, to { overflow: hidden; }
}
That does the trick!
Try adjusting the height to something less to see how you don’t see scrollbars during the animation but only at the end when they are needed. That causes a little bit of jerkiness when the scrollbar pops in, but that was acceptable in my case as it’s rare that it happens at all. If you absolutely wanted to stop the jerkiness, you’d probably apply a (custom) scrollbar at all times to the dropdown and perhaps adjust the styling of the scrollbar during the animation, if needed.
Credit here to Mr. Stephen Shaw of the fancy @keyframers for this trick. I yanked him in to help me figure it out while I was working on it for something on CodePen. We decided to turn the trick into a video for the CodePen channel showcasing Collab Mode, which we used to figure out the problem/solution:
Cool! Thank you for sharing this trick, Chris.
Please check out my solution. Also a workaround but working ;) https://codepen.io/kyvaith/pen/WwzVXK
The body of your page gets stretched when using this technique, so any content after the menu will get pushed much further down, any solution to that?
Very similar to what I use, except I don’t use “backwards” or “to”. What’s the point of “backwards” when the from and to keyframes are the same?