Munsif.
AboutExperienceProjectsAchievementsBlogsContact
HomeAboutExperienceProjectsAchievementsBlogsContact
Munsif.

Frontend Developer crafting scalable web applications with modern technologies and clean code practices.

Quick Links

  • About
  • Experience
  • Projects
  • Achievements
  • Blogs
  • Contact

Connect

© 2026 Shaik Munsif. All rights reserved.

Built with Next.js & Tailwind

0%
Welcome back!Continue where you left off
Back to Blogs
CSS

CSS Animations & Transitions Mastery: From Hover Effects to Keyframe Sequences

Master CSS transitions and animations. Covers timing functions, @keyframes, animation-fill-mode, performance (transform vs layout properties), will-change, practical patterns like loading spinners and staggered entry, and prefers-reduced-motion accessibility.

Mar 14, 202622 min read
CSSAnimationsTransitionsPerformanceBeginner Guide
CSS MasteryPart 4 of 10
  • 1. CSS Flexbox Mastery: The Complete Visual Guide to Flexible Layouts
  • 2. CSS Grid Mastery: From Basics to Advanced Layouts
  • 3. CSS Custom Properties (Variables) Mastery: Dynamic Theming & Beyond
  • 4. CSS Animations & Transitions Mastery: From Hover Effects to Keyframe Sequences
  • 5. CSS Selectors Deep Dive: From Basic to :has() & :is()
  • 6. CSS Box Model Mastery: Content, Padding, Border & Margin Explained
  • 7. CSS Responsive Design Mastery: Mobile-First, clamp(), & Container Queries
  • 8. CSS Specificity & Cascade Mastery: How Browsers Resolve Conflicts
  • 9. CSS Modern Layout Techniques: Scroll Snap, Subgrid, Logical Properties & More
  • 10. CSS Pseudo-Elements Mastery: ::before, ::after, Counters & Decorative Techniques

CSS animations bring your interfaces to life. A well-placed transition can make a button feel satisfying to click, and a carefully crafted animation can guide a user's attention exactly where you need it. But used badly, they create jank, distraction, and frustration.

In this guide, we'll cover both transitions (simple A-to-B changes) and animations (complex multi-step sequences), with a strong focus on performance — because a beautiful animation that drops frames is worse than no animation at all.


1. Transitions: Smooth State Changes

A CSS transition smoothly animates a property from one value to another when that property changes (on hover, focus, class toggle, etc.).

The Four Ingredients

css
.button {
  transition-property: background-color;    /* What to animate */
  transition-duration: 0.3s;                 /* How long */
  transition-timing-function: ease;          /* Speed curve */
  transition-delay: 0s;                      /* Wait before starting */
}

/* Shorthand */
.button {
  transition: background-color 0.3s ease 0s;
}
Live Preview

2. Transition Properties You Can Animate

Not all CSS properties can be transitioned. Here's what works:

AnimatableExamples
✅ Colorscolor, background-color, border-color
✅ Opacityopacity
✅ Transformtransform (translate, rotate, scale, skew)
✅ Box modelwidth, height, margin, padding
✅ Shadowbox-shadow, text-shadow
✅ Border radiusborder-radius
❌ Displaydisplay: none → can't transition
❌ Font familyDiscrete values can't interpolate
⚠️ Height autoheight: 0 → height: auto doesn't work directly

Pro Tip: When you use transition: all, every animatable property will transition. This is convenient but can cause unwanted transitions. Be specific when possible: transition: background-color 0.3s, transform 0.3s.


3. Timing Functions Explained

The timing function controls the speed curve of the animation — how fast it goes at different points.

FunctionBehavior
ease (default)Slow start, fast middle, slow end
linearConstant speed throughout
ease-inSlow start, fast end
ease-outFast start, slow end
ease-in-outSlow start, slow end
cubic-bezier(x1, y1, x2, y2)Custom curve
Live Preview
Hover over the box to see timing functions in action
ease
linear
ease-in
ease-out
ease-in-out

Custom Curves with cubic-bezier()

For total control, define your own curve. Useful values:

css
/* Snappy "pop" effect */
transition-timing-function: cubic-bezier(0.68, -0.55, 0.27, 1.55);

/* Smooth deceleration */
transition-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94);

Tool Tip: Use cubic-bezier.com to visually design and preview custom timing curves.


4. Keyframe Animations: Multi-Step Sequences

While transitions go from A to B, @keyframes animations can have as many steps as you want:

css
@keyframes slideIn {
  0%   { transform: translateX(-100%); opacity: 0; }
  50%  { opacity: 0.5; }
  100% { transform: translateX(0); opacity: 1; }
}

.element {
  animation: slideIn 0.5s ease-out;
}

The animation Shorthand

css
animation: name duration timing-function delay iteration-count direction fill-mode;

/* Example */
animation: bounce 1s ease-in-out 0s infinite alternate both;
PropertyValuesDefault
animation-namekeyframe namenone
animation-durationtime (e.g., 0.5s)0s
animation-timing-functionease, linear, etc.ease
animation-delaytime0s
animation-iteration-countnumber or infinite1
animation-directionnormal, reverse, alternatenormal
animation-fill-modenone, forwards, backwards, bothnone
Live Preview
Bounce
↕
Pulse
♥
Spin
⟳
Slide In
→

5. animation-fill-mode: Before & After States

This property controls how an element looks before the animation starts and after it ends.

ValueBefore AnimationAfter Animation
none (default)Original stylesOriginal styles
forwardsOriginal stylesStays at last keyframe
backwardsTakes 0% keyframeOriginal styles
bothTakes 0% keyframeStays at last keyframe
css
.fade-in {
  opacity: 0;
  animation: fadeIn 0.5s ease forwards;
  /* 'forwards' keeps opacity: 1 after animation ends */
}

@keyframes fadeIn {
  to { opacity: 1; }
}

6. animation-direction: Reverse & Alternate

ValueBehavior
normal0% → 100% every time
reverse100% → 0% every time
alternate0% → 100%, then 100% → 0% (ping-pong)
alternate-reverseStarts at 100% and alternates

alternate is perfect for yoyo/pulsing effects:

css
.breathe {
  animation: breathe 2s ease-in-out infinite alternate;
}

@keyframes breathe {
  from { transform: scale(1); }
  to { transform: scale(1.05); }
}

7. Multiple Animations

You can apply multiple animations to one element:

css
.element {
  animation:
    fadeIn 0.3s ease,
    slideUp 0.5s ease-out,
    pulse 2s ease-in-out 1s infinite;
}

Each animation can have its own duration, timing, delay, and iteration count.


8. Performance: What to Animate

This is arguably the most important section. Not all CSS properties perform equally during animation.

The Performance Tiers

TierPropertiesCostWhat the Browser Does
🟢 Besttransform, opacityCheapestGPU compositing only
🟡 Okaycolor, background-colorMediumRepaint (no layout shift)
🔴 Avoidwidth, height, margin, top, leftExpensiveFull layout recalculation

Why transform & opacity Win

When you animate transform or opacity, the browser promotes the element to its own GPU layer and can animate it independently without affecting the rest of the page. This is called compositing.

When you animate width or height, the browser has to recalculate the layout of every element that's affected — this is called layout thrashing and causes jank (dropped frames).

css
/* ❌ Causes layout thrashing */
.bad {
  transition: left 0.3s, top 0.3s;
}
.bad:hover {
  left: 50px;
  top: 50px;
}

/* ✅ Smooth GPU-accelerated animation */
.good {
  transition: transform 0.3s;
}
.good:hover {
  transform: translate(50px, 50px);
}

will-change: Preparation Hint

The will-change property tells the browser to prepare for an animation in advance:

css
.card {
  will-change: transform, opacity;
  transition: transform 0.3s, opacity 0.3s;
}

Warning: Don't overuse will-change. Each element with will-change gets its own GPU layer, which consumes memory. Only use it on elements that will actually animate, and remove it when the animation is done.


9. Transition Events in JavaScript

You can listen for transition completion in JavaScript:

javascript
element.addEventListener('transitionend', (e) => {
  console.log(`${e.propertyName} finished transitioning`);
  // Do something after animation completes
});

// Also available: transitionstart, transitioncancel

Animation Events

javascript
element.addEventListener('animationstart', handler);
element.addEventListener('animationiteration', handler); // Each loop
element.addEventListener('animationend', handler);

10. Practical Animation Patterns

Pattern 1: Hover Lift Effect

css
.card {
  transition: transform 0.2s ease, box-shadow 0.2s ease;
}

.card:hover {
  transform: translateY(-4px);
  box-shadow: 0 12px 24px rgba(0, 0, 0, 0.15);
}
Live Preview
🎨
Design
⚡
Performance
🔒
Security

Pattern 2: Loading Spinner

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

.spinner {
  width: 32px;
  height: 32px;
  border: 3px solid #e5e7eb;
  border-top-color: #3b82f6;
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
}
Live Preview
Ring Spinner
Dot Bounce

Pattern 3: Staggered Entry Animation

css
@keyframes fadeInUp {
  from {
    opacity: 0;
    transform: translateY(20px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.card:nth-child(1) { animation: fadeInUp 0.5s ease both; }
.card:nth-child(2) { animation: fadeInUp 0.5s ease 0.1s both; }
.card:nth-child(3) { animation: fadeInUp 0.5s ease 0.2s both; }
Live Preview
Card 1
Card 2
Card 3
Card 4

11. prefers-reduced-motion: Respecting User Preferences

Some users experience motion sickness or distraction from animations. Always respect the prefers-reduced-motion media query:

css
/* Default: animations are on */
.element {
  animation: fadeIn 0.5s ease;
  transition: transform 0.3s;
}

/* User prefers reduced motion: disable/simplify */
@media (prefers-reduced-motion: reduce) {
  .element {
    animation: none;
    transition: none;
  }
}

Or use a progressive enhancement approach:

css
/* No animations by default */
.element {
  opacity: 1;
}

/* Only add animations if user is okay with motion */
@media (prefers-reduced-motion: no-preference) {
  .element {
    animation: fadeIn 0.5s ease;
  }
}

12. Common Mistakes & Gotchas

Mistake 1: Animating Layout Properties

css
/* ❌ Expensive — triggers layout recalculation */
.card:hover { height: 300px; width: 400px; }

/* ✅ Cheap — GPU-composited */
.card:hover { transform: scale(1.05); }

Mistake 2: Forgetting animation-fill-mode

Your animated element snaps back to its original state? You probably need forwards:

css
.fade-in {
  opacity: 0;
  animation: fadeIn 0.3s ease forwards; /* ← key! */
}

Mistake 3: Using transition: all

css
/* ❌ Everything transitions, even things you didn't intend */
.card { transition: all 0.3s; }

/* ✅ Only transition what you need */
.card { transition: transform 0.3s, box-shadow 0.3s; }

Mistake 4: No Reduced Motion Fallback

Always check: @media (prefers-reduced-motion: reduce). It's an accessibility requirement.


Conclusion

Here's your animation decision framework:

  • Simple A→B change? → Use transition
  • Multi-step sequence? → Use @keyframes + animation
  • Need it forever? → animation-iteration-count: infinite
  • Keep final state? → animation-fill-mode: forwards
  • Performance critical? → Animate only transform and opacity
  • Accessibility? → Always add prefers-reduced-motion support

Remember: The best animations are the ones users don't consciously notice. They should make the interface feel natural and responsive, not draw attention to themselves.

🧠 Test Your Knowledge

Now that you've learned the concepts, let's see if you can apply them! Take this quick quiz to test your understanding.

PreviousCSS Custom Properties (Variables) Mastery: Dynamic Theming & BeyondNextCSS Selectors Deep Dive: From Basic to :has() & :is()

Written by

Shaik Munsif

Read more articles

Found this helpful? Share it with your network!

On this page

0/27
Question 1 of 10Easy
Score: 0/0

You add transition: background-color 0.3s ease to a button. When does the animation trigger?