Why Direct Width and Height Animation Falls Short
When you animate the width or height properties directly, the browser must recalculate the positions and sizes of all affected elements. This process, called a reflow, can cascade through your entire page layout. The browser needs to determine which children, siblings, and parent elements are impacted by each change and update them accordingly. After reflow completes, any pixels that have changed require a repaint to update the visual output.
- Reflow vs Repaint: Layout recalculation followed by pixel updates
- Main Thread Blocking: CPU-intensive calculations that prevent smooth JavaScript execution
- Frame Rate Impact: Difficulty maintaining 60fps with layout-triggering animations
As documented by MDN's CSS Triggers reference, animating layout properties triggers both reflow and repaint operations, placing significant load on the browser's main thread.
The Squish Effect Explained
When developers use transform: scale() to work around performance issues, content gets stretched or squished rather than cleanly revealed. A rounded rectangle with border-radius: 20px becomes an oval shape when scaled horizontally because the GPU treats the element as a flat texture, warping all visual properties uniformly.
This distortion occurs because transform: scale() affects the entire element as a composite layer, preserving none of the intrinsic proportions that make your UI design visually appealing. The corners don't maintain their radius, text reflows unpredictably, and images appear stretched.
For professional interfaces, this visual degradation is unacceptable. Your website deserves better--animations should enhance the experience, not compromise it. Our front-end development team specializes in building polished interfaces with smooth, performant animations that delight users.
If you're working with CSS layouts and want to understand how different properties affect rendering performance, our guide on using em vs rem CSS provides essential context for responsive sizing.
The Modern Solution: interpolate-size
The interpolate-size property enables smooth transitions between numerical values (like 100px) and keyword values (like auto). This breakthrough in CSS animation capabilities eliminates the need for complex workarounds while maintaining excellent performance.
html {
interpolate-size: allow-keywords;
}
.collapsible-element {
height: 0;
overflow: hidden;
transition: height 400ms ease-out;
}
.collapsible-element.expanded {
height: auto;
}
When interpolate-size: allow-keywords is applied, the browser internally calculates the computed height of auto and interpolates smoothly between the starting and ending values. As explained by Josh W. Comeau's interactive demo, this calculation happens once at transition start, not on every frame, making it far more efficient than continuous reflows.
Browser Support Status
Browser support for interpolate-size has expanded significantly:
- Chrome/Edge: Version 131+ (November 2024)
- Firefox: Version 129+ (September 2024)
- Safari: Version 18.1+ (October 2024)
- Opera: Version 117 and later
For projects requiring broader compatibility, feature detection allows graceful degradation:
@supports (interpolate-size: allow-keywords) {
html {
interpolate-size: allow-keywords;
}
}
Without support, elements simply snap between states rather than animating--a functional but less polished experience. This progressive enhancement approach ensures your front-end development works for all users while providing enhanced experiences where supported.
The property is inheritable, meaning you can set it on the html element and all descendants automatically benefit. This "set and forget" approach makes it ideal for global CSS resets or framework defaults.
Traditional Technique: 9-Slice Scaling
Before interpolate-size, developers used 9-slice scaling to animate dimensions without distortion. This technique, documented by PQINA's technical guide, borrows from game development and mobile app design where fixed-size corner elements prevent distortion during scaling.
The 9-Slice Concept
Imagine dividing an element into a 3×3 grid:
┌─────┬─────┬─────┐
│ 1 │ 2 │ 3 │
├─────┼─────┼─────┤
│ 4 │ 5 │ 6 │
├─────┼─────┼─────┤
│ 7 │ 8 │ 9 │
└─────┴─────┴─────┘
Corners (1, 3, 7, 9): Fixed size and position--these maintain your border-radius and corner treatments Edges (2, 4, 6, 8): Stretch along one axis--these extend your borders and backgrounds Center (5): Scale in both directions--this fills the remaining space with content or background
This ensures decorative features like border radius maintain their proportions throughout the animation. For buttons, cards, and other UI components with consistent styling, 9-slice scaling produces pixel-perfect results.
When to Use 9-Slice Scaling
While more complex to implement, 9-slice scaling remains valuable for:
- Fixed-size decorative elements like buttons and cards
- Components with complex border-radius or background patterns
- Animations requiring absolute visual fidelity
- Scenarios where the final element dimensions are known in advance
The technique requires significant markup and CSS complexity but works best for elements with consistent decorative styling rather than complex content. For most modern projects, interpolate-size provides a simpler alternative.
Transform-Based Alternative: The Sliding Technique
Another proven approach, discussed on Stack Overflow, uses transform: translate() to slide content out of view without affecting layout. This technique creates a collapse effect similar to jQuery's slideUp() method while remaining GPU-accelerated.
Three-Layer Structure
The technique uses three nested div elements, each serving a specific purpose:
<div class="outer-container">
<div class="inner-container">
<div class="content">
<!-- Your collapsible content here -->
</div>
</div>
</div>
.outer-container {
height: fit-content;
overflow: hidden;
}
.inner-container {
height: 100%;
overflow: hidden;
}
.content {
height: 100%;
}
/* Collapse state */
.outer-container.collapsed .inner-container {
transform: translateY(-100%);
}
.outer-container.collapsed .content {
transform: translateY(100%);
}
By translating the inner container upward and content downward at matching rates, the element appears to collapse smoothly. The surrounding elements shift to fill the gap because the outer container's height animates naturally when set to fit-content.
Handling Dynamic Heights
For elements with dynamic content, JavaScript captures the current height and applies it as a CSS variable:
const container = document.querySelector('.outer-container');
const height = container.querySelector('.inner-container').offsetHeight;
document.documentElement.style.setProperty('--collapse-height', `${height}px`);
container.classList.add('collapsed');
This approach ensures that elements following the collapsible section move up by the exact amount of space being removed. It's particularly useful for responsive web design patterns where content height varies across breakpoints.
For more insights on building responsive layouts, explore our responsive web design services to create fluid interfaces that adapt beautifully across all devices.
Performance Optimization Strategies
Choosing the Right Technique
| Technique | Use Case | Performance | Complexity |
|---|---|---|---|
interpolate-size | Modern browsers, collapsible sections | Excellent | Low |
| 9-slice scaling | Fixed-size decorative elements | Excellent | High |
| Transform translate | Accordions, modals, sliding panels | Excellent | Medium |
Hardware Acceleration Tips
Even GPU-accelerated properties benefit from optimization:
.animated-element {
will-change: transform;
transform: translateZ(0);
backface-visibility: hidden;
}
The will-change property hints to the browser that an element will animate, allowing it to promote the element to its own compositor layer. The translateZ(0) hack achieves the same result with broader compatibility. These techniques prevent unnecessary recalculations during the animation itself.
Avoiding Common Pitfalls
- Overflow Hidden: Always apply
overflow: hiddento animated containers to prevent content from "bleeding" during animation - Fixed Origins: Set
transform-originexplicitly when scaling to prevent unexpected visual shifts - Subpixel Rendering: Use whole pixel values where possible to prevent subpixel anti-aliasing during animation
- Composite Layers: Don't overuse
will-change--too many compositor layers consume GPU memory
For optimal website performance, test your animations on target devices. What performs smoothly on a desktop might struggle on mobile. Use browser dev tools to profile frame rates and identify bottlenecks before deployment.
Remember that smooth animations contribute to perceived quality. Users might not articulate why one site feels more polished than another, but the difference often comes down to animation performance. Our website performance optimization services can help ensure your animations run smoothly across all devices.
Understanding CSS unit choices like em vs rem can also impact layout performance and responsive behavior.
Complete Implementation: Accordion Component
Here is a production-ready accordion component using interpolate-size:
<style>
html {
interpolate-size: allow-keywords;
}
.accordion {
border: 1px solid #e0e0e0;
border-radius: 8px;
overflow: hidden;
}
.accordion-item {
border-bottom: 1px solid #e0e0e0;
}
.accordion-item:last-child {
border-bottom: none;
}
.accordion-header {
padding: 16px 20px;
background: #f8f9fa;
cursor: pointer;
display: flex;
justify-content: space-between;
align-items: center;
font-weight: 600;
}
.accordion-header:hover {
background: #f0f0f0;
}
.accordion-icon {
transition: transform 300ms ease;
}
.accordion-item.open .accordion-icon {
transform: rotate(180deg);
}
.accordion-content {
height: 0;
overflow: hidden;
transition: height 400ms cubic-bezier(0.4, 0, 0.2, 1);
}
.accordion-item.open .accordion-content {
height: auto;
}
.accordion-body {
padding: 20px;
}
</style>
<div class="accordion">
<div class="accordion-item">
<div class="accordion-header">
What is CSS interpolation?
<span class="accordion-icon">▼</span>
</div>
<div class="accordion-content">
<div class="accordion-body">
<p>Interpolation is the process of calculating intermediate values between two endpoint values during an animation transition.</p>
</div>
</div>
</div>
<div class="accordion-item">
<div class="accordion-header">
How does will-change improve performance?
<span class="accordion-icon">▼</span>
</div>
<div class="accordion-content">
<div class="accordion-body">
<p>The will-change property informs the browser that an element will be modified, allowing it to optimize rendering in advance.</p>
</div>
</div>
</div>
</div>
<script>
document.querySelectorAll('.accordion-header').forEach(header => {
header.addEventListener('click', () => {
const item = header.parentElement;
item.classList.toggle('open');
});
});
</script>
This implementation uses modern CSS where supported while gracefully degrading on older browsers. For the best user experience across all browsers, consider combining this approach with the transform-based sliding technique as a fallback.
The key is testing: verify your animations perform smoothly on the devices your audience actually uses.
Conclusion
Animating width and height without the squish effect has evolved from a complex challenge to a straightforward task. The interpolate-size property provides a clean, performant solution for modern projects, while 9-slice scaling and transform-based techniques offer reliable fallbacks when broader compatibility is required.
Key Takeaways
- Avoid direct layout animation: Animating
width/heightdirectly triggers costly reflows that impact performance - Embrace modern CSS: Use
interpolate-size: allow-keywordsfor the cleanest solution with modern browsers - Choose appropriate fallbacks: 9-slice scaling or transform techniques when supporting older browsers
- Test on target devices: Performance varies across devices--profile your animations before deployment
Implementation Guidance
For new projects, start with interpolate-size and add fallback techniques only when analytics indicate significant browser share among unsupported versions. For existing projects, consider a progressive enhancement approach where base functionality works everywhere and smooth animations enhance the experience for modern browser users.
The key insight is understanding why traditional animations fail: layout-triggering properties force the browser to recalculate element positions, consuming CPU cycles and causing jank. By using GPU-accelerated properties like transform or leveraging new CSS capabilities like interpolate-size, you create smooth, professional animations that enhance user experience without sacrificing performance.
Ready to implement smooth animations in your project? Our web development team can help you build performant, polished interfaces that delight users. Contact us to discuss how we can bring your vision to life.
Frequently Asked Questions
Sources
-
PQINA: Animating Width and Height Without the Squish Effect - Comprehensive guide to 9-slice scaling technique and reflow vs transform explanation
-
Josh W. Comeau: Keyword Transitions - Animate height: 0px to height: auto - In-depth explanation of the interpolate-size CSS property with interactive examples
-
Stack Overflow: How to animate height with transform without squishing content - Developer discussion of transform-based approaches and the three-layer technique
-
MDN Web Docs: CSS Triggers - Official documentation on which CSS properties trigger reflow, repaint, or compositing