Animate Intrinsic Size With CSS

A wood construction table with tools and rulers.
Josh Taylor

UX Developer

Josh Taylor

Your digital product needs to stand out from the competition and attract users at the same time. We help our partners do just that. Book a free consult to learn how we can do it for you.

For a long time, animating the size of UI elements with CSS has been a source of frustration.  Think of a time you were tasked with building a common UI pattern such as an accordion or drop-down menu. You likely needed to animate the height property, but quickly discovered this is not readily achievable with CSS alone.

When using CSS transitions, the browser cannot calculate the intrinsic size of an element. For example, you cannot animate an element’s size to an auto value. Instead, you would have to use JavaScript to calculate those values.

Modern CSS now empowers us with tools that make animating height more seamless. Here, we’ll explore how to use CSS Grid and the new calc-size() function.

Animating size with CSS Grid

CSS Grid is perhaps the cleanest browser-safe option that allows us to replicate intrinsic-size animations with CSS alone. For demonstration, the following snippet presents some basic HTML markup you might use to start building an accordion.

<ul>
  <li>
    <button type="button">Label</button>
    <!-- Content Container -->
    <div class="accordion-content">
      <p class="contents">
        <!-- Content Here -->
      </p>
    </div>
  </li>
  ...
</ul>

Following this example, we can set a few properties on the accordion’s content container for its initial “closed” state.  The fundamental properties are display: grid and grid-template-rows: 0fr with transition-property: grid-template-rows.  We’ll benefit from Grid’s fractional units, which allow us to hide our accordion content initially.  We’ll also require an inner element with overflow: hidden to ensure our content doesn’t overflow its container. When our accordion is in an “open” state we can update the value of grid-template-rows to 1fr. This wil allow our content to take up as much space as it needs.

The CSS would look like our next example. Note the use of the [data-open] attribute to set the accordion’s ‘open’ state.

.accordion-content {
  display: grid;
  grid-template-rows: 0fr;
  transition-property: grid-template-rows;
  transition-duration: 750ms;
}

.contents {
  overflow: hidden;
}

.accordion-content[data-open] {
  grid-template-rows: 1fr;
}

Here, we have a functional example of our accordion, which includes more detailed markup, additional CSS for presentation, and minimal JS to handle clicks.

This method works well but, it does have a couple drawbacks. For instance, you cannot add padding directly to the content element. While collapsed, the content will remain hidden but, the element will visibly expand to accommodate the added padding. Instead, you’ll need to apply padding to a new child element. Additionally, the Grid container and child elements may animate out of sync. When transitioning between 0 and 1fr, Grid will briefly create a second row, causing a slight stutter. Including grid-row: 1 / span 2; (grid-row-start 1, span 2 rows) on the Grid container should resolve this.

Introducing calc-size()

The new CSS function calc-size() holds great promise. It will allow developers to calculate intrinsic size natively.  According to the official CSS Working Group Drafts, calc-size() is similar to the calc() function, but also supports intrinsic size values such as, “auto, min-content, max-content, fit-content, stretch, or contain.” The function accepts two arguments, calc-size-basis & calc-sum.

Once again, we’ll start with an HTML snippet.

<ul>
  <li>
    <button type="button">Label</button>
    <!-- Content Container -->
    <div class="accordion-content">
      <p class="contents">
        <!-- Content Here -->
      </p>
    </div>
  </li>
  ...
</ul>

We can apply an initial state of height:0 on the content container, set transition-property to height, and include overflow: hidden to ensure any inner content doesn’t overflow its container.  When the accordion is in the “opened” state, we can update the height value to calc-size(auto, size).  Now, our element will expand as needed based on the intrinsic size of its content.

The CSS would look like the following. Notice the difference in comparison to the earlier example.

.accordion-content {
  height: 0;
  transition-property: height;
  transition-duration: 750ms;
  overflow: hidden;
}

.accordion-content[data-open] {
  height: calc-size(auto, size);
}

Here, we have a functional example of our accordion using calc-size. This example also includes more detailed markup, additional CSS for presentation, and minimal JS to handle clicks.

Note: Currently, the calc-size() function has limited browser support and is only available in Chrome. To view this animation, update Chrome to the latest stable release (129).

Now that Chrome supports calc-size(), broader browser support may not be far behind, as Firefox and Safari have already opened issues regarding this new feature. Visit caniuse to stay updated with the latest support.

According to CSS Working Drafts, animating intrinsic size is one of the most requested features. We can now begin to take advantage of calc-size() to make these complex calculations with only a few lines of CSS!  Optionally, we can already use CSS Grid in all major browsers to replicate intrinsic-size animations.

Newsletter

Stay in the Know

Get the latest news and insights on Elixir, Phoenix, machine learning, product strategy, and more—delivered straight to your inbox.

Narwin holding a press release sheet while opening the DockYard brand kit box