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;
}