DEV Community

Stephan Nijman
Stephan Nijman

Posted on

Mastering Smooth UI Transitions: The End of the "Height: Auto" Hack

In this article, I want to show you how to finally solve one of the most annoying hurdles in CSS development: animating elements with dynamic heights.

If you’ve been building for the web for a while, you’ve likely felt the frustration of trying to animate an accordion or a dropdown. For years, we’ve had to rely on "hacks" like animating max-height to a massive, arbitrary number or bringing in a JavaScript library just to measure pixels. It works, but it’s never felt like "the right way" to me.

Today, we have two new properties interpolate-size and transition-behavior that handle the math and the logic natively. Let's walk through how to implement them.

What is the "Auto" Problem?

The issue was never the animation itself, but the math. Browsers are great at calculating the distance between 0px and 500px, but they traditionally couldn't calculate the midpoint between 0px and a keyword like auto.

When you toggled a class, the browser would simply "snap" to the final height because it didn't have a numeric path to follow.

1. Animating Intrinsic Sizes with interpolate-size

To fix the math, we use interpolate-size. I like to think of this as giving the browser permission to calculate the "uncalculatable."

The best way to implement this is to set it at the :root level. Since it's an inherited property, you enable it once and it works for every component on your site.

:root {
  /* This tells the engine to allow animations for keywords like auto or fit-content */
  interpolate-size: allow-keywords;
}

.accordion-content {
  height: 0;
  overflow: hidden;
  transition: height 0.4s ease-in-out;
}

.accordion-content.is-open {
  /* Thanks to the root property, this now slides smoothly */
  height: auto; 
}
Enter fullscreen mode Exit fullscreen mode

2. Handling Discrete States with transition-behavior

Even with the height solved, we still have to deal with the display property. For a clean DOM and better accessibility, we usually want to use display: none when an element is hidden.

The problem? display is a discrete property—it’s either there or it isn't. Usually, if you toggle it, the element vanishes instantly, which kills your smooth height or opacity transition mid-flight.

transition-behavior: allow-discrete is the solution. It tells the browser to wait until the transition duration finishes before it actually flips the display switch.

.dropdown {
  display: none;
  opacity: 0;
  /* 'allow-discrete' ensures the element stays 'block' until the fade ends */
  transition: 
    display 0.3s allow-discrete, 
    opacity 0.3s;
}

.dropdown.is-open {
  display: block;
  opacity: 1;
}
Enter fullscreen mode Exit fullscreen mode

3. Putting it into Practice: The "Right Way"

When we combine these, we get a component that is performant, accessible, and smooth. Here is how I usually structure a panel that needs to both slide and fade.

I’ll explain these transition values from left to right: first we define the property (like display), then the duration (0.5s), and finally the behavior (allow-discrete).

:root {
  interpolate-size: allow-keywords;
}

.panel {
  display: none;
  height: 0;
  opacity: 0;
  overflow: hidden;
  /* Walkthrough: display waits for the others, height slides, opacity fades */
  transition: 
    display 0.5s allow-discrete, 
    height 0.5s, 
    opacity 0.5s;
}

.panel.is-active {
  display: block;
  height: auto;
  opacity: 1;
}
Enter fullscreen mode Exit fullscreen mode

4. Real-World Application and Support

I'm a big advocate for progressive enhancement. We don't need to wait for 100% support to start using these tools, as long as the fallback is acceptable.

transition-behavior: This is very stable now (about 89% support). It’s ready for prime time in Chrome, Firefox, and Safari.

interpolate-size: This is the newer piece of the puzzle (around 71%). It works beautifully in Chromium browsers, and the other engines are moving quickly to catch up.

If a user is on an older browser, they won't see a broken layout; the panel will just snap open instantly. In my experience, that's a perfectly fine fallback for the sake of writing cleaner, more maintainable CSS.

If you want to be extra careful, you can wrap your root opt-in in a feature query:

@supports (interpolate-size: allow-keywords) {
  :root {
    interpolate-size: allow-keywords;
  }
}
Enter fullscreen mode Exit fullscreen mode

This approach allows us to ditch the old 1000px max-height hacks and write code that finally does exactly what we intend. Give it a try on your next project—it’s a much more satisfying way to build.

Top comments (2)

Some comments may only be visible to logged-in visitors. Sign in to view all comments.