CSS transition from specific height to "auto"
There are certain patterns I keep using on different websites. One of those is that some content is hidden and can be shown when clicking a button. Of course the content should not only just appear then but a nice "slide down" transition should be used.
I found out that it's possible to transition from height 0 to "auto" using CSS grid a while ago. Before learning about that, I used JavaScript to find the maximum height of the "hidden" content and set this as the value for the CSS max-height property. A workaround for not having to use JavaScript would be to set the max-height to a value which is definitely larger than the content. The downside of this is, that the timing of the transition can't be set properly. But all this isn't needed anymore, CSS grid for the win.
So here is how the height transition with CSS grid works:
<button>Show more</button>
<div class="expander-wrapper">
<div class="inner">
Lorem Ipsum dolor sit amet ...
</div>
</div>
.expander-wrapper{
display: grid;
grid-template-rows: 0fr;
transition: grid-template-rows 0.4s ease-out;
overflow: hidden;
}
.expander-wrapper .inner{
min-height: 0;
}
.expander-wrapper.open{
grid-template-rows: 1fr;
}
const btn = document.querySelector("button");
btn.addEventListener("click", (e) => {
document.querySelector(".expander-wrapper").classList.toggle("open");
});
Recently I needed to do something similar which turned out doesn't work with the approach mentioned before: I wanted to initially only show a part of a quite long text and have a button to show it completely. The difference to the example above is that the initial value for grid-template-rows would be set in em and not be 0fr.
.expander-wrapper{
display: grid;
grid-template-rows: 5em;
transition: grid-template-rows 0.4s ease-out;
overflow: hidden;
}
When doing so the transition no longer works. It seems like the transition doesn't work when different units are used (even using just 0 instead of 0fr doesn't work).
The solution I found for this to work is to use two rows. The first row has the "minimum height" and the second row 0fr — which then can be transitioned to 1fr. The inner element is set to span over both rows.
.expander-wrapper{
display: grid;
grid-template-rows: 5em 0fr;
transition: grid-template-rows 0.4s ease-out;
overflow: hidden;
}
.expander-wrapper .inner{
min-height: 0;
grid-row: 1 / -1;
}
.expander-wrapper.open{
grid-template-rows: 5em 1fr;
}
Demo:
Note: In many use cases the details element would be the perfect fit for something like this. Unfortunately it's not possible to transition the opening of the details element without using JavaScript. In case you are interested how to do it with JavaScript, have a look here.