Easing functions are the secret ingredient that transforms mechanical, robotic motion into smooth, natural transitions that feel intuitive to users. When you hover over a button that gently accelerates into view or a modal that decelerates to a soft landing, you're experiencing the power of easing at work.
This guide covers everything you need to master easing functions in CSS, from basic built-in keywords to advanced custom curves that create spring-like physics without JavaScript. Master these techniques as part of a comprehensive web development strategy to create interfaces that feel polished and professional.
What Are Easing Functions?
Easing functions define the rate of change of a value over time during an animation or transition. They control how an element moves between states, creating natural motion instead of linear (robotic) movement.
The Physics Behind Easing
Objects in the real world don't move at constant speeds--they accelerate, decelerate, and respond to forces like gravity and friction. A ball dropped from a height starts slow, accelerates due to gravity, then bounces and decelerates. Easing functions replicate these natural motion patterns, making digital interfaces feel more intuitive.
1/* Linear: Constant speed - feels robotic */2.element-linear {3 transition: transform 0.3s linear;4}5 6/* Ease: Default - slightly better but still mechanical */7.element-ease {8 transition: transform 0.3s ease;9}10 11/* Custom cubic-bezier: Natural, polished motion */12.element-smooth {13 transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);14}Built-in Easing Keywords
CSS provides five built-in easing keywords that cover most common use cases. Understanding when to use each is essential for creating polished interfaces.
| Keyword | Behavior | Best Use Cases |
|---|---|---|
linear | Constant speed | Loaders, continuous motion |
ease | Slight start/end, fast middle | Default transitions |
ease-in | Slow start, fast end | Elements entering view |
ease-out | Fast start, slow end | Elements exiting view |
ease-in-out | Slow start/end, fast middle | General smooth transitions |
1/* Continuous mechanical motion */2.loader {3 animation: spin 1s linear infinite;4}5 6/* Default transition - general purpose */7.card {8 transition: transform 0.3s ease;9}10 11/* Slow acceleration - elements entering */12.modal-enter {13 animation: fadeIn 0.3s ease-in;14}15 16/* Deceleration - elements exiting */17.modal-exit {18 animation: fadeOut 0.3s ease-out;19}20 21/* Smooth throughout - balanced motion */22.smooth-transition {23 transition: all 0.4s ease-in-out;24}Custom Easing with cubic-bezier()
The cubic-bezier() function is your primary tool for creating custom easing curves. Unlike keywords, it gives you complete control over the acceleration curve.
Understanding the Syntax
cubic-bezier(x1, y1, x2, y2) defines a custom curve using two control points:
- P1 (x1, y1): First control point
- P2 (x2, y2): Second control point
- X values must be 0-1 (time cannot go backward)
- Y values can exceed 0-1 for bounce effects
Recommended Presets
| Preset | cubic-bezier Value | Feel |
|---|---|---|
| Material Design | 0.4, 0, 0.2, 1 | Smooth, natural |
| iOS Swift Out | 0.55, 0, 0.4, 1 | iOS-style deceleration |
| Apple Standard | 0.5, 0, 0.25, 1 | Refined, subtle |
| Elastic | 0.68, -0.55, 0.265, 1.55 | Bouncy, playful |
1/* Material Design standard easing */2.material {3 transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);4}5 6/* Subtle bounce effect */7.bouncy {8 transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);9}10 11/* Elastic button press */12.button:active {13 transform: scale(0.95);14 transition: transform 0.1s cubic-bezier(0.4, 0, 0.2, 1);15}16 17/* Spring-like card hover */18.card:hover {19 transform: translateY(-4px);20 transition: transform 0.3s cubic-bezier(0.3, 0, 0, 1.5);21}Step Functions for Discrete Animations
The steps() function creates frame-by-frame or sprite-sheet animations by dividing the transition into discrete jumps instead of smooth motion.
Step Direction Modifiers
| Value | Behavior |
|---|---|
steps(4, jump-start) | Jump at 0%, 25%, 50%, 75% |
steps(4, jump-end) | Jump at 25%, 50%, 75%, 100% |
steps(4, jump-both) | Jump at 0%, 33%, 66%, 100% |
steps(4, jump-none) | Smooth between intervals |
Use cases: retro animations, loading indicators, progress meters, countdown timers.
1/* Sprite sheet animation - 8 frames */2.sprite {3 animation: walk 0.8s steps(8) infinite;4}5 6/* Loading indicator with 4 steps */7.loading-dots span {8 animation: dots 1s steps(4) infinite;9}10 11/* Jump-start: appear immediately */12.character {13 animation: walk 0.5s steps(6, jump-start) infinite;14}15 16/* Jump-end: appear after first frame (default) */17.character {18 animation: walk 0.5s steps(6, jump-end) infinite;19}The Modern linear() Function
The linear() function creates complex easing curves using linear segments. Unlike the linear keyword (constant speed), it can create variable speed motion with precise control.
When to Use linear() vs cubic-bezier()
- Use cubic-bezier() for most custom easing--intuitive and well-supported
- Use linear() for complex multi-segment curves that cubic-bezier cannot achieve
- linear() is ideal for replicating spring physics and bounce effects with exact control
- Consider browser support--linear() is newer and may need fallbacks
For most web development projects, cubic-bezier() provides the best balance of flexibility and browser compatibility.
1/* Creates ease-in-out-like behavior with segments */2.custom-ease {3 transition-timing-function: linear(4 0, 0.05, 0.2, 0.45, 0.7, 0.9, 15 );6}7 8/* Simulates spring physics */9.spring {10 animation: springIn 0.6s linear(11 0, 0.02, 0.05, 0.1, 0.15, 0.25,12 0.35, 0.45, 0.55, 0.65, 0.75,13 0.85, 0.95, 114 );15}Applying Easing to CSS Properties
Easing functions work with both transitions and animations through specific properties.
transition-timing-function
Controls easing for CSS transitions when properties change state.
animation-timing-function
Controls easing for keyframe animations, either globally or per-keyframe.
1/* Smooth hover with custom easing */2.button {3 transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1),4 opacity 0.2s ease-out;5}6 7/* Keyframe animation with easing */8@keyframes slideIn {9 0% {10 transform: translateX(-100%);11 animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1);12 }13 60% {14 transform: translateX(10%);15 animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1);16 }17 100% {18 transform: translateX(0);19 }20}21 22/* Per-property timing functions */23.card {24 transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1),25 opacity 0.2s ease-out,26 background-color 0.3s ease;27}Performance Best Practices
Optimize your animations for smooth 60fps performance with these techniques.
GPU Acceleration
Animate only transform and opacity properties for best performance (GPU-accelerated). These properties don't trigger layout recalculations.
Best Practices
- Animate only
transformandopacityfor 60fps - Avoid animating
width,height,margin,padding - Use
will-changesparingly to hint browser optimization - Test on actual devices, not just development tools
Proper animation performance is a key aspect of professional web development that ensures smooth user experiences across all devices.
1/* Good: GPU-accelerated properties */2.animate-good {3 transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1),4 opacity 0.3s ease;5}6 7/* Bad: Triggers layout recalculation */8.animate-bad {9 transition: width 0.3s ease,10 height 0.3s ease;11}12 13/* Create compositing layer */14.gpu-layer {15 transform: translateZ(0);16 will-change: transform;17}18 19/* Use translate instead of top/left */20.move-element {21 position: relative;22 transform: translateX(0);23 transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);24}Accessibility: Respecting Reduced Motion
Many users experience motion sensitivity (vestibular disorders). Respect their preferences by providing alternative animations.
Using prefers-reduced-motion
@media (prefers-reduced-motion: reduce) {
.animated-element {
transition: transform 0.1s ease;
}
}
Key Considerations
- Never disable all motion without user consent
- Provide subtle or minimal animation for reduced motion preferences
- Ensure information is not conveyed through motion alone
- Test with motion disabled to verify functionality
Practical Code Examples
Real-world patterns you can use in your projects.
Button Hover Effects
.btn-primary {
position: relative;
transform: translateY(0);
transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1),
box-shadow 0.2s cubic-bezier(0.4, 0, 0.2, 1);
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
}
.btn-primary:active {
transform: translateY(0);
transition: transform 0.1s ease;
}
Modal Animations
.modal-content {
opacity: 0;
transform: scale(0.95) translateY(10px);
transition: opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1),
transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.modal-content.active {
opacity: 1;
transform: scale(1) translateY(0);
}
Staggered List Animations
.list-item {
opacity: 0;
transform: translateY(20px);
transition: opacity 0.4s ease-out,
transform 0.4s cubic-bezier(0.4, 0, 0.2, 1);
}
.list-item.visible {
opacity: 1;
transform: translateY(0);
}
/* Stagger delays */
.list-item:nth-child(1) { transition-delay: 0ms; }
.list-item:nth-child(2) { transition-delay: 50ms; }
.list-item:nth-child(3) { transition-delay: 100ms; }
Quick Reference: Choosing the Right Easing
| Animation Type | Recommended Easing | Example |
|---|---|---|
| Modal dialogs | cubic-bezier(0.4, 0, 0.2, 1) | Smooth, natural appearance |
| Button hovers | cubic-bezier(0.4, 0, 0.2, 1) | Subtle lift effect |
| Page transitions | ease-in-out | Gradual start and end |
| Loading spinners | linear | Consistent rotation |
| Notification toasts | cubic-bezier(0.4, 0, 0.2, 1) | Gentle appearance |
| Card selections | cubic-bezier(0.34, 1.56, 0.64, 1) | Energetic feedback |
| Menu items | ease-out | Quick open, slow close |
| Form inputs | ease | Standard focus effect |