// blog/developer/
Back to Blog
Developer · June 13, 2026 · 8 min read · Updated May 22, 2026

CSS Animation Timing Functions: A Practical Guide

CSS Animation Timing Functions: A Practical Guide

The difference between a good animation and a great one is timing. Two animations can move the same element the same distance in the same duration, and one feels smooth and natural while the other feels robotic and jarring. The difference is the timing function, the curve that defines how speed changes over the duration of the animation.

In the physical world, nothing moves at a constant speed. Objects accelerate when they start moving and decelerate before they stop. A ball thrown in the air slows down as it rises and speeds up as it falls. Doors swing open quickly at first, then slow down as the spring resistance increases. Our brains are wired to expect these speed variations, and when a digital animation moves at constant speed, something feels wrong even if you cannot articulate why.

CSS provides timing functions that replicate these natural motion patterns. Understanding them is the fastest way to upgrade your animations from functional to polished.

* * *

The Built-in Timing Functions

CSS offers five keyword timing functions that cover the most common use cases:

linear: Constant speed from start to finish. The element moves the same distance in each time interval. This feels mechanical and is rarely the right choice for UI animations. It works for progress bars, spinning loaders, and anything that should feel machine-like.

ease: The default timing function. It starts slow, speeds up through the middle, and slows down at the end. This is a good general-purpose choice that feels natural for most transitions. If you do not specify a timing function, this is what you get.

ease-in: Starts slow and accelerates toward the end. The element appears to build momentum. Use this for elements leaving the screen, because the speed at the end of the animation suggests continued motion beyond the viewport.

ease-out: Starts fast and decelerates toward the end. The element appears to lose momentum. Use this for elements entering the screen, because the deceleration at the end suggests the element arriving at its destination.

ease-in-out: Slow start, fast middle, slow end. Symmetric easing that works well for elements that stay on screen and move from one position to another.

The Animation Delay Calculator helps you compute staggered delays for multiple animated elements, creating orchestrated sequences where items animate in succession rather than all at once.

Smooth motion blur abstract representing animation curves
Smooth motion blur abstract representing animation curves
* * *

Custom Cubic Bezier Curves

The keyword functions are convenient but limited. For precise control, CSS supports custom timing functions through cubic-bezier curves:

`css transition-timing-function: cubic-bezier(0.25, 0.1, 0.25, 1.0); `

A cubic-bezier curve is defined by four numbers that represent two control points on a graph. The x-axis is time (0 = start, 1 = end) and the y-axis is progress (0 = starting position, 1 = ending position).

The four values are: cubic-bezier(x1, y1, x2, y2) where (x1, y1) is the first control point and (x2, y2) is the second.

Some useful custom curves:

Snappy ease-out (elements arriving with confidence): `css cubic-bezier(0.0, 0.0, 0.2, 1.0) `

Soft bounce (y values exceeding 1.0 create overshoot): `css cubic-bezier(0.34, 1.56, 0.64, 1.0) `

Material Design standard curve: `css cubic-bezier(0.4, 0.0, 0.2, 1.0) `

Apple-style deceleration: `css cubic-bezier(0.22, 0.61, 0.36, 1.0) `

The y values can exceed the 0-1 range, which creates overshoot effects where the element temporarily moves past its destination and then settles back. This mimics the behavior of a spring and adds a sense of physicality to the animation.

Key takeaway

The keyword functions are convenient but limited.

* * *

Animation Duration: How Long Should Animations Take?

Duration is just as important as the curve shape. Too fast and the user misses the animation entirely. Too slow and the interface feels sluggish.

Research and design system guidelines converge on these ranges:

50-100ms: Micro-interactions (button state changes, hover effects, toggle switches). These should feel instantaneous while still being perceptible. Below 50ms, the human eye cannot register the change. Above 100ms, buttons feel laggy.

150-300ms: Standard UI transitions (dropdown menus opening, modals appearing, sidebar expanding). This range gives users enough time to understand what is happening without making them wait.

300-500ms: Complex transitions (page transitions, large element repositioning, multi-step animations). These need more time because the eye has to track larger movements.

500ms+: Decorative or attention-drawing animations only. UI elements should never take this long to respond to user input. Use longer durations for background animations, loading sequences, or initial page load effects.

A useful rule: animation duration should scale with the distance the element moves. A tooltip sliding 10 pixels should take 100ms. A sidebar sliding 300 pixels should take 250-300ms. A page sliding across the full viewport should take 400-500ms.

For loading states, the Loading Spinner Generator creates CSS-only spinners with customizable timing. Spinners typically use linear timing with infinite iteration, because their purpose is to indicate ongoing activity rather than progress toward a destination.

* * *

Staggered Animations: Creating Orchestrated Motion

When multiple elements animate, staggering their start times creates a sense of choreography that makes the interface feel intentional and polished.

The most common pattern is cascading entrance animations, where items in a list animate in sequence with a slight delay between each:

`css .item { opacity: 0; transform: translateY(20px); animation: fadeUp 300ms ease-out forwards; } .item:nth-child(1) { animation-delay: 0ms; } .item:nth-child(2) { animation-delay: 50ms; } .item:nth-child(3) { animation-delay: 100ms; } .item:nth-child(4) { animation-delay: 150ms; }

@keyframes fadeUp { to { opacity: 1; transform: translateY(0); } } `

The delay between items should be short enough that the sequence feels connected (30-80ms per item) but long enough that each item's entrance is individually perceivable.

For larger lists, limit the stagger to the first 5-8 items. Beyond that, the total animation time becomes too long, and later items sit in their pre-animation state for an awkwardly long time. Items beyond the stagger limit should animate immediately or with a fixed maximum delay.

The Animation Delay Calculator computes delay values for any number of elements with customizable base delay and increment. This is especially useful when the stagger timing needs to match specific design requirements or when you are coordinating multiple animation groups.

Developer working on web animations on a monitor
Developer working on web animations on a monitor
* * *

Performance: Animations That Do Not Jank

A smooth animation runs at 60 frames per second, which means each frame has about 16.67 milliseconds to complete. If your animation triggers layout recalculations or paint operations that take longer than that, frames get dropped and the animation stutters (janks).

Properties safe to animate (GPU-accelerated, no layout or paint): - transform (translate, rotate, scale, skew) - opacity - filter

Properties that cause layout (expensive, avoid animating): - width, height - padding, margin - top, right, bottom, left - font-size - border-width

Properties that cause paint (moderately expensive): - background-color - color - box-shadow - border-color

The practical rule: always animate transform and opacity instead of position and size properties. Instead of animating left: 0 to left: 100px, use transform: translateX(100px). Instead of animating width: 0 to width: 200px, use transform: scaleX(1) with transform-origin set appropriately.

For complex animation CSS, run the final stylesheet through a CSS Minifier to reduce the file size. Keyframe definitions with many steps can become verbose, and minification removes the whitespace without affecting behavior.

The will-change property hints to the browser that an element is about to be animated, letting it prepare in advance:

`css .about-to-animate { will-change: transform, opacity; } `

Use will-change sparingly. Applying it to too many elements wastes GPU memory and can actually hurt performance. Add it just before the animation starts and remove it after the animation ends.

* * *

Modern Alternatives: Spring Physics and the Linear() Function

CSS animation is evolving beyond cubic-bezier curves:

The linear() function (widely supported in 2026) lets you define timing curves with any number of points rather than being limited to two control points. This enables complex easing patterns that cubic-bezier cannot express:

`css transition-timing-function: linear( 0, 0.006, 0.025, 0.056, 0.1, 0.157, 0.225, 0.306, 0.4, 0.506, 0.625, 0.756, 0.9, 1.056, 1.125, 1.106, 1.05, 1.006, 0.981, 0.975, 0.981, 1 ); `

This particular curve creates a spring bounce effect that was previously only possible with JavaScript animation libraries.

Spring-based animations model motion as a mass on a spring, defined by stiffness, damping, and mass parameters. They produce natural-feeling motion because they are based on physical simulation rather than mathematical curves. Libraries like Framer Motion and React Spring provide spring animations in JavaScript, and the CSS spring() function is progressing through the specification process.

The key advantage of spring animations is that they handle interruption gracefully. If a user triggers a new animation while the previous one is still running, spring physics naturally blend the current velocity into the new motion. Bezier curves have no concept of current velocity and simply restart, causing visual jumps.

For most production CSS, the built-in keywords and custom cubic-bezier curves cover 95% of use cases. Spring physics and the linear() function are worth learning for interface animations that need to feel particularly physical or responsive.

Key takeaway

CSS animation is evolving beyond cubic-bezier curves: **The `linear()` function** (widely supported in 2026) lets you define timing curves with any number of points rather than being limited to two control points.

* * *

FAQ

Which timing function should I use as a default?

Use ease-out for most UI transitions. It starts fast (giving immediate feedback to user input) and decelerates at the end (feeling settled and intentional). The built-in ease keyword is also good but has a slightly slower start that can feel less responsive for interactive elements.

How do I avoid animations feeling slow on mobile devices?

Reduce durations by 20-30% on mobile. What feels snappy at 300ms on a desktop can feel sluggish on a phone where users expect faster responses. Also, always use GPU-accelerated properties (transform, opacity) because mobile GPUs are less powerful than desktop GPUs and are more sensitive to expensive paint operations.

Should I disable animations for users who prefer reduced motion?

Yes. The prefers-reduced-motion media query lets you detect this preference and provide alternatives. At minimum, remove or simplify decorative animations. For functional animations (a sidebar opening, a modal appearing), replace motion with instant state changes or simple opacity fades.

`css @media (prefers-reduced-motion: reduce) { * { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; } } `

Can CSS animations achieve 60fps consistently?

Yes, if you follow the rules: animate only transform and opacity, avoid animating layout properties, use will-change judiciously, and test on lower-end devices. The browser's compositor thread handles transform and opacity animations separately from the main thread, so even heavy JavaScript execution will not cause these animations to jank.