Skip to content

RESOURCES / BLOG

How are fade-in animations created using CSS transitions and keyframes?

CSS featured image

Subtle fade-ins make UIs feel smoother and more polished without stealing focus. You will see dev threads filled with questions like when to use transitions vs keyframes, how to trigger fades on scroll, and how to keep animations accessible and fast.

How are fade-in animations created using CSS transitions and keyframes? I want a simple, reusable pattern for images, cards, and text that: 

  1. Fades in on load or when scrolled into view
  2. Stays performant on mobile
  3. Respects prefers-reduced-motion.

Sample CSS and a minimal JS snippet would help. Tips for handling images and placeholders would be great too.

You can implement fade-ins either with transitions or with keyframes. Use transitions when you toggle a property once, and keyframes when you need timeline control or multiple stages.

<style>
/* Start hidden */
.fade-target {
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 300ms ease-out, transform 300ms ease-out;
}

/* When ready to show */
.fade-target.is-visible {
  opacity: 1;
  transform: translateY(0);
}

/* Respect user preferences */
@media (prefers-reduced-motion: reduce) {
  .fade-target {
    transition: none;
    transform: none;
  }
}
</style>

<div class="fade-target" id="card">Content...</div>

<script>
// Example: reveal on page load
window.addEventListener('load', () => {
  document.getElementById('card').classList.add('is-visible');
});
</script>Code language: HTML, XML (xml)
<style>
@keyframes fadeIn {
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: 1; transform: translateY(0); }
}

.fade-in {
  animation: fadeIn 450ms ease-out both;
}

/* Motion preference */
@media (prefers-reduced-motion: reduce) {
  .fade-in { animation: none; }
}
</style>

<div class="fade-in">I will fade and lift in.</div>Code language: HTML, XML (xml)
<style>
.fade-on-scroll { opacity: 0; transform: translateY(12px); transition: opacity 360ms, transform 360ms; }
.fade-on-scroll.is-inview { opacity: 1; transform: translateY(0); }
</style>

<section class="fade-on-scroll">Section A</section>
<section class="fade-on-scroll">Section B</section>

<script>
const io = new IntersectionObserver((entries) => {
  entries.forEach(e => {
    if (e.isIntersecting) {
      e.target.classList.add('is-inview');
      io.unobserve(e.target);
    }
  });
}, { rootMargin: '0px 0px -10% 0px', threshold: 0.1 });

document.querySelectorAll('.fade-on-scroll').forEach(el => io.observe(el));
</script>Code language: HTML, XML (xml)
  • Animate only opacity and transform. Avoid layout-affecting properties like width or top.
  • Use short durations and gentle easing. 200 to 500 ms usually feels natural.
  • Avoid stacking too many effects. No more than a few parallel transitions.
  • Gate animations behind Intersection Observer instead of scroll handlers.
  • Respect prefers-reduced-motion. Provide a no-animation path.
  • Apply will-change sparingly and temporarily if needed for heavy views.

A common pattern is a blur-up or transparent-to-opaque fade once the image is loaded. For markup guidance, see this overview of the HTML image tag.

<style>

.img-fade { opacity: 0; transition: opacity 300ms ease-out; }
.img-fade.is-loaded { opacity: 1; }
</style>

<img
  class="img-fade"
  src="image-large.jpg"
  alt="Decorative fade-in"
  loading="lazy"
  width="1600" height="900"
/>

<script>
document.querySelectorAll('img.img-fade').forEach(img => {
  if (img.complete) img.classList.add('is-loaded');
  else img.addEventListener('load', () => img.classList.add('is-loaded'));
});
</script>Code language: HTML, XML (xml)
<style>
@keyframes fadeUp { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } }
.stagger > * { opacity: 0; animation: fadeUp 380ms ease-out both; }
.stagger > *:nth-child(1) { animation-delay: 50ms; }
.stagger > *:nth-child(2) { animation-delay: 120ms; }
.stagger > *:nth-child(3) { animation-delay: 190ms; }
</style>

<ul class="stagger">
  <li>Item 1</li>
  <li>Item 2</li>
  <li>Item 3</li>
</ul>Code language: HTML, XML (xml)

You can pair fade-ins with responsive, optimized assets to keep pages snappy. For background images, see these CSS tips on sizing and coverage: 6 ways to stretch a background image with CSS. For overall site performance principles, review what makes an optimized website.

  • Serve responsive images with automatic format and quality: https://res.cloudinary.com/<cloud_name>/image/upload/f_auto,q_auto,w_800/<public_id>.jpg
  • Use a tiny blurred placeholder and swap to the full image on load:
<img class="img-fade" src="https://res.cloudinary.com/demo/image/upload/e_blur:2000,q_1,w_40/sample.jpg" data-full="https://res.cloudinary.com/demo/image/upload/f_auto,q_auto,w_1200/sample.jpg" alt="Sample" />

<script>
// Blur-up swap
document.querySelectorAll('img[data-full]').forEach(img => {
  const full = new Image();
  full.src = img.getAttribute('data-full');
  full.addEventListener('load', () => {
    img.src = full.src;
    img.classList.add('is-loaded'); // fade-in
  });
});
</script>Code language: JavaScript (javascript)

You can also explore handy utilities on the Cloudinary tools page for quick media tasks during development.

  • Animating display or visibility. Use opacity and transforms for a smooth fade.
  • Forgetting initial state. Make sure elements start at opacity 0 before you toggle in.
  • Not handling reduced motion. Always provide a no-animation path.
  • Use transitions for one-off fades by toggling a class. Use keyframes for staged or staggered reveals.
  • Animate only opacity and transform for performance. Gate with Intersection Observer.
  • Respect prefers-reduced-motion. Keep durations short and easing gentle.
  • For images, apply a blur-up placeholder and fade-in once fully loaded. Combine with optimized delivery for best UX.

Want to ship fast, polished visuals with effortless optimization and delivery? Create a free Cloudinary account and start enhancing your fade-in workflows today.

Start Using Cloudinary

Sign up for our free plan and start creating stunning visual experiences in minutes.

Sign Up for Free