If you messed around with CSS for long enough, chances are you've tried at least once to make a transition from height: 0
to auto
... only to find out that it doesn't work! π’
β‘οΈ Luckily, today there is actually a solution to this problem: it uses CSS Grid under the hood, and it is just so easy and it works flawlessly!
Let's start with a practical example. I have built this simple accordion:
The HTML for it is pretty straightforward:
<div class="accordion">
<div class="accordion-title">Hover me!</div>
<div class="accordion-body">
<div>
<p>Lorem ipsum ...</p>
</div>
</div>
</div>
If you hover with your mouse over the accordion, you'll notice that a dropdown appears. That's cool, but what if we wanted to make it appear with a nice smooth transition?
I actually tried to do so in the previous codepen by adding a little transition on the height
property:
.accordion-body {
height: 0;
transition: 500ms height ease;
}
.accordion:hover .accordion-body {
height: auto;
}
β Unfortunately, this doesn't work: transitioning from height: 0
to height: auto
, as I was saying earlier, is something not possible with CSS.
π€ How to solve this?
Well, a first solution could be setting the height
property to a fixed number, instead of auto
.
This would work, but it's not such a great approach: in order to compute this fixed number we would have to resort to JavaScript, in order to calculate how much our .accordion-body
is actually tall... not really what we aimed for!
π Can we still achieve this effect, but with a CSS-only solution?
π‘ Actually, yes! Why don't we just use max-height
instead?
.accordion-body {
max-height: 0;
transition: 500ms max-height ease;
}
.accordion:hover .accordion-body {
max-height: 200px;
}
This would be the result:
Since we are defining a fixed value for max-height
, the browser is now able to perform the transition correctly.
π The only problem is that, since we are defining a fixed value for max-height
, now the content could potentially overflow:
If you're sure that your content will always be such that it never reaches a certain height... then it's perfectly fine to use this method! Just use an appropriate value for max-height
, and you're good to go.
Be aware though, the higher the value for max-height
, the weirder the transition gets (try putting a max-height: 1000px
in the previous codepen, and see how things change!).
π€ Can we do better? Can we avoid having any fixed height
/max-height
in the first place?
π CSS Grid comes to the rescue!
β We can actually use a neat trick which basically consists in making a CSS grid with a single grid item.
All we really have to do then, is taking our grid-template-rows
and make it transition from 0fr
to 1fr
: this way, our grid item will transition from 0 to its "natural" height. It's THAT simple:
.accordion-body {
display: grid;
grid-template-rows: 0fr;
transition: 250ms grid-template-rows ease;
}
.accordion:hover .accordion-body {
grid-template-rows: 1fr;
}
.accordion-body > div {
overflow: hidden;
}
This feels a lot cleaner. No fixed heights, no fancy stuff, just our accordion working as expected. Wonderful! π
The one caveat to this solution is that you actually need to set an overflow: hidden
to the .accordion-body
's internal div
in order to make this work. In my opinion, this little extra CSS is totally worth it, but let me know in the comments what you think about it!
π Bonus tip
This trick only works because of the animability of grid-template-rows
(and, more generally speaking, of grid tracks).
This is quite a recent feature for some browsers: if you visit this page you'll notice that grid tracks animability is something that, for example, only landed in Chrome starting from version 107.
At the time I'm writing this article all major browsers supports this feature, but always check for compatibility first if you want to use this feature in production code!
And that's all! Feel free to leave a comment and let me know if you already knew about this awesome CSS feature! π
Till next time! π
Top comments (65)
I was having this trouble building the FAQs section of this landing page I was working on, I had to result to the max-height property. Will try this neat trick and see
Thank you for the insight
Awesome, glad to hear that! Thanks for stepping by! π
Really cool! This is a problem that's been annoying me occasionally and I've never found a good solution without using fixed heights. I have a codepen somewhere with a much more hacky way of achieving this, but I never used that in any real projects because of how ugly it is.
I totally understand! My go-to solution has always been using the
max-height
approach... and yeah, I was not so proud of my code everytime I had to build an accordion πBut knowing that this can be now achieved without magic numbering anything and without any JS, just got me hyped up so much!
Great post! Thank you π
Small suggestion...
When simply looking at your HTML DOM Structure for the accordion component, I was wondering why there was an empty
<div>
within the accordion body, only later to find out it is a necessary element.Maybe specify a class on the empty
<div>
with something likeoverflow-hidden
oraccordion-body__required-inner-container
(obv this is overkill π) or something that makes it clear this element is required.Then, obv update your css:
Again, just a suggestion! Great write-up overall!
βοΈ βοΈ βοΈ βοΈ βοΈ
This is a great idea! Giving that div a class makes more clear what the purpose of it really is. And using BEM for it is also a nice touch. Thanks for the tip! π
Dude, I had this problem for years, this helped a lot. Thanks.
Glad it helped! βΊοΈ
Hey there Francesco, thanks for sharing.
I've just started a Front End Dev bootcamp course and I will definitely be using some of these techniques that you've suggested in the future. - Bookmarked and followed!
Thank you so much! This motivates me a lot π
there is one very niche specific problem with this what almost no one will face. If you happen to have a meatball menu in this click-or-hover-to-expand component, the menu will be cropped because of the overflow hidden property. I am yet to find a solution for this. Using html details and summary tags may be a nice way of doing it. havenβt tried tho.
great post btw π»
I don't think I completely understood the scenario you're describing: could you provide an example of this, or a codepen where this situation happens?
Great post, thank you for this!
Glad you liked it! π
This is an excellent solution to a perpetually annoying issue. Great writeup!
Thank you so much!
I used margin instead of height transition to implement height transition in a multi-level menu, but I think gtr can also be used in this case.
Really slick solution π
Glad you enjoyed it! π