CSS Transforms & Transitions: The Complete Guide for 2026

Published on

CSS transforms and transitions are the foundation of every smooth, polished interface on the modern web. Transforms let you move, rotate, scale, and distort elements without touching document flow. Transitions let you animate those changes over time. Together, they give you high-performance motion that runs on the GPU and works even when JavaScript is disabled. This guide covers every transform function, every transition property, practical patterns you can copy into your projects today, and the performance knowledge you need to keep animations at 60fps.

1. 2D Transform Functions

The transform property accepts one or more transform functions that modify an element's coordinate system. The element's layout space remains unchanged — transforms are purely visual. This means a translated element still occupies its original position in the document flow, and surrounding elements are not affected.

translate()

Moves an element along the X and/or Y axes. Accepts lengths (px, rem, em) or percentages. Percentages are relative to the element's own size, which makes translate(-50%, -50%) the classic centering trick.

/* Move 20px right and 10px down */
.element {
    transform: translate(20px, 10px);
}

/* Horizontal only */
.slide-right {
    transform: translateX(100px);
}

/* Vertical only */
.slide-down {
    transform: translateY(50%);
}

/* Classic absolute centering */
.centered {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
}

rotate()

Rotates an element clockwise around its center point (by default). Accepts deg, rad, grad, or turn units. Negative values rotate counter-clockwise.

/* Rotate 45 degrees clockwise */
.tilted {
    transform: rotate(45deg);
}

/* Quarter turn (same as 90deg) */
.quarter {
    transform: rotate(0.25turn);
}

/* Counter-clockwise */
.reverse {
    transform: rotate(-15deg);
}

scale()

Resizes an element. A value of 1 is the original size, 2 doubles it, and 0.5 halves it. You can scale X and Y independently. Unlike changing width and height, scaling does not trigger layout recalculation.

/* Scale uniformly to 150% */
.grow {
    transform: scale(1.5);
}

/* Scale X and Y independently */
.stretch {
    transform: scale(2, 0.5);
}

/* Mirror horizontally (flip) */
.flip-h {
    transform: scaleX(-1);
}

/* Mirror vertically */
.flip-v {
    transform: scaleY(-1);
}

skew()

Distorts an element by tilting it along the X and/or Y axes. Useful for creating parallelogram shapes, dynamic angles, and decorative effects without background images.

/* Skew along X axis */
.slant {
    transform: skewX(-5deg);
}

/* Skew along both axes */
.distort {
    transform: skew(10deg, 5deg);
}

/* Parallelogram button — skew container, un-skew text */
.parallelogram {
    transform: skewX(-15deg);
}
.parallelogram span {
    display: inline-block;
    transform: skewX(15deg);
}

matrix()

The matrix(a, b, c, d, tx, ty) function combines translate, rotate, scale, and skew into a single 2D transformation matrix. In practice, you rarely write matrix values by hand — browsers compute them internally. But understanding the matrix is useful for reading computed styles and for library interop.

/* Equivalent to: translate(30px, 20px) scale(1.5) rotate(45deg) */
.complex {
    transform: matrix(1.06, 1.06, -1.06, 1.06, 30, 20);
}

/* Identity matrix (no transform) */
.identity {
    transform: matrix(1, 0, 0, 1, 0, 0);
}

For most use cases, the individual functions (translate, rotate, scale) are far more readable. Reserve matrix() for when you need to set a computed transform directly, such as when syncing CSS with a JavaScript physics engine.

2. Transform Origin

By default, transforms originate from the center of the element (50% 50%). The transform-origin property changes this pivot point, dramatically altering how rotations, scales, and skews behave.

/* Rotate from top-left corner */
.swing {
    transform-origin: top left;
    transform: rotate(15deg);
}

/* Scale from bottom-center */
.grow-up {
    transform-origin: bottom center;
    transform: scale(1.5);
}

/* Pixel values for precise control */
.precise {
    transform-origin: 20px 40px;
    transform: rotate(30deg);
}

/* Three-value syntax for 3D (x, y, z) */
.depth-pivot {
    transform-origin: 50% 50% -100px;
}

Common keyword values include top, right, bottom, left, and center. You can combine them: top right is the same as 100% 0%. Understanding transform-origin is essential for building realistic rotation effects like swinging doors, clock hands, and orbiting elements.

3. Combining Transforms

You can chain multiple transform functions in a single declaration. The key insight is that order matters — transforms are applied from right to left (or equivalently, each transform operates in the coordinate system established by the previous one).

/* Translate THEN rotate: element moves first, then rotates in place */
.a {
    transform: rotate(45deg) translate(100px, 0);
}

/* Rotate THEN translate: element rotates first,
   then moves along the rotated axis */
.b {
    transform: translate(100px, 0) rotate(45deg);
}

/* Practical: scale + lift on hover */
.card:hover {
    transform: translateY(-8px) scale(1.02);
}

/* Multiple transforms for a 3D-style badge tilt */
.badge:hover {
    transform: perspective(600px) rotateY(-5deg) scale(1.05);
}

A common mistake is trying to set individual transform functions separately. Each transform declaration replaces the previous one entirely. To animate just one transform independently, use the individual transform properties introduced in CSS Individual Transform Properties (now widely supported):

/* Individual transform properties (modern browsers) */
.element {
    translate: 0 0;
    rotate: 0deg;
    scale: 1;
    transition: translate 0.3s, rotate 0.3s, scale 0.3s;
}

.element:hover {
    translate: 0 -10px;
    rotate: 5deg;
    scale: 1.1;
}

Individual transform properties are applied in a fixed order: translate first, then rotate, then scale. This is often more intuitive than the right-to-left order of the combined transform property.

4. 3D Transforms

CSS 3D transforms extend the coordinate system with a Z axis, enabling effects that simulate depth: card flips, cube rotations, parallax layers, and more. Three key properties control the 3D rendering context.

perspective

The perspective property (or function) defines how far the viewer is from the Z=0 plane. Smaller values create more dramatic foreshortening; larger values produce subtler depth.

/* Applied to the parent — all children share one vanishing point */
.stage {
    perspective: 1000px;
}

/* Applied per-element via the transform function */
.element {
    transform: perspective(800px) rotateY(25deg);
}

/* Move the vanishing point */
.stage {
    perspective: 1000px;
    perspective-origin: 25% 50%;
}

The difference between perspective as a property vs. as a function: the property on a parent creates a shared 3D space where all children relate to a single vanishing point. The function applies per-element with its own independent perspective.

rotateX(), rotateY(), rotateZ()

Rotate around individual axes. rotateZ() is equivalent to the 2D rotate().

/* Tilt backward (rotate around horizontal axis) */
.tilt-back {
    transform: perspective(600px) rotateX(15deg);
}

/* Turn sideways (rotate around vertical axis) */
.turn {
    transform: perspective(600px) rotateY(30deg);
}

/* Rotate around an arbitrary axis */
.custom-axis {
    transform: rotate3d(1, 1, 0, 45deg);
}

translate3d() and translateZ()

Move elements along the Z axis to bring them closer to or farther from the viewer. Combined with perspective, this creates genuine depth effects.

/* Push element toward the viewer (appears larger) */
.closer {
    transform: perspective(600px) translateZ(100px);
}

/* Push element away (appears smaller) */
.farther {
    transform: perspective(600px) translateZ(-200px);
}

/* Full 3D movement */
.floating {
    transform: perspective(600px) translate3d(20px, -10px, 50px);
}

transform-style and backface-visibility

These two properties are essential for multi-element 3D scenes like card flips and cube rotations.

/* preserve-3d lets children exist in the parent's 3D space */
.card-inner {
    transform-style: preserve-3d;
    transition: transform 0.6s;
}

/* Hide the back of rotated elements */
.card-front,
.card-back {
    backface-visibility: hidden;
    position: absolute;
    inset: 0;
}

.card-back {
    transform: rotateY(180deg);
}

/* Flip on hover */
.card:hover .card-inner {
    transform: rotateY(180deg);
}

Without transform-style: preserve-3d, child elements are flattened into the parent's plane, destroying any 3D effect. Without backface-visibility: hidden, you see mirrored content when an element rotates past 90 degrees.

5. CSS Transitions Deep Dive

Transitions animate property changes between two states. They are the simplest way to add motion to your UI — no keyframes needed, no JavaScript required. A transition needs only two things: a property that changes and a transition declaration.

transition-property

Specifies which CSS properties to animate. You can target specific properties or use all. Using specific properties is better for performance and prevents unintended animations.

/* Only animate background-color and transform */
.button {
    transition-property: background-color, transform;
}

/* Animate everything that changes (convenient but less precise) */
.card {
    transition-property: all;
}

transition-duration

How long the transition takes. Accepts seconds (s) or milliseconds (ms). Different properties can have different durations.

.button {
    transition-property: background-color, transform, box-shadow;
    transition-duration: 0.2s, 0.3s, 0.3s;
}

Guidelines: 100–200ms for micro-interactions (button presses, toggles). 200–400ms for UI changes (panel slides, card lifts). 400–600ms for large-scale changes (page transitions, modal entrances). Anything over 600ms starts to feel sluggish.

transition-timing-function

Controls the acceleration curve. Built-in values:

transition-delay

Adds a delay before the transition starts. Useful for staggering animations on multiple elements.

/* Stagger list items */
.list-item:nth-child(1) { transition-delay: 0ms; }
.list-item:nth-child(2) { transition-delay: 50ms; }
.list-item:nth-child(3) { transition-delay: 100ms; }
.list-item:nth-child(4) { transition-delay: 150ms; }

Transition Shorthand

The transition shorthand combines all four properties: property duration timing-function delay.

/* Shorthand: property | duration | timing | delay */
.button {
    transition: background-color 0.2s ease 0s;
}

/* Multiple transitions */
.card {
    transition:
        transform 0.3s ease-out,
        box-shadow 0.3s ease-out,
        background-color 0.2s ease;
}

/* Practical hover effect */
.card {
    background: #1a1d27;
    border-radius: 8px;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
    transition: transform 0.3s ease-out, box-shadow 0.3s ease-out;
}

.card:hover {
    transform: translateY(-6px);
    box-shadow: 0 12px 24px rgba(0, 0, 0, 0.4);
}

6. Timing Functions & Custom Cubic-Bezier

The built-in timing keywords are actually predefined cubic-bezier() curves. Understanding cubic-bezier gives you complete control over how your animations feel.

How cubic-bezier() Works

A cubic Bezier curve is defined by four control values: cubic-bezier(x1, y1, x2, y2). The X axis represents time (always 0 to 1). The Y axis represents progression (0 to 1, but can exceed these bounds for overshoot effects).

/* Built-in equivalents */
ease:        cubic-bezier(0.25, 0.1, 0.25, 1.0)
linear:      cubic-bezier(0, 0, 1, 1)
ease-in:     cubic-bezier(0.42, 0, 1.0, 1.0)
ease-out:    cubic-bezier(0, 0, 0.58, 1.0)
ease-in-out: cubic-bezier(0.42, 0, 0.58, 1.0)

Custom Curves for Professional UI

/* Snappy — fast start, decelerates sharply */
.snappy {
    transition: transform 0.2s cubic-bezier(0.2, 0, 0, 1);
}

/* Bounce — overshoots then settles */
.bounce {
    transition: transform 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
}

/* Elastic — overshoots, comes back, settles */
.elastic {
    transition: transform 0.6s cubic-bezier(0.68, -0.55, 0.27, 1.55);
}

/* Material Design standard curve */
.material {
    transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}

/* Apple-style deceleration */
.apple {
    transition: all 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}

Use your browser's DevTools to experiment: Chrome and Firefox both have built-in cubic-bezier editors in the Styles panel. Click the timing function icon next to any transition to open a visual curve editor.

steps() Timing Function

The steps() function creates a stepping animation instead of a smooth curve. Useful for sprite sheet animations, typewriter effects, and clock-tick movements.

/* Sprite sheet animation: 8 frames */
.sprite {
    width: 64px;
    height: 64px;
    background: url('spritesheet.png') 0 0;
    animation: walk 0.8s steps(8) infinite;
}

@keyframes walk {
    to { background-position: -512px 0; }
}

/* Typewriter cursor blink */
.cursor {
    border-right: 2px solid white;
    animation: blink 1s steps(1) infinite;
}

@keyframes blink {
    50% { border-color: transparent; }
}

7. Practical Examples

Card Flip Animation

A 3D card flip is one of the most popular transform effects. This pattern uses perspective, transform-style: preserve-3d, and backface-visibility: hidden.

/* HTML:
<div class="flip-card">
  <div class="flip-card-inner">
    <div class="flip-card-front">Front</div>
    <div class="flip-card-back">Back</div>
  </div>
</div> */

.flip-card {
    width: 300px;
    height: 200px;
    perspective: 1000px;
}

.flip-card-inner {
    position: relative;
    width: 100%;
    height: 100%;
    transform-style: preserve-3d;
    transition: transform 0.6s cubic-bezier(0.4, 0, 0.2, 1);
}

.flip-card:hover .flip-card-inner {
    transform: rotateY(180deg);
}

.flip-card-front,
.flip-card-back {
    position: absolute;
    inset: 0;
    backface-visibility: hidden;
    border-radius: 12px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 1.5rem;
    font-weight: 600;
}

.flip-card-front {
    background: linear-gradient(135deg, #667eea, #764ba2);
    color: white;
}

.flip-card-back {
    background: linear-gradient(135deg, #f093fb, #f5576c);
    color: white;
    transform: rotateY(180deg);
}

Hover Lift Effect

A subtle lift with shadow deepening creates a tactile, interactive feel for cards and buttons.

.hover-card {
    background: #1a1d27;
    border: 1px solid #2a2e3a;
    border-radius: 12px;
    padding: 2rem;
    transition:
        transform 0.25s cubic-bezier(0.2, 0, 0, 1),
        box-shadow 0.25s cubic-bezier(0.2, 0, 0, 1);
}

.hover-card:hover {
    transform: translateY(-4px);
    box-shadow:
        0 12px 20px rgba(0, 0, 0, 0.3),
        0 4px 8px rgba(0, 0, 0, 0.2);
}

/* Subtle press effect on click */
.hover-card:active {
    transform: translateY(-1px);
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
    transition-duration: 0.1s;
}

CSS Loading Spinner

A pure-CSS spinner using rotation and a partial border. No images, no JavaScript, minimal markup.

.spinner {
    width: 40px;
    height: 40px;
    border: 3px solid rgba(255, 255, 255, 0.1);
    border-top-color: #3b82f6;
    border-radius: 50%;
    animation: spin 0.8s linear infinite;
}

@keyframes spin {
    to { transform: rotate(360deg); }
}

/* Dual-ring spinner variant */
.dual-spinner {
    width: 48px;
    height: 48px;
    border: 4px solid transparent;
    border-top-color: #3b82f6;
    border-bottom-color: #10b981;
    border-radius: 50%;
    animation: spin 1s ease-in-out infinite;
}

Parallax Scrolling Layers

CSS-only parallax using perspective and translateZ() on a scroll container. Each layer moves at a different speed based on its Z position.

.parallax-container {
    height: 100vh;
    overflow-x: hidden;
    overflow-y: auto;
    perspective: 1px;
    perspective-origin: center center;
}

.parallax-layer {
    position: absolute;
    inset: 0;
}

/* Background moves slowly */
.parallax-bg {
    transform: translateZ(-2px) scale(3);
}

/* Midground moves at medium speed */
.parallax-mid {
    transform: translateZ(-1px) scale(2);
}

/* Foreground stays at normal speed */
.parallax-fg {
    transform: translateZ(0);
}

The scale() compensates for the perspective shrinkage at negative Z positions, keeping elements at their intended size. This technique works entirely with CSS and gives smooth scrolling parallax without any JavaScript scroll handlers.

Staggered Navigation Animation

Combine transitions with CSS custom properties to create staggered reveal animations.

.nav-item {
    opacity: 0;
    transform: translateY(20px);
    transition:
        opacity 0.4s ease-out,
        transform 0.4s ease-out;
    transition-delay: calc(var(--i) * 80ms);
}

.nav-open .nav-item {
    opacity: 1;
    transform: translateY(0);
}

/* HTML:
<li class="nav-item" style="--i: 0">Home</li>
<li class="nav-item" style="--i: 1">About</li>
<li class="nav-item" style="--i: 2">Work</li>
<li class="nav-item" style="--i: 3">Contact</li> */

8. Performance & GPU Acceleration

Not all CSS properties animate equally. Understanding the browser's rendering pipeline is essential for smooth 60fps animations.

The Rendering Pipeline

Every frame, the browser goes through up to five stages: Style → Layout → Paint → Composite. Properties that trigger earlier stages are more expensive to animate:

Rule of thumb: Whenever possible, animate only transform and opacity. These two properties can be handled entirely by the GPU compositor without touching the main thread, producing consistently smooth animations even during heavy JavaScript execution.

will-change

The will-change property hints to the browser which properties are about to change, allowing it to promote the element to its own compositor layer ahead of time.

/* Apply will-change just before the animation */
.card {
    transition: transform 0.3s ease-out;
}

/* Promote to own layer when parent is hovered */
.card-container:hover .card {
    will-change: transform;
}

/* Or apply on the element's own hover for simpler cases */
.button:hover {
    will-change: transform;
}

/* WRONG: Don't set will-change on everything permanently */
/* * { will-change: transform; } <-- wastes GPU memory */

GPU Acceleration Best Practices

/* GOOD: animate transform and opacity */
.animate-good {
    transition: transform 0.3s, opacity 0.3s;
}
.animate-good:hover {
    transform: scale(1.1);
    opacity: 0.9;
}

/* AVOID: animating layout properties */
.animate-avoid {
    transition: width 0.3s, margin-left 0.3s; /* triggers layout */
}

/* REFACTOR: use transform instead */
.animate-refactored {
    transition: transform 0.3s;
}
.animate-refactored:hover {
    transform: scaleX(1.1) translateX(10px);
}

/* Force GPU layer (old trick, prefer will-change) */
.gpu-layer {
    transform: translateZ(0); /* null transform, creates layer */
}

Monitor your animation performance in Chrome DevTools by enabling the Rendering > Paint flashing and Layer borders overlays. Green flashes indicate repaints. Yellow/orange borders show compositor layers. The Performance panel's Frames section reveals dropped frames.

9. CSS vs JavaScript Animations

Both CSS and JavaScript can create animations, but they excel at different things. Here is when to use each.

Use CSS Transitions/Transforms When:

Use JavaScript (Web Animations API / GSAP) When:

/* CSS approach: simple and declarative */
.modal {
    opacity: 0;
    transform: translateY(20px) scale(0.95);
    transition: opacity 0.3s ease-out, transform 0.3s ease-out;
    pointer-events: none;
}
.modal.open {
    opacity: 1;
    transform: translateY(0) scale(1);
    pointer-events: auto;
}

// JavaScript approach: dynamic and controllable
const el = document.querySelector('.element');
const animation = el.animate([
    { transform: 'translateX(0)', opacity: 1 },
    { transform: 'translateX(300px)', opacity: 0 }
], {
    duration: 500,
    easing: 'cubic-bezier(0.4, 0, 0.2, 1)',
    fill: 'forwards'
});

// Pause, reverse, or adjust at runtime
animation.pause();
animation.reverse();
animation.playbackRate = 0.5;

The Web Animations API (WAAPI) bridges the gap: it gives you JavaScript control over animations that still run on the compositor thread. For complex animation needs, consider our CSS Animations guide which covers keyframes in depth.

10. Common Pitfalls & Debugging Tips

Even experienced developers hit these issues. Knowing them saves hours of debugging.

Pitfall 1: Transitioning display or visibility

You cannot transition display: none to display: block. The element is either in the render tree or it is not. Workaround: use opacity and pointer-events, or use the newer @starting-style rule and transition-behavior: allow-discrete.

/* Modern approach (2024+ browsers) */
.tooltip {
    opacity: 0;
    display: none;
    transition: opacity 0.3s, display 0.3s allow-discrete;
}

@starting-style {
    .tooltip.visible {
        opacity: 0;
    }
}

.tooltip.visible {
    display: block;
    opacity: 1;
}

/* Classic fallback approach */
.tooltip-classic {
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transition: opacity 0.3s, visibility 0.3s;
}
.tooltip-classic.visible {
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
}

Pitfall 2: Overwriting transforms

Setting transform in a hover state wipes out any base transform. Combine all transforms in one declaration, or use individual transform properties.

/* BUG: hover wipes out the centering */
.centered {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
}
.centered:hover {
    transform: scale(1.1); /* Centering is GONE */
}

/* FIX: include all transforms */
.centered:hover {
    transform: translate(-50%, -50%) scale(1.1);
}

/* BETTER FIX: use individual properties */
.centered {
    position: absolute;
    top: 50%;
    left: 50%;
    translate: -50% -50%;
    scale: 1;
    transition: scale 0.3s ease;
}
.centered:hover {
    scale: 1.1;
}

Pitfall 3: Transition on initial load

Elements with transitions can flash on page load as styles are applied. Prevent this by adding a no-transition class to the body and removing it after the page has fully rendered.

/* CSS */
.no-transition * {
    transition: none !important;
}

// JavaScript
window.addEventListener('load', () => {
    document.body.classList.remove('no-transition');
});

Pitfall 4: Jank from animating box-shadow

Animating box-shadow directly triggers repaints. For smooth shadow animations, use a pseudo-element with the shadow and animate its opacity.

.card {
    position: relative;
}

.card::after {
    content: '';
    position: absolute;
    inset: 0;
    border-radius: inherit;
    box-shadow: 0 16px 32px rgba(0, 0, 0, 0.4);
    opacity: 0;
    transition: opacity 0.3s ease-out;
    pointer-events: none;
}

.card:hover::after {
    opacity: 1;
}

Debugging Tools

11. Browser Support & Progressive Enhancement

2D transforms, 3D transforms, and transitions have been supported across all major browsers for years. Here is the current state for newer features:

Progressive Enhancement Pattern

/* Base: works everywhere, no animation */
.element {
    opacity: 1;
}

/* Enhanced: add transitions for capable browsers */
@supports (transition: opacity 0.3s) {
    .element {
        transition: opacity 0.3s ease-out, transform 0.3s ease-out;
    }
}

/* Respect user preferences */
@media (prefers-reduced-motion: reduce) {
    .element {
        transition-duration: 0.01ms !important;
        animation-duration: 0.01ms !important;
    }
}

/* Use individual transforms where supported */
@supports (translate: 0) {
    .element {
        translate: 0 0;
        rotate: 0deg;
        scale: 1;
        transition: translate 0.3s, rotate 0.3s, scale 0.3s;
    }
}

Always include prefers-reduced-motion handling. Some users experience motion sickness or vestibular disorders, and reducing or removing animation is both an accessibility requirement and good practice. See our CSS Performance Optimization guide for more on this topic.

Frequently Asked Questions

What is the difference between CSS transforms and CSS transitions?

CSS transforms change the visual appearance of an element by rotating, scaling, skewing, or translating it. They apply instantly and define the end state. CSS transitions animate the change between two states over a specified duration. You use transforms to define what changes, and transitions to define how smoothly those changes occur. For example, transform: scale(1.2) makes an element 20% larger immediately, but adding transition: transform 0.3s ease makes that scaling animate smoothly over 300 milliseconds.

Why should I use translate() instead of changing top/left for positioning?

Using transform: translate() instead of changing top, left, right, or bottom properties is significantly better for performance. Changing top/left triggers layout recalculation (reflow) for the element and potentially its siblings, which is expensive. Transforms are handled by the GPU compositor and only affect the composite stage of the rendering pipeline, skipping layout and paint entirely. This means translate() animations run at 60fps even on low-powered devices. Additionally, translate() supports sub-pixel rendering for smoother movements.

How do I create a 3D card flip effect with CSS transforms?

To create a 3D card flip, you need three elements: a container, a front face, and a back face. Set perspective on the container (e.g., perspective: 1000px). The inner wrapper needs transform-style: preserve-3d and a transition on the transform property. Both card faces should be positioned absolutely with backface-visibility: hidden, and the back face should start rotated with transform: rotateY(180deg). On hover, rotate the inner wrapper 180 degrees. This reveals the back face while hiding the front, creating a realistic 3D flip.

What is cubic-bezier and how do I create custom timing functions?

Cubic-bezier is a mathematical function that defines the acceleration curve of a CSS transition. It takes four parameters: cubic-bezier(x1, y1, x2, y2), where (x1, y1) and (x2, y2) are the control points of the curve. The X axis represents time (0 to 1) and the Y axis represents progression. Values outside 0–1 on the Y axis create overshoot or bounce effects. Built-in keywords like ease, ease-in, and ease-out are predefined cubic-bezier curves. Use browser DevTools or tools like cubic-bezier.com to visually design custom curves.

When should I use will-change in CSS and what are the risks?

The will-change property hints to the browser that an element's property is about to change, allowing it to promote the element to its own compositor layer. Use will-change: transform on elements that will be animated, but only apply it just before the animation starts (e.g., on parent hover) and remove it after. The risks of overusing will-change include increased GPU memory consumption, potential visual artifacts, and degraded performance if too many elements are promoted simultaneously. Never apply will-change to many elements at once or set it permanently in static stylesheets.

For a deeper dive into keyframe animations and the animation property, see our CSS Animations Complete Guide. For layout techniques that pair well with transforms, check out our CSS Grid Complete Guide.

Related Tools