CSS Timing Functions
What You Learn From This Page
- How a timing function maps elapsed time to transition or animation progress.
- How
linear,ease,ease-in,ease-out, andease-in-outdiffer. - How cubic Bezier control points shape acceleration, deceleration, and overshoot.
- How
steps(),step-start, andstep-endproduce discrete changes. - How the modern
linear()function describes multi-stop easing. - How to select different curves for entrances, exits, direct feedback, and continuous motion.
- How timing functions interact with duration, distance, multiple properties, and keyframes.
- How to create reusable motion tokens, respect reduced-motion preferences, and debug poor motion.
Introduction
A CSS timing function controls how progress is distributed across the duration of a transition or animation. It answers a question that duration alone cannot answer: after 30 percent of the available time has elapsed, how far should the visual change have progressed? A linear function reports 30 percent progress. An easing curve may report more or less, creating acceleration or deceleration without changing the declared start value, end value, or total time.
This distinction explains why two movements covering the same distance in the same 300 milliseconds can feel completely different. One may begin immediately and settle gently, while another may appear hesitant and then rush toward its destination. The timing function changes perceived responsiveness, visual weight, and continuity. It does not decide which property changes, how far it changes, when the state is triggered, or how long the full operation lasts.
Timing functions are shared by CSS transitions and keyframe animations. The same keywords and functions can be supplied through transition-timing-function, the transition shorthand, animation-timing-function, or the animation shorthand. Choosing well requires more than memorizing keyword definitions. The curve must fit the interaction, duration, travel distance, product tone, and accessibility needs of the interface.
Basic Syntax
In a transition shorthand, the easing value normally follows the duration. The following button changes its transform over 180 milliseconds using an ease-out curve.
.button {
transition: transform 180ms ease-out;
}
.button:hover {
transform: translateY(-2px);
}
The equivalent longhand declaration isolates the curve so it can be changed independently. Both forms compute to the same behavior.
.button {
transition-property: transform;
transition-duration: 180ms;
transition-timing-function: ease-out;
transition-delay: 0ms;
}
An animation uses the same easing vocabulary. Here, constant-rate rotation is appropriate because a loading indicator should not visibly accelerate and brake on every revolution.
.spinner {
animation: spin 900ms linear infinite;
}
@keyframes spin {
to { transform: rotate(1turn); }
}
What a Timing Function Controls
A timing function maps an input progress value to an output progress value. Input progress is the fraction of duration that has elapsed, from zero at the beginning to one at the end. Output progress is the fraction of interpolation that should have occurred. If a box moves from zero to 300 pixels, an output of 0.5 means the interpolated position is 150 pixels, regardless of whether that output occurs halfway through the clock time.
The browser evaluates the easing repeatedly while rendering frames. At each frame it determines elapsed time, applies the timing function, and interpolates the participating property using the resulting progress. A steep portion of the curve advances a large visual distance in a short time and therefore appears fast. A shallow portion advances little and appears slow. Smooth curves create gradual velocity changes; step functions deliberately replace interpolation with jumps.
.box {
transform: translateX(0);
transition: transform 1s ease-out;
}
.box.is-moved {
transform: translateX(300px);
}
This box always finishes after one second and always travels 300 pixels. Only the distribution of that travel changes. Easing is not a shorter duration, a longer distance, or a performance switch.
The linear Keyword
The linear keyword maps elapsed time directly to progress. Twenty-five percent of the duration produces twenty-five percent progress, halfway through time produces halfway through the change, and so on. Because velocity remains constant when the property itself changes uniformly, linear motion can look mechanical. That is appropriate when the interface is representing a clock, a continuously rotating mechanism, or progress that genuinely advances at a constant rate.
Loading spinners, indeterminate tracks, repeating marquees, and frame rotations are common candidates. Ordinary buttons and panels usually benefit from acceleration or deceleration because physical objects rarely begin or stop at full speed. Linear easing is not inherently bad; it is simply a specific visual statement and should match the behavior being represented.
.progress__fill {
transform-origin: left;
transition: transform 1s linear;
}
If this bar represents measured progress, update it from real data rather than using easing to imply work that has not happened. Visual smoothness must not misrepresent completion.
The ease Keyword
ease is the initial value for transition and animation timing functions. It begins with relatively quick progress, changes speed through the middle, and slows toward the end. Its exact curve is cubic-bezier(0.25, 0.1, 0.25, 1). Because it offers a soft general-purpose rhythm, it works well for color changes, shadows, and routine interface feedback where a more specialized entrance or exit curve is unnecessary.
.card {
transition:
background-color 200ms ease,
box-shadow 200ms ease;
}
Omitting an easing from a transition does not produce linear movement. The browser uses ease, so transition: color 200ms and transition: color 200ms ease have equivalent timing. Writing the value explicitly can still be useful when a codebase wants motion decisions to be visible during review.
ease-in
ease-in starts slowly and accelerates toward the end. Its predefined curve is cubic-bezier(0.42, 0, 1, 1). Because little visible progress happens at first, it often feels less responsive when attached to a hover, press, or entrance. The user has acted, but the interface appears reluctant to respond.
The curve can be effective for selected exits. An object that begins gently and accelerates away may feel as though it is leaving the scene rather than arriving. Even then, the duration should remain short enough that dismissal does not obstruct the next task. Ease-in is a useful option, not a universal rule for every exit.
.toast {
opacity: 1;
transform: translateY(0);
transition:
opacity 140ms ease-in,
transform 180ms ease-in;
}
.toast.is-leaving {
opacity: 0;
transform: translateY(-8px);
}
ease-out
ease-out starts quickly and decelerates as it approaches the final value. It corresponds to cubic-bezier(0, 0, 0.58, 1). The early visible movement gives direct interactions an immediate response, while the slower ending avoids an abrupt stop. This makes ease-out a strong starting point for buttons, cards, dropdowns, tooltips, drawers, and modal entrances.
.menu {
opacity: 0;
transform: translateY(-0.5rem);
transition:
opacity 140ms ease-out,
transform 180ms ease-out;
}
.menu.is-open {
opacity: 1;
transform: translateY(0);
}
The phrase "starts quickly" does not mean the transition completes before its duration. A 200-millisecond ease-out and a 200-millisecond linear transition both end at 200 milliseconds. The ease-out version covers more of the visual distance earlier, which is why it can feel faster.
ease-in-out
ease-in-out starts slowly, accelerates through the middle, and slows before finishing. Its curve is cubic-bezier(0.42, 0, 0.58, 1). The symmetry suits reversible movement in which the same object remains visible from start to finish, such as an expanding panel, carousel track, switch thumb, or layout region moving between two established positions.
.accordion__content {
display: grid;
grid-template-rows: 0fr;
transition: grid-template-rows 280ms ease-in-out;
}
.accordion.is-open .accordion__content {
grid-template-rows: 1fr;
}
A slow start can still be undesirable for a tiny direct-control response. Curve selection should be evaluated in the actual component instead of assigned solely from a category name. A 120-millisecond toggle and a 500-millisecond carousel may need different pacing even though both reverse.
Keyword Comparison
| Function | Progress Pattern | Useful Starting Point |
|---|---|---|
linear | Constant rate | Spinners and time-linked progress |
ease | Quick movement with a soft finish | Colors, shadows, and general feedback |
ease-in | Slow start, fast finish | Selected exit motion |
ease-out | Fast start, slow finish | Entrances and direct interactions |
ease-in-out | Slow start and finish | Reversible or larger movement |
Understanding cubic-bezier()
The cubic-bezier() function provides precise control over an easing curve. Its four arguments define two movable control points: cubic-bezier(x1, y1, x2, y2). The start point is fixed at (0, 0) and the end point at (1, 1). The horizontal axis represents elapsed time, and the vertical axis represents output progress.
.drawer {
transition: transform 300ms cubic-bezier(0.2, 0, 0, 1);
}
The two x coordinates must remain in the range from zero through one so the curve is a valid function of time. The y coordinates may fall below zero or exceed one. Values outside the normal progress range can create anticipation or overshoot, although the observable result also depends on how the animated property's value type handles interpolation.
Control points do not mark positions that the element literally visits. They pull the mathematical curve and determine its slope. Moving a control point can change how quickly the curve leaves the start, how aggressively it travels through the middle, and how gently it approaches the end. Browser developer tools often provide a curve editor that makes this relationship easier to understand than guessing numbers.
Built-In Keywords as Bezier Curves
| Keyword | Equivalent Curve |
|---|---|
linear | cubic-bezier(0, 0, 1, 1) |
ease | cubic-bezier(0.25, 0.1, 0.25, 1) |
ease-in | cubic-bezier(0.42, 0, 1, 1) |
ease-out | cubic-bezier(0, 0, 0.58, 1) |
ease-in-out | cubic-bezier(0.42, 0, 0.58, 1) |
These exact equivalents are useful when comparing a custom curve with a familiar baseline. They also show that keyword easing is not vague browser behavior; it represents standardized coordinates. Prefer the readable keyword when it expresses the intended curve exactly, and reserve explicit coordinates for a meaningful custom motion pattern.
Custom Curves and Overshoot
A product may define a faster entrance curve that moves most of the distance early and then spends the remaining time settling. Reusing that curve across menus, popovers, and drawers creates a recognizable motion vocabulary.
:root {
--ease-enter: cubic-bezier(0.2, 0.8, 0.2, 1);
--ease-emphasized: cubic-bezier(0.2, 0, 0, 1);
}
.popover {
transition: transform 220ms var(--ease-enter);
}
A curve such as cubic-bezier(0.34, 1.56, 0.64, 1) allows output progress to exceed one before returning to the endpoint. This can suggest spring-like overshoot, but it is not a physical spring simulation. It has a fixed duration and a predetermined curve. Excessive overshoot can make ordinary controls feel unstable, distract from work, or conflict with a restrained product tone.
Do not create arbitrary curves merely because the syntax allows it. Start with a built-in function, identify what feels wrong in context, and adjust with a specific goal. A small, documented set of curves is easier to test and maintain than unrelated Bezier numbers scattered throughout component files.
Discrete Motion with steps()
steps() divides progress into a specified number of intervals and jumps between values rather than interpolating smoothly. This is appropriate for sprite sheets, typewriter reveals, digital counters, frame-by-frame illustrations, and state indicators that should change at distinct boundaries.
.sprite {
animation: walk 800ms steps(8, end) infinite;
}
@keyframes walk {
to { background-position-x: -1024px; }
}
The first argument must be a positive integer, subject to additional validity rules for some step positions. The optional second argument controls where jumps occur. Common forms are jump-start, jump-end, jump-none, and jump-both; the aliases start and end correspond to start-side and end-side behavior. The default is end.
jump-start includes a jump at the beginning, while jump-end includes one at the end. jump-none removes jumps from both boundaries and holds the first and last states longer. jump-both adds jumps at both boundaries, effectively adding a step. These details matter for sprite alignment because showing one extra frame or holding the wrong frame changes the sequence.
step-start and step-end
step-start is equivalent to steps(1, start). The output jumps to the final state immediately and holds it for the duration. step-end is equivalent to steps(1, end); it holds the initial state and jumps when the interval ends.
.delayed-indicator {
transition: visibility 200ms step-end;
}
.delayed-indicator.is-visible {
visibility: visible;
transition-timing-function: step-start;
}
These functions can coordinate discrete values with opacity or transform, but they need careful state testing. They are not substitutes for natural movement. Applying five steps to a card lift produces visible stutter because the design asks for smooth spatial continuity while the function deliberately removes it.
A Typewriter Example
A typewriter reveal uses discrete character-width increments. The number of steps should correspond to the visible units being revealed, and a monospace font makes the relationship predictable.
.typewriter {
inline-size: 30ch;
overflow: hidden;
white-space: nowrap;
animation: typing 3s steps(30, end) both;
}
@keyframes typing {
from { inline-size: 0; }
to { inline-size: 30ch; }
}
This is a decorative effect, not an ideal way to expose essential information. Screen readers may announce the complete text independently of the visual reveal, and users who reduce motion should receive the final state without waiting through a long sequence.
The Modern linear() Function
The linear() easing function is different from the linear keyword. The keyword describes one constant-rate line. The function defines a piecewise-linear curve through multiple output values and optional progress positions. It can approximate complex easing data with straight segments and is useful when importing carefully designed motion curves.
.element {
animation-timing-function: linear(
0,
0.15 15%,
0.7 55%,
0.92 80%,
1
);
}
Each stop supplies an output progress value, and percentages locate that value on the time axis. Omitted positions are distributed according to the syntax rules. Repeated positions can create sharp changes. Because long lists are difficult to review manually, store sophisticated curves as named tokens, document their source, and provide a reasonable fallback when supporting older browser environments.
.panel {
animation-timing-function: ease-out;
}
@supports (animation-timing-function: linear(0, 1)) {
.panel {
animation-timing-function: var(--ease-spring-sampled);
}
}
Multiple Transition Timing Functions
Comma-separated transitions can assign a different timing function to each property. A modal might fade at a simple pace while its spatial movement settles more gradually.
.modal {
transition:
opacity 160ms linear,
transform 220ms ease-out;
}
When longhand lists have different lengths, CSS repeats or truncates values according to list-matching rules. That behavior is valid but can be hard to read. Complete transition items in the shorthand make ownership explicit and reduce accidental pairings during maintenance. The curve should suit the property: opacity can tolerate simple pacing, while a moving edge makes velocity changes much more noticeable.
Timing Functions in Keyframe Animations
An animation-level timing function controls progress between keyframes, not one undivided curve over the entire animation when intermediate keyframes are present. Each keyframe segment receives the timing behavior. A declaration placed inside a keyframe applies to the interval beginning at that keyframe and ending at the next one.
@keyframes travel {
0% {
transform: translateX(0);
animation-timing-function: ease-in;
}
50% {
transform: translateX(100px);
animation-timing-function: ease-out;
}
100% {
transform: translateX(0);
}
}
Here, ease-in shapes the zero-to-fifty-percent segment, and ease-out shapes the fifty-to-one-hundred-percent segment. A timing declaration on the final keyframe has no following segment in a normal iteration and therefore does not shape a later interval. This directionality is a frequent source of confusion during animation debugging.
Entry and Exit Motion
Entering content should acknowledge the triggering action quickly. An ease-out curve often works because visible progress begins immediately and settles near the final position. Exiting content can use ease-in when acceleration away supports the visual story. The two directions do not have to share one curve.
.panel {
opacity: 0;
transform: translateY(6px);
transition:
opacity 140ms ease-in,
transform 160ms ease-in;
}
.panel.is-open {
opacity: 1;
transform: translateY(0);
transition-timing-function: ease-out;
}
State-specific timing declarations are resolved from the state that becomes active. Test both directions and rapid reversal. If a user reopens the panel while it is leaving, the browser normally transitions from the current interpolated value rather than restarting from the original endpoint. Curves that look attractive in isolation can feel inconsistent when interrupted repeatedly.
Duration, Distance, and Perceived Speed
Easing and duration must be designed together. A curve that feels crisp over 140 milliseconds can feel dramatic over 900 milliseconds because every phase becomes visible. Likewise, moving an object four pixels and moving it across an entire viewport should not automatically use identical durations. Larger travel generally needs enough time for the eye to maintain continuity, but long movement must not block interaction.
Perceived speed depends heavily on early progress. An ease-out entrance may feel faster than a linear entrance of the same duration because the object covers more distance immediately. Conversely, a long ease-in can look frozen at first. When users report that an interface feels slow, inspect both the clock duration and the first portion of the curve.
Do not compensate for an unsuitable curve by making everything extremely short. Motion that finishes too quickly can become a flash and fail to communicate spatial relationships. Evaluate on realistic hardware, at normal viewing distance, and while performing the actual task rather than watching the component repeatedly in a motion showcase.
Buttons, Links, and Cards
Direct manipulation needs immediate feedback. A button press can use a short ease-out transform, while color may use the general ease curve. Keep the distance and scale restrained so the control does not shift away from the pointer or make surrounding content appear unstable.
.button {
transition:
background-color 150ms ease,
transform 100ms ease-out;
}
.button:active {
transform: scale(0.98);
}
A card hover follows the same principle. The transform supplies clear spatial response; the shadow can use a softer curve. Always provide a useful focus state independently of hover and ensure the motion is supplementary rather than the only indication that the element is interactive.
.card {
transition:
transform 180ms ease-out,
box-shadow 200ms ease;
}
.card:hover,
.card:focus-within {
transform: translateY(-3px);
}
Menus, Modals, and Drawers
Overlays often combine opacity with a small transform. The movement should clarify origin and layering without sending the surface across an unnecessarily large distance. A dropdown may descend a few pixels from its trigger, while a drawer may enter from the edge it occupies.
.dialog {
opacity: 0;
transform: scale(0.97);
transition:
opacity 160ms ease,
transform 220ms cubic-bezier(0.2, 0, 0, 1);
}
.dialog.is-open {
opacity: 1;
transform: scale(1);
}
Timing functions do not manage focus, the accessibility tree, pointer events, or element removal. Opening a modal requires focus placement and background interaction control. Closing requires a lifecycle that allows the visual exit to finish before removal when an exit transition is desired. Listen for transitionend carefully, filter by the intended property, and include a fallback for canceled transitions or reduced motion.
Spinners, Progress, and Repetition
Repeated motion exposes the seam between the last and first iteration. An eased spinner slows near the end, jumps into a new iteration, and accelerates again, producing a pulse that usually looks accidental. Constant linear rotation hides that seam and communicates ongoing activity more honestly.
Progress indicators require a distinction between visual interpolation and real system status. Smoothing updates with a short linear transition can prevent abrupt jumps, but the displayed position must follow measured progress. An ease-out curve can race ahead early and then appear stuck, which may frustrate users even when the total duration is unchanged.
.progress__bar {
transform: scaleX(var(--progress));
transform-origin: left;
transition: transform 200ms linear;
}
Motion Tokens in a Design System
A maintainable interface uses a small vocabulary rather than inventing easing for every component. Name tokens by purpose instead of by coordinates so component code communicates intent. Durations can be tokenized alongside curves, but they remain separate decisions.
:root {
--ease-standard: ease;
--ease-enter: cubic-bezier(0, 0, 0.2, 1);
--ease-exit: cubic-bezier(0.4, 0, 1, 1);
--ease-move: cubic-bezier(0.4, 0, 0.2, 1);
--duration-fast: 120ms;
--duration-standard: 220ms;
}
.tooltip {
transition:
opacity var(--duration-fast) var(--ease-standard),
transform var(--duration-fast) var(--ease-enter);
}
Document which token is intended for entrances, exits, reversible movement, continuous motion, and emphasized branded moments. Review tokens as a system when the product tone changes. Centralization makes reduced-motion adaptations and consistency audits substantially easier.
Accessibility and Reduced Motion
Large movement, zooming, parallax, and repeated animation can cause discomfort for motion-sensitive users. Easing alone does not make such movement accessible. Use the prefers-reduced-motion media feature to reduce or remove nonessential spatial effects, while preserving clear state feedback.
@media (prefers-reduced-motion: reduce) {
.drawer,
.modal,
.card {
transition-duration: 1ms;
transition-delay: 0ms;
}
}
Another strategy replaces a long slide with a brief opacity change. Reduced motion does not always require eliminating every visual transition, but the result should avoid unnecessary displacement and repetition. Do not make meaning depend on watching motion: selected, expanded, loading, successful, and invalid states need persistent text, semantics, shape, or color cues with sufficient contrast.
Also consider cognitive pacing. Long decorative transitions delay access to information, while rapid flashing changes can be difficult to follow. Keyboard and screen-reader users may move focus before an entrance finishes. Interactive content should be operable according to its actual state, not according to where it appears midway through an animation.
Performance Considerations
Replacing ease with linear does not normally solve rendering performance. Cost is driven more by the property being animated, the size of the affected area, the number of elements, and the browser work required for layout, paint, and compositing. Transform and opacity are commonly efficient choices for visual movement, while properties such as width, height, top, and left can trigger layout.
/* Usually preferable for visual movement */
.card {
transition: transform 180ms ease-out;
}
/* May require layout on every frame */
.card {
transition: left 180ms ease-out;
}
This is guidance, not a promise that every transform is free. Large layers, filters, shadows, and many simultaneous animations can still be expensive. Profile the actual interaction using browser performance tools. Use will-change only when measurement shows a benefit and remove it when no longer needed; global promotion wastes memory.
Debugging Motion That Feels Wrong
Begin with the user-visible symptom. If response feels delayed, inspect delay, duration, and the curve's initial slope. If the ending snaps, confirm that the property is interpolable and that no class is removed early. If multiple effects feel disconnected, inspect each comma-separated transition and verify that duration and easing values are paired with the intended property.
Temporarily replace the curve with linear. This does not fix the design, but it helps distinguish an easing issue from a property, lifecycle, or frame-rate issue. Slow the animation in developer tools to inspect its trajectory, then restore realistic timing before making a final judgment. Custom Bezier editors reveal control points, while animation panels can replay motion and show segment timing.
Test entrance, exit, interruption, rapid toggling, pointer hover, keyboard focus, touch activation, page zoom, and reduced motion. Verify behavior after content length changes and at narrow widths. A curve approved on an isolated square may not work when applied to a full-height drawer containing focusable controls.
Common Timing Function Mistakes
| Mistake | Why It Fails | Better Direction |
|---|---|---|
Using linear for every control | Direct interactions feel mechanical | Start with ease-out or ease |
Using long ease-in hover motion | Initial response appears delayed | Use a short curve with quick early progress |
| Easing a repeating spinner | Every loop visibly speeds up and slows down | Use constant-rate linear |
| Random Bezier coordinates | Motion becomes inconsistent and hard to maintain | Define purposeful shared tokens |
| Excessive overshoot | Routine UI feels unstable or distracting | Reserve emphasis for suitable contexts |
Using steps() for natural movement | Spatial motion visibly jumps | Use steps only for discrete sequences |
| Assuming easing changes total duration | The mental model is incorrect | Separate duration from progress distribution |
| One curve for every property | Fade and movement may need different pacing | Assign curves per property when valuable |
| Ignoring reverse and interruption | Components feel inconsistent during real use | Test rapid state changes |
| Using motion as the only state cue | State disappears when motion is reduced | Provide persistent semantic and visual cues |
Practical Timing Function Guide
| Interaction | Useful Starting Point | Reason |
|---|---|---|
| Button or link feedback | ease-out | Immediate response with a soft finish |
| Color or shadow change | ease | General smooth interpolation |
| Modal entrance | ease-out | Quick appearance and gentle settling |
| Modal exit | ease-in | Optional acceleration away |
| Accordion or switch | ease-in-out | Balanced reversible movement |
| Drawer entrance | ease-out or a system curve | Responsive large-surface movement |
| Continuous spinner | linear | No visible iteration seam |
| Sprite sheet | steps() | Discrete frame selection |
| Typewriter reveal | steps() | Discrete character-width increments |
| Branded emphasized motion | cubic-bezier() or linear() | Controlled custom progress |
Common Interview Questions
What is a CSS timing function? It maps elapsed time to interpolation progress, controlling how a transition or animation accelerates, decelerates, or jumps during its duration.
What is the default timing function? The initial value of both transition and animation timing functions is ease.
Does ease-out make a transition finish sooner? No. It advances more progress early but still completes at the declared duration.
When is linear appropriate? It suits genuinely constant-rate or continuous motion such as spinner rotation and time-linked progress.
What does cubic-bezier() define? It defines two control points for a cubic Bezier curve whose fixed endpoints map zero time to zero progress and full time to full progress.
What restrictions apply to Bezier coordinates? The x coordinates must be between zero and one; y coordinates may fall outside that range.
What does steps() do? It divides progress into discrete intervals and controls at which boundaries the jumps occur.
What is the difference between linear and linear()? The keyword is constant-rate easing, while the function describes a custom piecewise-linear curve with multiple stops.
Can animation timing change between keyframes? Yes. A timing function declared at a keyframe shapes the segment from that keyframe to the next one.
Are timing functions a performance optimization? Not primarily. Rendering cost depends more on animated properties, affected pixels, element count, and pipeline work.
Practical Review Checklist
- Identify whether the motion represents entry, exit, reversal, progress, or discrete frames.
- Start with a built-in keyword before introducing a custom curve.
- Use quick early progress for direct user feedback.
- Pair easing with an appropriate duration and travel distance.
- Use
linearonly when constant-rate progress communicates the right behavior. - Use
steps()only for intentionally discrete changes. - Keep custom Bezier x coordinates in the valid zero-to-one range.
- Use overshoot sparingly and in a way that matches the product tone.
- Assign separate timing functions to properties when their visual roles differ.
- Test both entry and exit rather than assuming one curve fits both.
- Test interrupted and rapidly reversed transitions.
- Centralize common curves as documented design-system tokens.
- Provide reduced-motion behavior for unnecessary spatial movement.
- Keep state understandable without animation.
- Profile the animated properties instead of blaming the easing keyword for performance.
- Use browser easing editors and animation tools to inspect custom motion.
- Evaluate motion in the real component on representative devices and viewport sizes.
Key Takeaway
A CSS timing function determines how progress unfolds inside a fixed duration. linear advances at a constant rate, ease-out responds quickly and settles, ease-in accelerates toward the end, and ease-in-out balances reversible movement. cubic-bezier() provides precise smooth curves, steps() creates deliberate jumps, and linear() supports custom multi-stop progress.
The best curve is the one that supports the interaction without calling unnecessary attention to itself. Consider direction, distance, duration, property, interruption, performance, and reduced-motion preferences together. Build a small reusable motion vocabulary, test it under real input and lifecycle conditions, and preserve meaning when motion is absent. Timing functions are most effective when they make the interface feel immediate, coherent, and understandable rather than merely animated.