Skip to main content

CSS Transitions: Smooth State Changes

CSS transitions allow property values to change smoothly over a specified duration, instead of switching abruptly. They are the foundation of polished, professional web interactions.

1. The Four Core Properties​

PropertyDescriptionExample
transition-propertyWhich CSS property to animatebackground-color, transform, all
transition-durationHow long the transition takes0.3s, 300ms
transition-timing-functionThe speed curveease, linear, cubic-bezier(...)
transition-delayWait before starting0s, 0.1s
/* Longhand */
.button {
transition-property: background-color, transform, box-shadow;
transition-duration: 0.25s, 0.2s, 0.3s;
transition-timing-function: ease, ease-out, ease;
transition-delay: 0s, 0s, 0s;
}

/* Shorthand (recommended) */
.button {
transition: background-color 0.25s ease,
transform 0.2s ease-out,
box-shadow 0.3s ease;
}

2. The all Shortcut​

transition: all 0.3s ease; animates every changing property simultaneously. Use it for prototyping but be more specific in production — animating all can accidentally animate properties like height or width which causes expensive layout recalculations.

3. Timing Functions in Depth​

The timing function controls the rate of change throughout the animation duration.

FunctionBehaviorBest For
easeSlow → Fast → SlowDefault, most natural feeling
ease-inSlow start → Fast endElements leaving the screen
ease-outFast start → Slow endElements entering the screen
ease-in-outSlow → Fast → Slow (symmetrical)Toggles, modals opening/closing
linearConstant speedLoading bars, rotating loaders
cubic-bezier(x1,y1,x2,y2)Custom curveBouncy or spring-like effects
steps(n)Jumps in N stepsSprite animations, typewriter effect

Custom Cubic Bezier​

Use cubic-bezier.com to visually design curves. Copy the output:

/* A satisfying "spring" feel */
.btn { transition: transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1); }

4. Triggering Transitions​

Transitions fire when a CSS property changes. Common triggers:

/* :hover — most common */
.card:hover { transform: translateY(-4px); }

/* :focus — keyboard / form accessibility */
.input:focus { border-color: var(--color-primary); box-shadow: 0 0 0 3px rgba(108,99,255,0.3); }

/* :active — button press feedback */
.btn:active { transform: scale(0.97); }

/* Class toggled by JavaScript */
.modal { opacity: 0; pointer-events: none; }
.modal.is-open { opacity: 1; pointer-events: auto; }

5. The transition-delay Use Case​

Stagger animations on a group of elements for a polished reveal effect:

.nav__item:nth-child(1) { transition-delay: 0ms; }
.nav__item:nth-child(2) { transition-delay: 50ms; }
.nav__item:nth-child(3) { transition-delay: 100ms; }
.nav__item:nth-child(4) { transition-delay: 150ms; }

6. Performance: What to Animate​

Only certain properties can be transitioned with GPU acceleration (cheap). Others trigger layout recalculation (expensive).

✅ GPU-Accelerated (Use These)❌ Triggers Layout (Avoid)
transformwidth, height
opacitytop, left, bottom, right
filtermargin, padding
clip-pathfont-size
/* ❌ Bad — triggers layout every frame */
.elem:hover { width: 300px; left: 100px; }

/* ✅ Good — GPU-composited, 60fps smooth */
.elem:hover { transform: scaleX(1.5) translateX(100px); }

7. Respecting Reduced Motion​

Always honor users who prefer less motion (vestibular disorders, epilepsy):

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

8. Practical Polished Button Example​

.btn {
background: var(--color-primary, #6c63ff);
color: white;
padding: 0.8rem 1.75rem;
border-radius: 8px;
border: none;
cursor: pointer;
/* Transition specific properties, not all */
transition:
background-color 0.2s ease,
transform 0.15s ease-out,
box-shadow 0.2s ease;
}
.btn:hover {
background: var(--color-primary-dark, #5a52d5);
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(108, 99, 255, 0.4);
}
.btn:active {
transform: translateY(0);
box-shadow: none;
}