Categories
css css-transitions

How can I transition height: 0; to height: auto; using CSS?

2648

I am trying to make a <ul> slide down using CSS transitions.

The <ul> starts off at height: 0;. On hover, the height is set to height:auto;. However, this is causing it to simply appear, not transition,

If I do it from height: 40px; to height: auto;, then it will slide up to height: 0;, and then suddenly jump to the correct height.

How else could I do this without using JavaScript?

#child0 {
  height: 0;
  overflow: hidden;
  background-color: #dedede;
  -moz-transition: height 1s ease;
  -webkit-transition: height 1s ease;
  -o-transition: height 1s ease;
  transition: height 1s ease;
}
#parent0:hover #child0 {
  height: auto;
}
#child40 {
  height: 40px;
  overflow: hidden;
  background-color: #dedede;
  -moz-transition: height 1s ease;
  -webkit-transition: height 1s ease;
  -o-transition: height 1s ease;
  transition: height 1s ease;
}
#parent40:hover #child40 {
  height: auto;
}
h1 {
  font-weight: bold;
}
The only difference between the two snippets of CSS is one has height: 0, the other height: 40.
<hr>
<div id="parent0">
  <h1>Hover me (height: 0)</h1>
  <div id="child0">Some content
    <br>Some content
    <br>Some content
    <br>Some content
    <br>Some content
    <br>Some content
    <br>
  </div>
</div>
<hr>
<div id="parent40">
  <h1>Hover me (height: 40)</h1>
  <div id="child40">Some content
    <br>Some content
    <br>Some content
    <br>Some content
    <br>Some content
    <br>Some content
    <br>
  </div>
</div>

9

  • I believe the height:auto/max-height solution will only work if you’re expanding area is greater than the height you want to restrict. If you have a max-height of 300px, but a combo box dropdown, which can return 50px, then max-height won’t help you, 50px is variable depending on the number of elements, you can arrive to an impossible situation where I can’t fix it because the height is not fixed, height:auto was the solution, but I can’t use transitions with this.

    May 9, 2012 at 12:31


  • 73

    OP is trying for css solution, not js, otherwise they could just use overflow and animate

    Apr 16, 2015 at 16:39

  • 5

    @VIDesignz: But inner div’s -100% margin-top receives the width of the wrapper div, not the height. So this solution has the same kind of problem, that the max-height solution. Moreover when the width is smaller than the height of the content, not all content is hidden by -100% margin-top. So this is a wrong solution.

    – GregTom

    Dec 27, 2015 at 8:23


  • 3

    See github.com/w3c/csswg-drafts/issues/626 for discussion around the spec and implementation of a proper solution

    Jul 20, 2017 at 2:10

  • @Paulie_D The question is about height, not width. All the answers are about height. Please don’t change the question title to be about something it’s not.

    – TylerH

    Oct 24, 2021 at 23:19

3383

Use max-height in the transition and not height. And set a value on max-height to something bigger than your box will ever get.

See JSFiddle demo provided by Chris Jordan in another answer here.

#menu #list {
    max-height: 0;
    transition: max-height 0.15s ease-out;
    overflow: hidden;
    background: #d5d5d5;
}

#menu:hover #list {
    max-height: 500px;
    transition: max-height 0.25s ease-in;
}
<div id="menu">
    <a>hover me</a>
    <ul id="list">
        <!-- Create a bunch, or not a bunch, of li's to see the timing. -->
        <li>item</li>
        <li>item</li>
        <li>item</li>
        <li>item</li>
        <li>item</li>
    </ul>
</div>

6

  • 424

    this works great! except there is a delay when it starts, because it starts for max-height which initially is very high..hmm, i think this is somewhat annoying

    – vsync

    Dec 5, 2011 at 16:03

  • 205

    +1 Great solution! The speed of the transition is calculated is calculated as the time you specify to transition to the max-height value… but since height will be less than max-height, the transition to actual height will occur faster (often significantly) than the time specified.

    Mar 3, 2012 at 4:15

  • 76

    Note that this may cause ugly transition ending when you have to use values that are much bigger than the actual computed value. I noticed this while trying to make a div grow from 0 height to the content height that varies greatly due to different screen sizes(2 lines on my 2560×1440 monitor vs >10 lines on a smartphone). For this I ended up going with js.

    May 17, 2013 at 10:22

  • 72

    Very ugly solution since it creates a delay in one direction but not the other.

    – Tim

    Nov 20, 2013 at 14:57

  • 115

    This is a pretty lazy solution. I’m assuming OP wants to use height : auto because the expanded height of the container is somewhat unpredictable. This solution will cause a delay before the animation becomes visible. Additionally the visible duration of the animation will be unpredictable. You’ll get much more predictable (and likely smoother) results by calculating the combined height of each of the containers child nodes and then easing to an exact height value.

    Apr 16, 2014 at 22:06

422

You should use scaleY instead.

ul {
  background-color: #eee;
  transform: scaleY(0);    
  transform-origin: top;
  transition: transform 0.26s ease;
}
p:hover ~ ul {
  transform: scaleY(1);
}
<p>Hover This</p>
<ul>
  <li>Coffee</li>
  <li>Tea</li>
  <li>Milk</li>
</ul>

I’ve made a vendor prefixed version of the above code on jsfiddle, and changed your jsfiddle to use scaleY instead of height.

Edit
Some people do not like how scaleY transforms the content. If that is a problem then I suggest using clip instead.

ul {
  clip: rect(auto, auto, 0, auto);
  position: absolute;
  margin: -1rem 0;
  padding: .5rem;

  color: white;

  background-color: rgba(0, 0, 0, 0.8);

  transition-property: clip;
  transition-duration: 0.5s;
  transition-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1.275);
}
h3:hover ~ ul,
h3:active ~ ul,
ul:hover {
  clip: rect(auto, auto, 10rem, auto);
}
<h3>Hover here</h3>
<ul>
  <li>This list</li>
  <li>is clipped.</li>
  <li>A clip transition</li>
  <li>will show it</li>
</ul>
<p>
  Some text...
</p>

7

  • 280

    This method only partially achieves the desired effect but doesn’t actually remove the space. The transformed box acts like a relatively positioned element – the space is taken up no matter how it is scaled. Check out this jsFiddle which takes your first one and just adds some bogus text at the bottom. Note how the text below it doesn’t move up when the box height is scaled to zero.

    – animuson

    Jul 29, 2013 at 20:37


  • 10

    Now it does: jsfiddle.net/gNDX3/1 Basically you need to style your elements according to what you need. There is no silver bullet or widget like behavior in CSS/HTML.

    Aug 30, 2013 at 16:07


  • 102

    While I applaud someone trying different approaches, the real-world effect and complications this solution brings is far worse than the already awful max-height hack. Please do not use.

    – mystrdat

    Nov 18, 2013 at 19:53


  • 3

    “Now it does” except that the content below the disappearing element jumps up before the element slides away. Could be useful sometimes, however the point of transitions is to smoothly change visuals, not trade one stark jump for another.

    May 2, 2021 at 1:51

  • 4

    In effect, using a clip transform is no different than simply setting the max-height value. It still suffers from non-fixed amount of appearent animation delay with highly dynamic content size.

    Sep 5, 2021 at 23:52

260

You can’t currently animate on height when one of the heights involved is auto, you have to set two explicit heights.

0