HTML/CSS - 23

HTML/CSS - 23: Scroll Animations & Effects

Scrolling can do more than move a page up and down. With CSS and a small amount of JavaScript, elements can react when they enter the visible part of the page.

In this lesson, you will create a simple reveal effect. Each section will begin slightly faded and lower on the page, then smoothly appear when the visitor scrolls to it.

The CSS controls how the animation looks, while JavaScript uses IntersectionObserver to detect when each section becomes visible.

Your challenge: Make three sections fade and slide into view as you scroll through the Live Output.

✦ HTML Editor
▶ Live Output

How does the reveal effect work?

Each section begins with opacity: 0;, which makes it invisible. It also starts with transform: translateY(50px);, which moves it slightly downward.

When the section receives the class visible, those values change to: opacity: 1; and transform: translateY(0);.

Because the section has a transition, the browser smoothly moves between the two states.

What does IntersectionObserver do?

The JavaScript first finds every section with: document.querySelectorAll('.reveal').

The IntersectionObserver then watches those sections. When one enters the visible area, entry.isIntersecting becomes true.

This line: entry.target.classList.add('visible'); adds the class that starts the CSS transition.

Experiment with the movement:

Change: transform: translateY(50px); to: transform: translateY(100px);

The sections will travel farther before reaching their normal position.

You can also try: transform: translateX(-50px); to make them slide in from the left instead.

Experiment with speed:

Change 0.8s to 0.3s for a quicker reveal, or try 1.5s for a slower effect.

Reveal only once:

After adding the visible class, you can optionally add: observer.unobserve(entry.target);

That tells the observer it no longer needs to watch an element after it has appeared.

Accessibility and motion:

Animation should support the page rather than distract from it. Some visitors also choose a device setting that asks websites to reduce movement.

A useful CSS media query for this is: @media (prefers-reduced-motion: reduce).

Inside it, you can remove the movement and transitions so the content appears normally.

Good animation habit: The content should still make sense if the animation does not run. Treat scroll effects as an enhancement rather than something the page depends on.

👀 Show Solution
<style>

  .reveal {
    opacity: 0;
    transform: translateY(50px);

    transition:
      opacity 0.8s ease,
      transform 0.8s ease;
  }

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

  @media (prefers-reduced-motion: reduce) {

    .reveal {
      opacity: 1;
      transform: none;
      transition: none;
    }

  }

</style>

<section class="reveal">
  <h2>Hidden Observatory</h2>
</section>

<section class="reveal">
  <h2>Quiet Signal</h2>
</section>

<section class="reveal">
  <h2>Last Marker</h2>
</section>

<script>

  const sections =
    document.querySelectorAll('.reveal');

  const observer =
    new IntersectionObserver((entries) => {

      entries.forEach((entry) => {

        if (entry.isIntersecting) {

          entry.target.classList.add('visible');

          observer.unobserve(entry.target);

        }

      });

    });

  sections.forEach((section) => {

    observer.observe(section);

  });

</script>

The sections begin hidden and slightly below their final positions. When the browser detects that one has entered the visible area, JavaScript adds the visible class and CSS smoothly reveals it.