DEV Community

Cover image for 🧙‍♂️ CSS trick: transition from height 0 to auto!
Francesco Vetere
Francesco Vetere

Posted on

🧙‍♂️ CSS trick: transition from height 0 to auto!

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>
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

❌ 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;
}

Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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! 👋

Buy Me A Coffee

Top comments (63)

Collapse
 
gyauelvis profile image
Gyau Boahen Elvis

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

Collapse
 
francescovetere profile image
Francesco Vetere

Awesome, glad to hear that! Thanks for stepping by! 😊

Collapse
 
darkwiiplayer profile image
𒎏Wii 🏳️‍⚧️

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.

Collapse
 
francescovetere profile image
Francesco Vetere

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!

Collapse
 
gregjacobs profile image
Greg

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 like overflow-hidden or accordion-body__required-inner-container (obv this is overkill 😆) or something that makes it clear this element is required.

<div class="accordion">
    <div class="accordion-title">Hover me!</div>
    <div class="accordion-body">
        <div class="accordion-body__overflow">
            <p>No longer do you need to use "max-height" to animate the transition of a hidden element.</p>
        </div>
    </div>
</div>
Enter fullscreen mode Exit fullscreen mode

Then, obv update your css:

.accordion-body__overflow { overflow: hidden; }
Enter fullscreen mode Exit fullscreen mode

Again, just a suggestion! Great write-up overall!
⭐️ ⭐️ ⭐️ ⭐️ ⭐️

Collapse
 
francescovetere profile image
Francesco Vetere

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! 😉

Collapse
 
laerciolopesll profile image
LaercioLopesLL

Dude, I had this problem for years, this helped a lot. Thanks.

Collapse
 
francescovetere profile image
Francesco Vetere

Glad it helped! ☺️

Collapse
 
aaron1490 profile image
Aaron Armstrong • Edited

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!

Collapse
 
francescovetere profile image
Francesco Vetere

Thank you so much! This motivates me a lot 😁

Collapse
 
shamemezahid profile image
Shamim Bin Zahid • Edited

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 🌻

Collapse
 
francescovetere profile image
Francesco Vetere

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?

Collapse
 
anitaolsen profile image
Anita Olsen*°•.¸☆

Thank you for this! I saved this for later so I can come back to it!

Collapse
 
francescovetere profile image
Francesco Vetere

Thanks to you for stepping by, I really appreciate it! 😊

Collapse
 
magnificode profile image
Dominic Magnifico

This is an excellent solution to a perpetually annoying issue. Great writeup!

Collapse
 
francescovetere profile image
Francesco Vetere

Thank you so much!

Collapse
 
benavern profile image
Benjamin CARADEUC

What a great solution!!!! Love it! 😻😻😻
I'll drop all the stuff I did with a lot of JS and much less maintainable code, right now !
Thanks a lot!

Collapse
 
francescovetere profile image
Francesco Vetere

Glad you liked it! 😊

Collapse
 
renedev profile image
Rene D

Great post, thank you for this!

Collapse
 
francescovetere profile image
Francesco Vetere

Glad you liked it! 😉

Collapse
 
sun_kanmii profile image
Sunkanmi Fafowora

Love it.

Collapse
 
hasanelsherbiny profile image
Hasan Elsherbiny

this is great 👏👏

Collapse
 
francescovetere profile image
Francesco Vetere

Thanks! Glad you liked it 😁

Collapse
 
nwarwick profile image
Nicholas Warwick

Really slick solution 👌

Collapse
 
francescovetere profile image
Francesco Vetere

Glad you enjoyed it! 😉

Collapse
 
artxe2 profile image
Yeom suyun

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.

Collapse
 
gourobdas0103 profile image
Gourob Das

Nibe