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, and ease-in-out differ.
  • How cubic Bezier control points shape acceleration, deceleration, and overshoot.
  • How steps(), step-start, and step-end produce 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

FunctionProgress PatternUseful Starting Point
linearConstant rateSpinners and time-linked progress
easeQuick movement with a soft finishColors, shadows, and general feedback
ease-inSlow start, fast finishSelected exit motion
ease-outFast start, slow finishEntrances and direct interactions
ease-in-outSlow start and finishReversible 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

KeywordEquivalent Curve
linearcubic-bezier(0, 0, 1, 1)
easecubic-bezier(0.25, 0.1, 0.25, 1)
ease-incubic-bezier(0.42, 0, 1, 1)
ease-outcubic-bezier(0, 0, 0.58, 1)
ease-in-outcubic-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

MistakeWhy It FailsBetter Direction
Using linear for every controlDirect interactions feel mechanicalStart with ease-out or ease
Using long ease-in hover motionInitial response appears delayedUse a short curve with quick early progress
Easing a repeating spinnerEvery loop visibly speeds up and slows downUse constant-rate linear
Random Bezier coordinatesMotion becomes inconsistent and hard to maintainDefine purposeful shared tokens
Excessive overshootRoutine UI feels unstable or distractingReserve emphasis for suitable contexts
Using steps() for natural movementSpatial motion visibly jumpsUse steps only for discrete sequences
Assuming easing changes total durationThe mental model is incorrectSeparate duration from progress distribution
One curve for every propertyFade and movement may need different pacingAssign curves per property when valuable
Ignoring reverse and interruptionComponents feel inconsistent during real useTest rapid state changes
Using motion as the only state cueState disappears when motion is reducedProvide persistent semantic and visual cues

Practical Timing Function Guide

InteractionUseful Starting PointReason
Button or link feedbackease-outImmediate response with a soft finish
Color or shadow changeeaseGeneral smooth interpolation
Modal entranceease-outQuick appearance and gentle settling
Modal exitease-inOptional acceleration away
Accordion or switchease-in-outBalanced reversible movement
Drawer entranceease-out or a system curveResponsive large-surface movement
Continuous spinnerlinearNo visible iteration seam
Sprite sheetsteps()Discrete frame selection
Typewriter revealsteps()Discrete character-width increments
Branded emphasized motioncubic-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 linear only 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.