Skip to main content
Nikos Printezis

Nikos Printezis

Hi! I'm Nikos and I'm a full stack engineer. I like coding all sorts of stuff, although I focus more on front end development with native JS/CSS features, React and 2D games with PixiJS. I'm really looking forward to provide more and more content in this website and I hope you enjoy my blog and games.

How to animate from height 0 to auto

A computer screen showing dropdown menus

Vertical expansion is one of the classic animations every front-end engineer encounters. When a user clicks, hidden content is revealed line by line. While it’s straightforward to achieve this with JS, the addition of CSS transitions has made many developers eager to implement it using just CSS. But is it possible? Well… yes and no.

In this post, we’ll explore 4 different ways to accomplish animated vertical expansion.

1. Using the interpolate-size CSS property

Ideally, the following CSS snippet should be enough to achieve the transition with just CSS.

.hidden-content {
height: 0;
overflow: hidden;
transition: height 0.5s ease;
&.opened {
height: auto;
}
}

But it’s not. If you follow this approach, you’ll notice that the hidden content is revealed immediately and the transition is not happening (codepen).

The interpolate-size CSS property enables transitions of CSS intrinsic sizing keywords like auto.

:root {
interpolate-size: allow-keywords;
}
.hidden-content {
height: 0;
overflow: hidden;
transition: height 0.5s ease;
&.opened {
height: auto;
}
}

If you check this codepen you’ll notice that the transition works like a charm. Everything looks fantastic except one thing. At the time of writing, interpolate-size is only supported by Chrome (caniuse).

2. Using the calc-size CSS function

The calc-size CSS function enables mathematical calculations on intrinsic sizes…like auto.

.hidden-content {
height: 0;
overflow: hidden;
transition: height 0.5s ease;
&.opened {
height: calc-size(auto, size);
}
}

Here’s another codepen to try the function. Same as above, it works like a charm but is currently supported only by Chrome (caniuse).

3. Using max-height for the transition

If the resulting height wasn’t auto, but a high value (e.g. 1000px), the CSS transition would work just fine. However, the element would have a huge height, most probably resulting into a broken UI. But what about the good old max-height ? It can produce the same effect and not force a huge height.

.hidden-content {
max-height: 0;
overflow: hidden;
transition: max-height 0.5s ease;
&.opened {
max-height: 1000px;
}
}

There is a problem though. We have to set a high value for max-height but we don’t know how much is good enough. Should it be 100px, 1000px, 9999px ? We can set a really high value, but this affects the transition, because it’s different to go from 0 to 1000px in 500ms and different to go from 0 to 99999px in 500ms (codepen).

4. Letting JS give us a helping hand

This approach combines the power of CSS and JS to achieve a precise transition (codepen).

HTML:

<p>
<button type="button" id="toggle">Open</button>
</p>
<div class="hidden-content" id="hidden-content">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed leo ante,
convallis non blandit vitae, rutrum a neque. Nam efficitur, neque quis
faucibus consectetur, est est condimentum nibh, ut viverra nisi elit nec
augue. Suspendisse dictum nulla neque, nec sollicitudin ante facilisis vel.
Cras feugiat sagittis ligula, rutrum maximus massa rhoncus a. Pellentesque
lobortis vel metus vel vehicula. Aliquam lobortis mauris ac ex ornare, a
dignissim ipsum rutrum. Orci varius natoque penatibus et magnis dis parturient
montes, nascetur ridiculus mus. Mauris gravida pharetra libero, sollicitudin
sollicitudin magna maximus at. Donec sit amet neque orci. Suspendisse nec
massa nec purus malesuada rutrum vel et quam. Nulla eu elit non quam commodo
viverra. Sed gravida, erat at tristique elementum, justo tortor tincidunt
augue, ac vestibulum felis lectus id risus. Sed cursus, metus nec tristique
semper, justo tortor consequat arcu, ut vehicula nulla augue vitae lectus.
Fusce mauris tellus, vestibulum consequat mi dictum, ornare volutpat sem.
Phasellus ultrices, purus vitae ultrices cursus, nisi sapien venenatis tellus,
a commodo lacus ligula id magna. Pellentesque blandit, est id fermentum
aliquet, elit erat tincidunt turpis, dapibus fringilla ipsum nunc nec mi.
Vestibulum quis odio placerat, interdum velit eget, sodales orci. Donec vitae
metus eget turpis commodo sodales. Fusce pulvinar eros et ipsum gravida
aliquet. Mauris tristique nisi sed dui tincidunt suscipit. Phasellus ultrices
magna in gravida faucibus. Maecenas vel semper orci, vel feugiat nisi.
Phasellus luctus et dolor vitae faucibus. Maecenas vehicula leo in elit luctus
vehicula. Quisque eget posuere metus. Proin eu tellus dapibus, tristique
mauris a, maximus nisl. Proin blandit felis vel porttitor accumsan.
Suspendisse luctus ante vel ipsum auctor luctus. Etiam orci diam, ornare ac
aliquam sollicitudin, malesuada nec lorem. In hac habitasse platea dictumst.
Cras cursus tempus massa, sed lacinia diam maximus at. Quisque sed lacus quis
libero auctor aliquet eu et ipsum. Donec congue ante ut eleifend bibendum.
Pellentesque ut nunc non ligula elementum lobortis. Nulla viverra rhoncus
ligula, vitae mollis nulla convallis pulvinar. Integer et aliquam massa. Sed
accumsan erat nulla, nec blandit diam lobortis nec. Nulla fermentum, lectus
vel feugiat rhoncus, nunc lectus tempus eros, ac pellentesque ex elit quis
enim. Morbi eleifend elementum sollicitudin. Sed lorem ipsum, tincidunt sit
amet nibh a, euismod varius lorem. Lorem ipsum dolor sit amet, consectetur
adipiscing elit. Sed ac justo sed ante lobortis euismod et sed ligula. In in
neque tempus, interdum risus sed, lacinia lorem. Interdum et malesuada fames
ac ante ipsum primis in faucibus. Duis accumsan lectus in purus sollicitudin
placerat. Vivamus vel laoreet odio, vel mollis dui. Suspendisse sit amet
maximus leo, sed aliquam urna. Duis fringilla mi hendrerit, malesuada sapien
eget, efficitur augue. Quisque quis imperdiet risus, vel facilisis arcu.
</div>

CSS:

.hidden-content {
height: 0;
overflow: hidden;
transition: height 0.5s ease;
}

JS:

const toggle = document.getElementById('toggle');
const hiddenContent = document.getElementById('hidden-content');
const openHiddenContent = () => {
const realHeight = hiddenContent.scrollHeight;
hiddenContent.addEventListener(
'transitionend',
() => {
hiddenContent.style.height = 'auto';
},
{ once: true },
);
hiddenContent.style.height = `${realHeight}px`;
};
const closeHiddenContent = () => {
const realHeight = hiddenContent.scrollHeight;
hiddenContent.style.height = `${realHeight}px`;
setTimeout(() => {
hiddenContent.style.height = '0';
}, 0);
};
toggle.addEventListener('click', () => {
if (toggle.innerHTML === 'Open') {
openHiddenContent();
toggle.innerHTML = 'Close';
} else {
closeHiddenContent();
toggle.innerHTML = 'Open';
}
});