<br />

Web features: [calc-size()](https://webstatus.dev/features/calc-size) [interpolate-size](https://webstatus.dev/features/interpolate-size)

> [!NOTE]
> **Note:** These guides are authored for AI coding agents (apologies for the robotic tone!). To have your coding agent adopt these patterns automatically, check out [Modern Web Guidance](https://github.com/GoogleChrome/modern-web-guidance).

Animating elements to dynamic sizes like `block-size: auto` or `inline-size: max-content` has historically required JavaScript or fragile "max-height" hacks. The `interpolate-size` property and `calc-size()` function allow the browser to natively interpolate between fixed lengths and intrinsic sizing keywords.

## Implementation steps

1. **Opt-in to keyword interpolation** : Apply `interpolate-size: allow-keywords` to a parent element (typically `:root`) to enable transitions for properties using intrinsic keywords.
2. **Define the transition** : Set a `transition` for the sizing property (e.g., `block-size`, `inline-size`) on the target element.
3. **Use intrinsic keywords** : Change the sizing property to a supported intrinsic keyword---`auto`, `min-content`, `max-content`, `fit-content`, or (for flex-basis) `content`---during an interaction (e.g., `:hover` or a state class).
4. **Perform calculations (Optional)** : Use `calc-size()` if you need to perform math on an intrinsic size (e.g., `auto + 2rem`). `calc-size()` also supports the `any` keyword for basis-agnostic calculations.

## Example: Generic Expansion Pattern

You can apply this pattern to any container (like a "Show More" section or a navigation menu) to transition between a restricted height and the element's natural size.

```css
/* Opt-in globally for all children */
:root {
  /* MANDATORY: Transitions to intrinsic keywords are disabled by default for compatibility */
  interpolate-size: allow-keywords;
}

.expandable-container {
  /* 1. Define a fixed initial size (or 0) and hide overflow */
  block-size: 100px;
  overflow: hidden;
  
  /* 2. Transition the sizing property */
  transition: block-size 0.4s ease-out;
}

.expandable-container.is-expanded {
  /* 3. Smoothly animate to the intrinsic natural height */
  block-size: auto;
}
```

## Example: Calculated Intrinsic Inline-Size

```css
.badge {
  inline-size: 40px;
  overflow: hidden;
  white-space: nowrap;
  transition: inline-size 0.3s ease;
}

.badge:is(:hover, :focus-visible) {
  /* calc-size(basis, calculation) */
  /* 'size' refers to the evaluated basis (max-content in this case) */
  inline-size: calc-size(max-content, size + 20px);
}
```

## Example: Transition from Intrinsic to Fixed

You can also animate in the opposite direction---starting from a natural size and collapsing to a specific length. This is useful for "dismissible" components.

```css
.collapsible-alert {
  /* 1. Start with the natural content height */
  block-size: auto;
  overflow: hidden;
  transition: block-size 0.5s ease-in-out, opacity 0.5s ease;
}

.collapsible-alert.is-dismissed {
  /* 2. Smoothly collapse to zero */
  block-size: 0;
  opacity: 0;
  pointer-events: none;
}

/* MANDATORY Copy-Paste Safety: Disable sizing animations for sensitive users */
@media (prefers-reduced-motion: reduce) {
  .expandable-container,
  .badge,
  .collapsible-alert {
    transition: none !important;
  }
}
```

```javascript
// MANDATORY Accessibility Synchronization: Ensure elements collapsed to zero dimensions are removed from the assistive technology tree, and sync aria-expanded states on triggers.
const alertElement = document.querySelector('.collapsible-alert');
function dismissAlert() {
  alertElement.classList.add('is-dismissed');
  // Waits for the transitions the class starts, ignoring any other animations
  // on the element; under reduced motion there are none
  const transitions = alertElement.getAnimations().filter((animation) => animation instanceof CSSTransition);
  Promise.allSettled(transitions.map((transition) => transition.finished)).then(() => {
    if (alertElement.classList.contains('is-dismissed')) {
      alertElement.hidden = true;
    }
  });
}

// Example trigger syncer
const triggerBtn = document.querySelector('.accordion-trigger');
triggerBtn?.addEventListener('click', () => {
  const isExpanded = triggerBtn.getAttribute('aria-expanded') === 'true';
  triggerBtn.setAttribute('aria-expanded', !isExpanded);
});
```

## Key constraints

- **Keyword-to-Keyword Restriction** : You cannot animate between two different keywords directly (e.g., from `min-content` to `max-content`). One end of the transition must be a fixed length or percentage (e.g., `0` to `auto`).
- **Calc-size Syntax** : Inside `calc-size()`, you cannot mix different intrinsic keywords in the same expression. The first argument (the basis) defines what `size` represents.
- **Opt-in Requirement** : Transitions to intrinsic keywords are disabled by default (`numeric-only`) to maintain backward compatibility. You must apply `interpolate-size: allow-keywords` to the element or an ancestor. `calc-size()` acts as a per-property override, automatically enabling interpolation whenever it is used.

## Fallback strategies

<iframe src="https:///frame/docs/modern-web-guidance/guides/css/animate-to-intrinsic-sizes_e6cf1df257ab0660a8f58192fad14c0d884dcc6ec6b08017106931ee964abf39.frame" class="framebox inherit-locale " allow="clipboard-write https://" allowfullscreen is-upgraded></iframe>

<iframe src="https:///frame/docs/modern-web-guidance/guides/css/animate-to-intrinsic-sizes_cfee62bf93672823e71c7f683bb66a2459b76c529501242eb06796acf78488d2.frame" class="framebox inherit-locale " allow="clipboard-write https://" allowfullscreen is-upgraded></iframe>

`interpolate-size` and `calc-size()` are progressive enhancements. Browsers that do not support them will perform an instant jump to the target size.

- **Graceful Degradation** : For simple `block-size: auto` transitions, standard browsers will simply toggle the size instantly, which is functional but less polished.
- **Manual keyword fallbacks** : When using `calc-size()`, always provide a standard keyword fallback for older browsers, as they will discard the entire `calc-size()` declaration.

```css
.card {
  block-size: auto; /* Fallback for older browsers */
  block-size: calc-size(auto, size); /* Modern browsers use this */
  transition: block-size 0.3s ease;
}
```
Source: [animate-to-intrinsic-sizes guide on GitHub](https://github.com/GoogleChrome/modern-web-guidance-src/blob/main/guides/css/animate-to-intrinsic-sizes/guide.md)