Question
I want to make a hidden section slide down using CSS transitions.
The element starts with height: 0 and overflow: hidden. On hover, I change it to height: auto, but instead of animating, it appears instantly.
If I start from a fixed height such as 40px and transition to height: auto, it first animates toward 0 and then suddenly jumps to the final size.
How can I create this expand/collapse effect using CSS only, without JavaScript?
#child0 {
height: 0;
overflow: hidden;
background-color: #dedede;
transition: height 1s ease;
}
#parent0:hover #child0 {
height: auto;
}
#child40 {
height: 40px;
overflow: hidden;
background-color: #dedede;
transition: height 1s ease;
}
#parent40:hover #child40 {
height: auto;
}
h1 {
font-weight: bold;
}
<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>
Short Answer
By the end of this page, you will understand why height: auto does not animate in CSS, what kinds of values CSS transitions can animate, and the most common CSS-only workaround: animating max-height. You will also see alternatives like transform and learn when each approach is appropriate.
Concept
CSS transitions work best when the browser can calculate both a clear starting value and a clear ending value.
For example, this is easy to animate:
height: 0px; /* start */
height: 200px; /* end */
The browser can generate the intermediate values: 20px, 40px, 60px, and so on.
But auto is different. It is not a fixed numeric value. It means: let the browser calculate the size based on the content and layout. Because of that, CSS cannot interpolate from 0px to auto during a transition.
That is why this does not animate:
height: 0;
transition: height 1s ease;
height: auto;
The browser jumps directly to the final layout instead of animating.
Why this matters
Expandable panels, dropdowns, accordions, menus, FAQ sections, and mobile navigation often need to grow and shrink based on content. Since content height is usually unknown ahead of time, developers often run into this exact limitation.
A common CSS-only workaround is to animate max-height instead of height:
- keep
heightcontrolled by the content - set
max-height: 0when collapsed - animate to a large enough
max-heightwhen expanded
This works because max-height uses numeric values, which CSS can animate.
Mental Model
Think of CSS transitions like animating a car trip between two addresses with exact coordinates.
height: 0pxis a known location.height: 200pxis another known location.height: autois like saying, "drive to wherever the content decides to be."
The browser cannot smoothly animate toward a destination that is not a fixed number.
Using max-height is like giving the browser a measurable limit instead:
- collapsed:
max-height: 0 - expanded:
max-height: 300px
Now the browser has two concrete numeric values and can animate between them.
Syntax and Examples
The most common CSS-only solution is to animate max-height.
Basic pattern
.panel {
max-height: 0;
overflow: hidden;
transition: max-height 0.4s ease;
}
.container:hover .panel {
max-height: 300px;
}
Example
<div class="menu">
<button>Hover me</button>
<div class="panel">
<p>Item 1</p>
<p>Item 2</p>
<p>Item 3</p>
</div>
</div>
.menu .panel {
max-height: 0;
overflow: hidden;
background: #dedede;
transition: max-height 0.5s ease;
}
.menu:hover .panel {
max-height: 200px;
}
Why it works
- The real content height can still be determined naturally.
overflow: hiddenprevents hidden content from showing.max-heighttransitions from0to200pxsmoothly.
Important note
The expanded max-height must be large enough to fit the content. If the content becomes taller than that value, it will be cut off.
Alternative: transform
If the visual effect is more important than the document layout, you can animate a scale effect:
.panel {
transform: scaleY(0);
transform-origin: top;
transition: transform 0.3s ease;
}
.container:hover .panel {
transform: scaleY(1);
}
This looks like a slide/open effect, but it does not resize layout in the same way as height-based expansion.
Step by Step Execution
Consider this example:
<div class="box">
<h2>Hover me</h2>
<div class="content">Hello<br>Hello<br>Hello</div>
</div>
.content {
max-height: 0;
overflow: hidden;
transition: max-height 1s ease;
}
.box:hover .content {
max-height: 100px;
}
What happens step by step
- The page loads.
.contenthasmax-height: 0, so it is fully collapsed.overflow: hiddenhides anything that does not fit inside that height.- When the user hovers over
.box, the rule.box:hover .contentbecomes active. max-heightchanges from0to100px.- Because both values are numeric, the browser animates the change.
- During the animation, the visible area grows until the content fully appears.
Why height: auto fails in the same scenario
If you wrote this instead:
.content {
height: 0;
transition: height 1s ease;
}
.box:hover .content {
height: auto;
}
the browser would know the start value (0), but the end value (auto) is not a number it can animate through frame by frame. So it jumps.
Real World Use Cases
This pattern appears in many common UI elements.
Dropdown menus
A navigation menu may reveal links on hover or click.
Accordion sections
FAQ pages often expand and collapse answers of different lengths.
Mobile navigation
A menu panel may open to show a list of routes.
Notification details
A compact card may expand to show more information.
Filter panels
Search interfaces often hide advanced filters until needed.
In all of these cases, the final height depends on content, which is why developers often use max-height or JavaScript-assisted measurement.
Real Codebase Usage
In real projects, developers usually choose one of these patterns:
1. max-height for simple CSS-only UI
Good for:
- small dropdowns
- FAQ sections with predictable size
- simple hover interactions
Pattern:
.section {
max-height: 0;
overflow: hidden;
transition: max-height 0.3s ease;
}
.section.is-open {
max-height: 500px;
}
2. JavaScript-measured height for precise animations
When content size is unknown or dynamic, developers often measure the element's scrollHeight and animate to that pixel value.
This avoids guessing a max-height.
3. transform for performance-focused effects
Developers sometimes animate transform because it is often smoother and cheaper for the browser to render. However, this is mainly a visual effect and may not affect surrounding layout as expected.
4. Class-based state changes
In real codebases, hover is often replaced by classes such as .is-open, especially for click-driven interfaces:
.panel {
max-height: 0;
overflow: hidden;
transition: max-height 0.3s ease;
}
.panel.is-open {
max-height: 300px;
}
This makes the code easier to manage and reuse.
Common supporting patterns
overflow: hiddento clip content during animation- guard values like
max-height: 999pxwhen content size varies - early state classes like
.is-openand.is-collapsed - reduced-motion support for accessibility
Common Mistakes
1. Trying to transition to auto
Broken example:
.panel {
height: 0;
transition: height 0.4s ease;
}
.panel:hover {
height: auto;
}
Why it fails:
autois not an animatable numeric endpoint forheight.
Use this instead:
.panel {
max-height: 0;
overflow: hidden;
transition: max-height 0.4s ease;
}
.panel:hover {
max-height: 200px;
}
2. Forgetting overflow: hidden
Broken example:
.panel {
max-height: 0;
transition: max-height 0.4s ease;
}
Problem:
- The content may still be visible even though the container is collapsed.
Fix:
.panel {
max-height: 0;
overflow: hidden;
}
3. Choosing a max-height that is too small
If the content is taller than the expanded value, part of it will be cut off.
.panel.open {
max-height: 100px; /* too small for content */
}
Choose a safe value:
.panel.open {
max-height: 500px;
}
4. Using hover for important interactions on touch devices
Hover works poorly or differently on mobile devices.
Prefer click-based toggles for production interfaces.
5. Animating the wrong property
Sometimes beginners animate display:
.panel {
display: none;
transition: display 0.3s ease;
}
This does not work because display is not animatable.
Use animatable properties like:
max-heightopacitytransform
Comparisons
| Approach | Can animate smoothly? | Affects layout? | Best for | Limitation |
|---|---|---|---|---|
height: 0 to height: auto | No | Yes | Not recommended | auto is not animatable |
max-height: 0 to max-height: 300px | Yes | Yes | Simple CSS-only expand/collapse | Must guess a large enough max value |
transform: scaleY(0) to scaleY(1) | Yes | Usually no real layout resizing | Pure visual open/close effects | Can look squashed; layout behavior differs |
opacity: 0 to opacity: 1 | Yes | No | Fade effects | Does not create slide/height expansion |
| JavaScript with measured height | Yes | Yes | Dynamic content with exact sizing | Requires scripting |
height vs max-height
| Property | Good for animation? | Notes |
|---|---|---|
height | Yes, if both values are fixed lengths | Example: 0px to 200px |
height: auto | No | Browser cannot interpolate auto |
max-height | Yes | Common workaround for unknown content height |
Cheat Sheet
/* CSS-only expand/collapse pattern */
.panel {
max-height: 0;
overflow: hidden;
transition: max-height 0.3s ease;
}
.panel.is-open {
max-height: 300px;
}
Key rules
height: autodoes not transition.heightcan transition only between numeric values like0pxand200px.max-heightis the usual CSS-only workaround.- Add
overflow: hiddento hide collapsed content. - Pick a
max-heightlarge enough for the content.
Useful alternatives
/* Visual-only open effect */
.panel {
transform: scaleY(0);
transform-origin: top;
transition: transform 0.3s ease;
}
.panel.is-open {
transform: scaleY(1);
}
Not animatable for this use case
height: autodisplay: nonetodisplay: block
Quick decision guide
- Need CSS-only and simple? Use
max-height. - Need exact dynamic height? Use JavaScript.
- Need a visual effect only? Use
transform.
FAQ
Why can't CSS transition height: auto?
Because auto is not a fixed numeric value. The browser cannot calculate intermediate values between 0px and auto.
What is the best CSS-only replacement for height: auto animation?
Usually max-height with overflow: hidden.
Is max-height a perfect solution?
No. You must choose an upper limit, and if the content grows beyond it, the content may be clipped.
Can I use display: none and transition it?
No. display is not animatable.
Should I use hover for dropdowns?
For simple desktop effects, yes. For important navigation or mobile-friendly UI, click-based interactions are usually better.
Is transform: scaleY() better than max-height?
It depends. transform can be smoother visually, but it does not behave like true layout expansion.
When should I use JavaScript instead?
Use JavaScript when the content height is unknown, changes dynamically, or must animate precisely to its actual size.
Mini Project
Description
Build a simple FAQ item that expands and collapses using only CSS. This demonstrates the common max-height technique for creating a slide-down effect when the content height is not known in advance.
Goal
Create a CSS-only expandable answer panel that smoothly opens and closes without using height: auto in the transition.
Requirements
- Create a question-and-answer block with a visible title.
- Keep the answer hidden by default.
- Reveal the answer with a smooth animation using CSS only.
- Prevent hidden content from overflowing while collapsed.
- Use a technique that works around the
height: autolimitation.
Keep learning
Related questions
Angular ngClass Conditional Class Binding Explained
Learn how to use Angular ngClass for conditional classes, fix common binding mistakes, and understand why this template error happens.
CSS :not() Selector for Elements Without a Class or Attribute
Learn how to use the CSS :not() selector to target elements that do not have a specific class or attribute, with examples and common pitfalls.
CSS Font Scaling Relative to Container Size: %, em, rem, vw, and Responsive Text
Learn how CSS font scaling really works and how to make text responsive using %, em, rem, vw, clamp(), and media queries.