Understanding CSS Animation Fill Mode
CSS animations bring websites to life, but by default, elements snap back to their original state when animations complete. The animation-fill-mode property solves this problem by controlling what styles apply before an animation starts and after it ends.
Without animation-fill-mode, your carefully crafted animations create jarring user experiences where elements reset instantly instead of maintaining their final positions. This property gives you precise control over every phase of the animation lifecycle, ensuring smooth transitions that feel natural and intentional.
According to the MDN Web Docs animation-fill-mode documentation, this property specifies how a CSS animation should apply styles before the first animation iteration begins and after it ends. Understanding this property is essential for creating polished, professional animations that enhance rather than disrupt user experience.
Key Takeaways
- animation-fill-mode controls styles during animation-delay periods
- It determines what styles persist after animation completion
- Four values: none, forwards, backwards, and both
- Essential for creating smooth, professional user experiences
By mastering animation-fill-mode, you gain fine-grained control over how elements appear throughout the entire animation lifecycle--from the moment an animation should start until well after it completes.
The Problem with Default Animation Behavior
By default, CSS animations have a limitation that creates jarring user experiences across countless websites. Understanding this limitation is the first step toward creating truly polished animations.
Before Animation Starts
During the animation-delay period, elements display their original CSS styles rather than any animation starting state. This means users see a "flash of unstyled content" where the element appears in its final position before the animation even begins. For entrance animations, this defeats the entire purpose of showing a smooth transition.
After Animation Ends
Elements immediately snap back to their original styles when the animation completes. This is perhaps the most common animation problem on the web--imagine a modal that fades in beautifully only to instantly disappear, or a button that slides into position but then immediately jumps back to its starting point.
Real-World Examples
- Hero section animations: Text that fades in nicely but then loses its opacity state and reappears at full visibility
- Form validation: Input fields that animate to an error state but then snap back, confusing users about the error status
- Navigation menus: Mobile menus that slide in smoothly but then appear static and unresponsive
- Card entrances: Product cards that animate into position but then lose their elevated state
- Loading indicators: Spinners that seem to reset between each rotation cycle
These problems occur because the default behavior treats animations as temporary visual effects rather than state changes. When you want an animation to represent a permanent or semi-permanent change to an element's appearance, the default behavior works against you.
The solution is animation-fill-mode, which gives you explicit control over how elements should appear during both the delay period and after completion.
The Four Values of Animation Fill Mode
The animation-fill-mode property accepts four distinct values, each controlling animation behavior differently. Understanding when to use each value is key to creating professional animations.
1. none (Default)
The none value represents the default animation behavior where no styles are applied before the animation starts, and no styles are retained after the animation ends. The element displays its original CSS rules throughout the entire animation lifecycle.
.element {
animation: slideIn 1s ease none;
}
Use this value when you want complete animation reset after completion, such as for looping animations or temporary visual effects that should not persist.
2. forwards
The forwards value causes the element to retain the computed values set by the last keyframe encountered during animation execution. This is essential for animations that should leave elements in their final animated state.
.element {
animation: slideIn 1s ease forwards;
}
The last keyframe depends on the values of animation-direction and animation-iteration-count. According to MDN Web Docs, the mapping works as follows:
| animation-direction | animation-iteration-count | Last Keyframe |
|---|---|---|
| normal | even or odd | 100% or to |
| reverse | even or odd | 0% or from |
| alternate | even | 0% or from |
| alternate | odd | 100% or to |
| alternate-reverse | even | 100% or to |
| alternate-reverse | odd | 0% or from |
3. backwards
The backwards value causes the animation to immediately apply the values from the first keyframe during the animation-delay period. This prevents the "flash of unstyled content" during the delay period.
.element {
animation: slideIn 1s ease backwards;
animation-delay: 0.5s;
}
This is particularly useful for entrance animations where you want the element to appear in its "starting" state immediately, rather than waiting for the animation to begin. Without backwards, users would see the element in its original state during the delay.
4. both
The both value combines forwards and backwards--the element applies first keyframe styles during the delay period AND retains last keyframe styles after completion. This provides complete control over both animation boundaries.
.element {
animation: slideIn 1s ease both;
animation-delay: 0.5s;
}
This is often the best choice for entrance animations where you want the element to appear in its starting state immediately during any delay, and then maintain its final position after the animation completes. It provides the smoothest experience for users.
For a practical introduction to CSS animations, see our guide on how to add CSS to HTML which covers animation fundamentals.
| Value | Before Animation | After Animation |
|---|---|---|
| none | Original styles | Original styles |
| forwards | Original styles | Last keyframe |
| backwards | First keyframe | Original styles |
| both | First keyframe | Last keyframe |
Combining Animation Fill Mode with Other Properties
Animation-fill-mode does not work in isolation--it interacts with other CSS animation properties to create sophisticated animation behaviors. Understanding these interactions is essential for predictable animations.
Interaction with Animation Direction
The animation-direction property changes the playback direction of animations, which directly impacts what the "last keyframe" means for forwards:
- normal: Animations play from 0% to 100%, so forwards uses the to or 100% keyframe
- reverse: Animations play from 100% to 0%, so forwards uses the from or 0% keyframe
- alternate: Animations alternate between normal and reverse, with forwards using different end states depending on whether the final iteration is even or odd
- alternate-reverse: Alternate in the reverse direction, with the opposite pattern
Interaction with Animation Iteration Count
The animation-iteration-count property determines how many times an animation plays:
- 1 (default): Animation plays once, making forwards straightforward--the last keyframe is the final state
- infinite: Animation never ends, so forwards has no visible effect since there's no "after" state
- Multiple values: Forwards uses the last completed iteration's final state, which may differ from the first iteration if using alternate direction
Interaction with Animation Delay
The animation-delay property specifies when an animation should begin, and it's where backwards behavior matters most:
- With backwards: First keyframe applies immediately during delay, preventing flash of unstyled content
- With forwards: Last keyframe applies after animation completes
- With both: Both behaviors apply at appropriate times
Multiple Animations with Different Fill Modes
You can apply multiple animations to a single element, each with different fill modes:
.element {
animation:
slideIn 0.5s ease forwards,
fadeIn 0.3s ease backwards;
}
This technique allows you to control different properties with different fill mode behaviors, giving you maximum flexibility in complex animation scenarios.
Practical Code Examples
The following examples demonstrate real-world applications of animation-fill-mode in modern web development projects.
Example 1: Button Hover Animation with Forwards
This example creates a button that slides and changes color on hover, maintaining its final state using forwards:
@keyframes buttonSlide {
from {
transform: translateX(0);
background-color: #3b82f6;
}
to {
transform: translateX(100px);
background-color: #1d4ed8;
}
}
.animated-button {
animation: buttonSlide 0.3s ease forwards;
}
.animated-button:hover {
animation: buttonSlide 0.3s ease forwards;
}
The forwards value ensures that when the hover animation completes, the button maintains its new position and color rather than snapping back.
Example 2: Modal Entrance Animation with Both
This example creates a modal that fades and scales in smoothly, using both fill mode for complete control:
@keyframes modalEnter {
from {
opacity: 0;
transform: scale(0.9) translateY(-20px);
}
to {
opacity: 1;
transform: scale(1) translateY(0);
}
}
.modal-overlay {
animation: modalEnter 0.3s ease-out both;
}
.modal-content {
animation: modalEnter 0.3s ease-out both 0.1s;
}
The overlay appears first, followed by the content with a slight delay. Both elements maintain their final states after the animation completes.
Example 3: Card Staggered Entrance with Backwards
This example creates a staggered entrance animation for a grid of cards, using backwards to prevent flash of unstyled content:
@keyframes fadeUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.card-grid .card:nth-child(1) {
animation: fadeUp 0.5s ease backwards;
}
.card-grid .card:nth-child(2) {
animation: fadeUp 0.5s ease 0.1s backwards;
}
.card-grid .card:nth-child(3) {
animation: fadeUp 0.5s ease 0.2s backwards;
}
.card-grid .card:nth-child(4) {
animation: fadeUp 0.5s ease 0.3s backwards;
}
Each card appears with a slight delay, creating a pleasing cascade effect. The backwards value ensures each card appears in its faded state immediately, even before its animation begins.
Example 4: Loading Spinner (Infinite Animation)
Infinite animations don't need fill mode since they never complete:
@keyframes spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
.loading-spinner {
animation: spin 1s linear infinite;
}
For more on CSS techniques, see our guide on how to override Bootstrap CSS for custom styling approaches.
Performance Considerations for CSS Animations
Creating beautiful animations is only half the battle--they must also perform well to provide a smooth user experience. Understanding performance implications helps you create animations that delight rather than frustrate users.
GPU Acceleration
CSS animations that animate only transform and opacity properties are GPU-accelerated and won't trigger expensive layout or paint operations. This is because these properties can be composited on the GPU without affecting the document flow.
GPU-accelerated properties: transform, opacity, filter, will-change CPU-bound properties: width, height, margin, padding, top, left, font-size
When using animation-fill-mode, the final state is maintained by the browser's compositor, making forwards efficient for transform and opacity animations.
Best Practices for Performance
- Animate only transform and opacity when possible--these are the only properties that can be truly performant for animations
- Use the will-change property sparingly to hint at upcoming animations, but only add it when you're certain an animation will occur
- Keep animations short--under 500ms for most interactions feels responsive without being distracting
- Use appropriate easing functions (ease-out, cubic-bezier) for natural motion that feels intuitive
- Test animations using browser dev tools performance tab to identify bottlenecks before deployment
Accessibility Considerations
Always respect users who prefer reduced motion. Many users experience discomfort or nausea from animations, and operating systems provide a way to express this preference:
@media (prefers-reduced-motion: reduce) {
.animated-element {
animation: none !important;
transition: none !important;
}
}
This media query respects user preferences while maintaining functionality--the content still appears, just without the animation effect. For more on event handling and animation patterns, see our guide on event bubbling and capturing in React.
Common Mistakes and How to Avoid Them
Even experienced developers encounter pitfalls when working with animation-fill-mode. Understanding these common mistakes helps you write better animation code from the start.
Mistake 1: Forgetting to Use Fill Mode
Problem: Elements snap back to original state after animation completes, creating jarring transitions.
Solution: Use forwards when you want elements to maintain their final animated state. For entrance animations, use both for complete control.
Debugging tip: Open browser dev tools and inspect the element after the animation completes. If styles are reverting, you likely need forwards or both.
Mistake 2: Confusing Backwards Behavior
Problem: Developers expect backwards to affect the end state, but it only affects the delay period.
Solution: Remember that backwards controls styles before the animation starts (during delay), while forwards controls styles after the animation ends. If you want both behaviors, use both.
Debugging tip: If you have no animation-delay, backwards will have no visible effect. Add a delay to see backwards behavior in action.
Mistake 3: Using Forwards with Infinite Animations
Problem: Infinite animations never end, so forwards has no visible effect.
Solution: Forwards is only useful for finite animations. For infinite animations like loading spinners, fill mode is unnecessary since the animation loops continuously.
Debugging tip: Set animation-iteration-count to 1 temporarily to test forwards behavior, then switch back to infinite when satisfied.
Mistake 4: Not Testing All Direction Modes
Problem: Animations behave differently with reverse, alternate, and alternate-reverse directions, leading to unexpected end states.
Solution: Test animations with each animation-direction value to ensure the final state is what you expect. The last keyframe for forwards varies significantly based on direction.
Additional Debugging Tips
- Use animation-play-state: paused to pause animations mid-flight and inspect current styles
- Check for conflicting CSS rules that might override animation styles
- Verify keyframe definitions include all necessary properties for the desired end state
- Use browser dev tools to step through animations and see each keyframe's effect
Browser Support and Compatibility
The animation-fill-mode property has excellent browser support across all modern browsers, making it safe to use without vendor prefixes in current projects.
Current Support
According to MDN Web Docs and W3Schools CSS animation-fill-mode reference:
- Chrome: Full support (version 43+)
- Firefox: Full support (version 49+)
- Safari: Full support (version 9+)
- Edge: Full support (version 79+)
- No vendor prefixes required in current versions of any major browser
This means you can confidently use animation-fill-mode in production without worrying about browser compatibility for the vast majority of your users.
Fallback Strategies
For older browsers or as a best practice, use feature detection to provide graceful degradation:
@supports (animation-fill-mode: forwards) {
.element {
animation: slideIn 0.5s ease forwards;
}
}
@supports not (animation-fill-mode: forwards) {
.element {
/* Fallback for older browsers */
animation: slideIn 0.5s ease;
}
}
The @supports rule allows you to provide alternative styles for browsers that don't fully support animation-fill-mode, ensuring a reasonable experience for all users.
Testing Recommendations
- Test animations in Chrome, Firefox, Safari, and Edge before deployment
- Use browser dev tools to verify animation behavior
- Test on actual mobile devices, not just emulators
- Consider using a service like BrowserStack for comprehensive cross-browser testing
Conclusion
The animation-fill-mode property is essential for creating polished, professional CSS animations that enhance rather than disrupt user experience. By controlling what styles apply before and after animation execution, you can create smooth transitions that feel natural and intentional.
Key Takeaways
- none provides the default behavior with no style persistence--use for temporary visual effects
- forwards maintains the last keyframe styles after animation completion--ideal for permanent state changes
- backwards shows first keyframe styles during the delay period--prevents flash of unstyled content
- both gives complete control over both animation boundaries--often the best choice for entrance animations
Best Practices Summary
- Use forwards when elements should maintain their final animated position
- Use both for entrance animations where you want control at both start and end
- Test with different animation-direction values to ensure predictable end states
- Combine with animation-delay and use backwards for smooth delayed animations
- Always respect prefers-reduced-motion for accessibility
- Animate only transform and opacity for optimal performance
Next Steps
- Practice with the examples provided in this guide--modify them for your own projects
- Experiment with different combinations of animation properties to understand interactions
- Test animations across multiple browsers and devices before deployment
- Consider performance and accessibility in all animation implementations
- Explore more advanced techniques like scroll-driven animations and JavaScript-triggered animations
Mastering animation-fill-mode is a small investment that pays significant dividends in the quality and professionalism of your web animations. Start applying these techniques in your projects today to create smoother, more polished user experiences.
Frequently Asked Questions
What is the difference between forwards and both in animation-fill-mode?
forwards only maintains the last keyframe styles after animation completion, while both maintains first keyframe styles during the delay period AND last keyframe styles after completion. Use forwards when you only need to control the end state, and both when you need control at both animation boundaries.
Does animation-fill-mode work with infinite animations?
Forwards has no visible effect on infinite animations since they never end. However, backwards can still affect the initial delay period. For infinite animations like loading spinners, fill mode is generally unnecessary.
Why is my animation snapping back after it completes?
This is the default behavior when animation-fill-mode is not specified. Use animation-fill-mode: forwards to maintain the final animated state, or both if you also want to control the appearance during any delay period.
What is the best value to use for entrance animations?
Both is often the best choice for entrance animations as it ensures the element appears in its starting state immediately (preventing flash of unstyled content) and maintains its final position after the animation completes.
How does animation-direction affect forwards behavior?
Animation-direction determines which keyframe is considered the "last" keyframe. With normal direction, the 100% or 'to' keyframe is used. With reverse direction, the 0% or 'from' keyframe is used. With alternate directions, it depends on whether the final iteration is even or odd.
Should I use animation-fill-mode with scroll-triggered animations?
Yes, animation-fill-mode works well with scroll-triggered animations. Use forwards to maintain the animated state after elements come into view, ensuring users see the final result rather than a snap-back to the original state.