Jaconir

CSS Scroll Animations: How to Animate Elements as You Scroll (No JS Required)

March 11, 2026
8 min read

Scroll animations make pages feel alive — elements that fade in, slide up, or scale as they enter the viewport create a sense of polish that flat static pages lack. Until recently, this required JavaScript libraries like AOS or GSAP. Native CSS scroll animations are now supported in all major browsers, making it possible to trigger animations purely from CSS with zero JavaScript. This guide covers every approach from modern CSS-only to JS fallbacks, with a free visual generator to speed up your workflow.

Build your scroll animation CSS visually with Jaconir Scroll Animation Generator — configure the effect, timing, and easing, then copy the ready-to-use CSS.

The Two Modern CSS Scroll Animation Approaches

1. CSS Animation Timeline (Newest — Chrome 115+, Firefox 110+)

The animation-timeline property links an animation directly to scroll position. No JavaScript, no Intersection Observer — the animation plays as the user scrolls:

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

.scroll-element {
  animation: fadeInUp 1s ease forwards;
  animation-timeline: view();
  animation-range: entry 0% entry 40%;
}

animation-timeline: view() ties the animation to when the element enters the viewport. animation-range: entry 0% entry 40% means the animation plays as the element moves from 0% to 40% into the viewport.

2. Intersection Observer + CSS Classes (Universal Support)

For maximum browser compatibility, use Intersection Observer to add a class when an element enters the viewport, then animate via that class:

/* CSS */
.reveal {
  opacity: 0;
  transform: translateY(30px);
  transition: opacity 0.6s ease, transform 0.6s ease;
}

.reveal.visible {
  opacity: 1;
  transform: translateY(0);
}

/* JavaScript */
const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      entry.target.classList.add('visible');
    }
  });
}, { threshold: 0.15 });

document.querySelectorAll('.reveal').forEach(el => observer.observe(el));

This works in every browser including older versions and is the approach most production sites use today.

Common Scroll Animation Patterns

Fade In

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

.fade-in {
  animation: fadeIn 0.8s ease forwards;
  animation-timeline: view();
  animation-range: entry 0% entry 30%;
}

Slide Up

@keyframes slideUp {
  from { opacity: 0; transform: translateY(50px); }
  to   { opacity: 1; transform: translateY(0); }
}

.slide-up {
  animation: slideUp 0.7s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards;
  animation-timeline: view();
  animation-range: entry 0% entry 35%;
}

Slide In from Left

@keyframes slideInLeft {
  from { opacity: 0; transform: translateX(-60px); }
  to   { opacity: 1; transform: translateX(0); }
}

Scale In

@keyframes scaleIn {
  from { opacity: 0; transform: scale(0.85); }
  to   { opacity: 1; transform: scale(1); }
}

Staggered Children (cards, list items)

.card-grid .card {
  opacity: 0;
  transform: translateY(30px);
  transition: opacity 0.5s ease, transform 0.5s ease;
}

.card-grid.visible .card:nth-child(1) { transition-delay: 0s; }
.card-grid.visible .card:nth-child(2) { transition-delay: 0.1s; }
.card-grid.visible .card:nth-child(3) { transition-delay: 0.2s; }
.card-grid.visible .card:nth-child(4) { transition-delay: 0.3s; }

.card-grid.visible .card {
  opacity: 1;
  transform: translateY(0);
}

How to Use the Scroll Animation Generator

  1. Open Jaconir Scroll Animation Generator
  2. Choose your animation type (fade, slide, scale, rotate)
  3. Configure direction, duration, easing, and delay
  4. Preview the animation in the live preview panel
  5. Copy the generated CSS and paste it into your stylesheet
  6. Add the class to any HTML element you want animated

Animation Timing and Easing Reference

The easing function controls the feel of the animation:

  • ease — gentle start and end, natural feel (most common)
  • ease-out — fast start, slow end — best for elements entering the screen
  • ease-in-out — smooth acceleration and deceleration
  • cubic-bezier(0.25, 0.46, 0.45, 0.94) — easeOutQuad, very smooth, widely used
  • cubic-bezier(0.34, 1.56, 0.64, 1) — spring-like overshoot, playful UI

Duration guidelines:

  • 200–400ms: micro-interactions, hover states
  • 500–800ms: scroll reveals, panel transitions (most common)
  • 1000ms+: hero animations, full-section transitions

Performance: Only Animate These Properties

For smooth 60fps animations, only animate properties that the browser can composite on the GPU:

  • Safe: opacity, transform (translate, scale, rotate)
  • Avoid: width, height, margin, padding, top, left — these trigger layout recalculation and cause jank

To move an element, use transform: translateX() not left:. To resize, use transform: scale() not width/height.

Accessibility: Respect Reduced Motion

@media (prefers-reduced-motion: reduce) {
  .reveal,
  [class*="animate-"] {
    animation: none !important;
    transition: none !important;
    opacity: 1 !important;
    transform: none !important;
  }
}

Always include this. Users who experience motion sickness or have vestibular disorders set this preference. Animations that ignore it are an accessibility failure.

FAQ

Do I need a JavaScript library for scroll animations?

No. Modern CSS with animation-timeline handles scroll-triggered animations natively in Chrome and Firefox. For full browser support, the Intersection Observer approach requires about 10 lines of JavaScript — no library needed.

What's the difference between CSS transitions and CSS animations?

Transitions animate between two states (from/to), triggered by a state change like a class being added. Animations use @keyframes and can have multiple steps, loop, and run automatically. Scroll reveals typically use transitions triggered by a class, or @keyframes with animation-timeline.

Can I animate elements that scroll back out of view?

With animation-timeline: view(), the animation reverses when the element leaves the viewport. With the Intersection Observer approach, remove the class when the element leaves by setting entry.target.classList.remove('visible') in the observer callback when entry.isIntersecting is false.

Conclusion

Scroll animations are one of the highest-impact visual improvements you can add to a web page. Native CSS support means they're now lightweight, performant, and require minimal code. Generate your animation CSS visually, then copy and paste.

Build your scroll animation: Jaconir Scroll Animation Generator →