Trigger CSS Animations on Scroll

Build and debug CSS animations that play as sections enter the viewport.

Introduction

30 Second Summary

When page sections move at the wrong moment, a polished layout can feel jumpy. The timing is hard to fix when you cannot see what starts the motion.

In this project, you will build a long landing page whose sections animate as they enter the viewport using CSS scroll-triggered animations. The Scroll-Triggered Animations Debugger reveals the ranges behind the motion so you can diagnose one deliberately mistimed section.

What You'll Build

You scroll through a landing page where each section enters at the intended point while a visual minimap exposes the trigger range behind every animation.

By the end of this project, you'll have:

  • A three-section landing page that reveals each section when its view timeline reaches the chosen range.
  • A visual trigger minimap inside Chrome DevTools that shows where every animation activates.
  • A repeatable debugging workflow for spotting a bad trigger range and testing the correction live.
  • Secret Mission: An optional challenge to push your skills further.

Are there any prerequisites?

You need basic HTML and CSS experience. You also need Chrome to use the debugger extension.

Before We Start

CSS scroll-triggered animations depend on browser support. Google Chrome renders the page. Chrome DevTools exposes what the browser sees.

In this step, you will prepare those browser tools. You will also open an empty project folder in Visual Studio Code.

In this step, get ready to:
  • Confirm Google Chrome opens with DevTools available.
  • Prepare Visual Studio Code for the project.
  • Create an empty landing page folder on your Desktop.
Confirm Chrome and DevTools are ready

DevTools is built into Chrome. The debugger extension you install later appears inside this browser tooling.

  • Press Cmd+Space on macOS or the Windows key on Windows to open system search.
  • Type Google Chrome and press Enter.

✔️ Chrome opens

Chrome is already available on your machine. Confirm that its built-in developer tools open correctly.

  • Open DevTools by pressing Cmd+Option+I on macOS or Ctrl+Shift+I on Windows.

You will see the DevTools interface beside the browser page. Your browser workspace is ready for the debugger panel you add later.

ⓧ Chrome is not installed

Chrome includes the DevTools support this project needs. Use the instructions for your operating system to install it.

macOS

  • Visit the official Google Chrome download page.
  • Click Download Chrome.
  • Open the downloaded installer.
  • Drag Chrome to the Applications folder.

Chrome is now installed on your Mac.

  • Press Cmd+Space to open Spotlight.
  • Type Google Chrome and press Enter.
  • Open DevTools by pressing Cmd+Option+I.

You will see DevTools open beside the browser page.

Windows

  • Visit the official Google Chrome download page.
  • Click Download Chrome.
  • Choose Run when the download prompt offers it.
  • Choose Save if the download prompt does not offer Run.
  • Open the downloaded installer if you chose Save.
  • Follow the installation prompts.

Chrome opens after the installation finishes.

  • Open DevTools by pressing Ctrl+Shift+I.

You will see DevTools open beside the browser page.

DevTools not opening?

  • Click inside the Chrome page before trying the keyboard shortcut again.
  • Confirm that Chrome is the active window when you press the shortcut.

Need help opening the browser tools?

Open Visual Studio Code

A code editor keeps the landing page files together. It also gives you one place to compare each CSS change.

  • Press Cmd+Space on macOS or the Windows key on Windows to open system search.
  • Type Visual Studio Code and press Enter.

✔️ Visual Studio Code opens

Visual Studio Code is already installed. Your editor is ready for the project folder.

ⓧ Visual Studio Code is not installed

Visual Studio Code is free to download. Use the instructions for your operating system to install it.

macOS

  • Visit the official Visual Studio Code download page.
  • Choose Mac Universal.
  • Open the downloaded .dmg file.
  • Drag Visual Studio Code.app to the Applications folder.

Visual Studio Code is now installed on your Mac.

  • Press Cmd+Space to open Spotlight.
  • Type Visual Studio Code and press Enter.

You will see the Visual Studio Code window open.

Windows

Visual Studio Code is now installed on your Windows computer.

  • Press the Windows key to open system search.
  • Type Visual Studio Code and press Enter.

You will see the Visual Studio Code window open.

Create and open the project folder

Opening a folder turns it into a VS Code workspace. This empty workspace becomes the home for the landing page you build next.

  • Click File in the Visual Studio Code menu bar.
  • Click Open Folder....

You will see your operating system's folder chooser.

  • Select Desktop in the folder chooser.
  • Click New Folder.
  • Enter scroll-triggered-landing-page as the folder name.
  • Press Enter to create the folder.

The new empty folder now appears on your Desktop.

  • Select the scroll-triggered-landing-page folder.
  • Click Open on macOS or Select Folder on Windows.

This folder is empty. You created it yourself, so trusting it enables Visual Studio Code's normal project features safely.

  • Click Yes, I trust the authors if the Workspace Trust dialog appears.

Before you inspect the sidebar, what should you see inside a project folder with no files yet?

  • Check the Explorer sidebar on the left.

You will see scroll-triggered-landing-page with no files beneath it. That empty view confirms the project starts clean.

Folder not showing in Explorer?

  • Confirm that you selected the outer scroll-triggered-landing-page folder.
  • Click View in the menu bar if the sidebar is hidden.
  • Click Explorer to restore the sidebar.

Need help finding the empty project folder?

Your browser tools are ready. Next up, you will build the long landing page that gives your scroll triggers room to work.

Build the Landing Page

Scroll-triggered effects need enough vertical distance for sections to cross the viewport. A static baseline lets you isolate layout behavior before motion exists.

In this step, you will create the HTML structure plus the CSS layout. The finished page scrolls through a tall header followed by three content sections.

In this step, get ready to:
  • Create the page structure with a tall header plus three named sections.
  • Add styling that gives every major region a full viewport of space.
  • Verify that the local page scrolls normally with no motion.
Define the page landmarks

Stable page landmarks give later trigger ranges a clear subject to target. Each content section gets a unique ID so the browser can address it directly.

  • Create index.html inside your empty project folder by adding the code below:
<!DOCTYPE html>
<html lang="en">
<head>
  <!-- Load page metadata and the shared stylesheet. -->
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Scroll Trigger Landing Page</title>
  <link rel="stylesheet" href="styles.css">
</head>
<body>
  <!-- Fill the first viewport with a clear landing-page introduction. -->
  <header class="hero">
    <div class="panel">
      <p>Scroll-triggered animation study</p>
      <h1>See every section arrive</h1>
      <p>Scroll through a simple page built for testing entrance animations.</p>
    </div>
  </header>
  <main>
    <!-- Give every scroll destination a stable ID. -->
    <section id="about" class="content-section"><div class="panel"><h2>About</h2><p>Learn how each trigger responds as its section enters the viewport.</p></div></section>

    <section id="features" class="content-section"><div class="panel"><h2>Features</h2><p>Compare trigger ranges across several full-height sections.</p></div></section>

    <section id="contact" class="content-section"><div class="panel"><h2>Contact</h2><p>Finish the page with one more clear scroll destination.</p></div></section>
  </main>
</body>
</html>

What does this code do?

  • The hero header introduces the page before the scrollable content begins.
  • The about, features, plus contact IDs give every content section a distinct destination.
  • The stylesheet link tells the browser to load the visual rules from styles.css.
  • Save index.html.
  • Load index.html from your project folder with the local-file option in the browser from earlier.

You will see the hero text followed by the About, Features, plus Contact sections. The page uses the browser's default appearance because the stylesheet does not exist yet.

Don't see the page content?

  • Confirm that the file is named index.html with no extra file extension.
  • Check that the hero plus all three sections sit between the opening <body> tag and the closing </body> tag.

Ask for help with the page structure.

✔️ Awesome, I've got everything!

Your HTML now matches the page structure for this step.

ⓧ I'd like to double check the full code

<!DOCTYPE html>
<html lang="en">
<head>
  <!-- Load page metadata and the shared stylesheet. -->
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Scroll Trigger Landing Page</title>
  <link rel="stylesheet" href="styles.css">
</head>
<body>
  <!-- Fill the first viewport with a clear landing-page introduction. -->
  <header class="hero">
    <div class="panel">
      <p>Scroll-triggered animation study</p>
      <h1>See every section arrive</h1>
      <p>Scroll through a simple page built for testing entrance animations.</p>
    </div>
  </header>
  <main>
    <!-- Give every scroll destination a stable ID. -->
    <section id="about" class="content-section"><div class="panel"><h2>About</h2><p>Learn how each trigger responds as its section enters the viewport.</p></div></section>

    <section id="features" class="content-section"><div class="panel"><h2>Features</h2><p>Compare trigger ranges across several full-height sections.</p></div></section>

    <section id="contact" class="content-section"><div class="panel"><h2>Contact</h2><p>Finish the page with one more clear scroll destination.</p></div></section>
  </main>
</body>
</html>

This reference includes the stylesheet link, the tall-page landmarks, plus all three required section IDs.

Build the scrolling canvas

The stylesheet creates enough vertical space for every section to enter and leave the viewport. Clear visual boundaries make each scroll destination easy to recognize.

  • Create styles.css beside index.html by adding these foundation styles:
/* Keep element sizes predictable across the page. */
* {
  box-sizing: border-box;
}

/* Set the page's shared colors and type. */
body {
  margin: 0;
  font-family: system-ui, sans-serif;
  color: #172033;
  background: #eef2ff;
}

What do these styles control?

  • The universal selector keeps padding plus borders inside each element's calculated size.
  • The body rules remove the browser's default margin.
  • The shared colors plus system font establish the page's visual foundation.
  • Save styles.css.
  • Refresh the local page in your browser.

You will see a pale background, dark text, plus updated typography across the page.

Still seeing the default browser styles?

  • Confirm that styles.css sits in the same folder as index.html.
  • Check that the stylesheet link uses href="styles.css".

Ask for help connecting the stylesheet.

The page now has a visual foundation. The next rules create the vertical distance that later triggers need.

  • Add the full-height region styles below the existing rules in styles.css by pasting this code:
/* Give each major region a full viewport of vertical space. */
.hero,
.content-section {
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 4rem 1.5rem;
}

/* Separate the scroll destinations with gentle contrast. */
.hero {
  background: linear-gradient(135deg, #dbeafe, #ede9fe);
}

.content-section {
  border-top: 1px solid #cbd5e1;
}

.content-section:nth-child(even) {
  background: #ffffff;
}

How does this create scrolling?

  • The min-height rule makes the header plus every content section at least as tall as the viewport.
  • The grid rules center each section's content without changing the document's normal scroll behavior.
  • The border plus alternating background make section boundaries visible during a slow scroll.
  • Save styles.css.
  • Refresh the local page in your browser.

You will see a tall gradient header followed by three full-height content sections. The browser now has several screens of vertical content to scroll through.

Does the page still feel short?

  • Confirm that both .hero plus .content-section use min-height: 100vh.
  • Check that every section in index.html has class="content-section".

Ask for help with the full-height layout.

The scroll distance is ready. One final styling pass keeps the content readable while you test each section.

  • Add the content-card styles below the existing rules in styles.css by pasting this code:
/* Keep each section's message in a readable card. */
.panel {
  width: min(42rem, 100%);
  padding: 3rem;
  border: 1px solid #cbd5e1;
  border-radius: 1.5rem;
  background: rgba(255, 255, 255, 0.8);
  box-shadow: 0 1.5rem 4rem rgba(15, 23, 42, 0.12);
}

/* Scale headings while keeping paragraphs easy to scan. */
h1,
h2 {
  margin-top: 0;
  line-height: 1.1;
}

h1 {
  font-size: clamp(3rem, 8vw, 6rem);
}

h2 {
  font-size: clamp(2rem, 5vw, 4rem);
}

p {
  max-width: 60ch;
  line-height: 1.7;
}

What completes the layout?

  • The .panel selector creates a readable card around each section's message.
  • The heading sizes adapt to the available screen width.
  • The paragraph width keeps long lines from stretching across the full card.
  • Save styles.css.
  • Refresh the local page in your browser.

You will see a centered translucent card in the header plus every content section. Each heading remains easy to identify as you scroll.

Don't see the content cards?

  • Confirm that every header or section contains an element with class="panel".
  • Check that the selector begins with .panel in styles.css.

Ask for help matching the card class.

✔️ Awesome, I've got everything!

Your stylesheet now contains the complete static layout for this step.

ⓧ I'd like to double check the full code

/* Keep element sizes predictable across the page. */
* {
  box-sizing: border-box;
}

/* Set the page's shared colors and type. */
body {
  margin: 0;
  font-family: system-ui, sans-serif;
  color: #172033;
  background: #eef2ff;
}

/* Give each major region a full viewport of vertical space. */
.hero,
.content-section {
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 4rem 1.5rem;
}

/* Separate the scroll destinations with gentle contrast. */
.hero {
  background: linear-gradient(135deg, #dbeafe, #ede9fe);
}

.content-section {
  border-top: 1px solid #cbd5e1;
}

.content-section:nth-child(even) {
  background: #ffffff;
}

/* Keep each section's message in a readable card. */
.panel {
  width: min(42rem, 100%);
  padding: 3rem;
  border: 1px solid #cbd5e1;
  border-radius: 1.5rem;
  background: rgba(255, 255, 255, 0.8);
  box-shadow: 0 1.5rem 4rem rgba(15, 23, 42, 0.12);
}

/* Scale headings while keeping paragraphs easy to scan. */
h1,
h2 {
  margin-top: 0;
  line-height: 1.1;
}

h1 {
  font-size: clamp(3rem, 8vw, 6rem);
}

h2 {
  font-size: clamp(2rem, 5vw, 4rem);
}

p {
  max-width: 60ch;
  line-height: 1.7;
}

This reference combines the page foundation, the full-height regions, plus the content-card styling.

Verify the no-motion baseline

URL fragments provide a quick check that all three IDs work. A final slow scroll confirms that the page still uses normal document movement.

  • Load the current local address with #about appended.

You will land directly on the About section.

  • Load the same local file with #features appended.

You will land directly on the Features section.

  • Load the same local file with #contact appended.

You will land directly on the Contact section. These jumps confirm that all three section IDs are available to the browser.

Before the final scroll, do you expect any section to fade or slide as it enters the viewport?

  • Load the same local file without a URL fragment.
  • Scroll slowly from the header to the Contact section.

You will see each section appear immediately through normal scrolling. No section fades, slides, or plays motion.

That baseline is now solid. Your three destinations load correctly without any animation behavior.

Your landing page now gives each trigger a clear section plus plenty of scroll distance. Next up, you will add the first scroll-triggered entrance animation.

Animate the First Section

Your static landing page already gives each section room to enter the viewport as you scroll. The #about section now needs the project's first visible entrance.

A view timeline uses that section's viewport position to drive animation progress. This step connects only #about so the other sections remain static.

In this step, get ready to:
  • Define the slide-in entrance motion in styles.css.
  • Link #about to its own view() timeline.
  • Test the entrance range in your browser.
Define the entrance motion

A CSS keyframes animation defines motion between named states. This entrance begins below the section's resting position with zero opacity.

The final state restores full opacity at the section's normal position.

  • Switch back to styles.css in your code editor.
  • Extend styles.css with the entrance states by copying this code after the existing base styles:
@keyframes slide-in {
  /* Start lower with no visible opacity. */
  from {
    opacity: 0;
    transform: translateY(3rem);
  }

  /* Finish at the section's normal position. */
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

What Does This Code Do?

  • The from state gives the section zero opacity. Its positive Y translation places it below its resting position.
  • The to state restores full opacity. A zero translation returns the section to its layout position.
  • The name slide-in makes this motion available to a CSS selector.
  • Save styles.css.
  • Return to DevTools in your browser.
  • Refresh the local page.
  • Select styles.css from the loaded file list in DevTools.

You should see @keyframes slide-in near the end of the browser-loaded stylesheet. This proves the browser has loaded your new animation definition.

Can't Find the Keyframes?

  • Confirm that styles.css is saved in your code editor.
  • Refresh the local page again.
  • Check that the page still links to the same styles.css file you edited.

Ask for help finding why the browser cannot load the new rule.

Connect the timeline to #about

The view() function creates a timeline from an element's journey through the viewport. The animation range chooses the part of that journey that controls the entrance.

  • In styles.css, place this rule immediately after the slide-in keyframes:
#about {
  /* Drive the first section from its own viewport progress. */
  animation-name: slide-in;
  animation-timeline: view();
  animation-range: entry 0% cover 40%;
}

How Does the Range Work?

  • The animation-name property applies the slide-in keyframes to #about.
  • The animation-timeline property gives #about its own viewport-based progress.
  • The range begins at entry 0%. The animation reaches its final state at cover 40%.
  • Save styles.css.

Before you test it, where do you expect #about to be when the motion begins?

  • Return to the local page in your browser.
  • Refresh the page.
  • Scroll downward until #about begins entering the viewport.

You should see #about fade into view while moving upward to its resting position. It should reach that position by the cover 40% point.

  • Continue scrolling through the remaining sections.

The #features section should remain static. The #contact section should also remain static.

Don't See the Entrance Motion?

  • Return to the top of the page before testing the entrance again.
  • Refresh the page before scrolling.
  • Check that the selector is #about because the matching section uses id="about".

Ask for help checking the timeline properties.

✔️ Awesome, I've got everything!

Your saved stylesheet now links only #about to the slide-in entrance.

ⓧ I'd like to double check the full code

  • Compare the animation code at the end of your existing styles.css with this reference:
@keyframes slide-in {
  /* Start lower with no visible opacity. */
  from {
    opacity: 0;
    transform: translateY(3rem);
  }

  /* Finish at the section's normal position. */
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

#about {
  /* Drive the first section from its own viewport progress. */
  animation-name: slide-in;
  animation-timeline: view();
  animation-range: entry 0% cover 40%;
}

You now have your first scroll-linked entrance working. Next, you'll apply the pattern to the remaining sections with another range to test.

Add the Remaining Triggers

Your about section now fades into place as it enters the viewport. Next, the same entrance behavior needs to cover the rest of the page.

Each section gets its own view timeline. Small range changes can shift when the motion finishes.

In this step, get ready to:
  • Give the features section its own entrance animation.
  • Give the contact section the intended trigger range.
  • Compare the timing across all three sections.
Add a timeline to the features section

The features section needs its own relationship with the viewport. Its range defines the portion of that relationship used by slide-in.

  • Switch back to styles.css in your code editor.
  • Place the following rule below the existing #about rule:
#features {
  animation-name: slide-in;
  animation-timeline: view();
  animation-range: entry 0% entry 100%;
}

What does this code do?

  • The animation-name property reuses the existing slide-in animation.
  • The animation-timeline property lets the section's viewport position drive its progress.
  • The animation-range property supplies the two boundaries that control the animation.
  • Save styles.css.
  • Return to the local page in your browser.
  • Scroll to the top of the page.
  • Refresh the page.

Before you scroll, do you expect the features entrance to feel the same as the about entrance?

  • Scroll down until the features section enters the viewport.

You'll see the features section finish its entrance while it is still entering the viewport. Its movement finishes earlier than the about movement.

Good catch. You now have a reproducible timing problem for the debugger to reveal.

Does the features section stay still?

  • Confirm that the selector starts with #features.
  • Check that every animation property sits inside the matching braces.
  • Reload the page from its top position before testing the entrance again.

If the animation still does not run, help me check the features animation rule.

Add the intended contact trigger

The contact section gives you another example of the intended timing. Its animation starts during entry before continuing into the cover phase.

  • Return to styles.css in your code editor.
  • Place the following rule below the #features rule:
#contact {
  animation-name: slide-in;
  animation-timeline: view();
  animation-range: entry 0% cover 40%;
}

Why use this range?

  • The contact section reuses the same slide-in animation.
  • Its view() timeline tracks this section independently.
  • The entry 0% cover 40% range spreads the animation across the intended entry-to-cover interval.
  • Save styles.css.
  • Return to the local page in your browser.
  • Scroll to the top of the page.
  • Refresh the page.

Before you scroll, which two sections do you expect to share the same entrance timing?

  • Scroll through the about section.
  • Continue through the features section.
  • Continue until the contact section finishes its entrance.

You'll see about use the intended entry-to-cover timing. You'll see contact match that timing.

The features animation completes during the entry phase. That deliberate outlier is ready for visual debugging.

Does the contact timing look different?

  • Check that the selector starts with #contact.
  • Confirm that its range reads entry 0% cover 40%.
  • Reload the page from its top position before comparing the sections again.

If the timing still does not match, help me compare the three animation ranges.

✔️ Awesome, I've got everything!

Great. Double-check that styles.css is saved before you continue.

ⓧ I'd like to double check the full code

Compare the complete animation configuration for all three sections:

#about {
  animation-name: slide-in;
  animation-timeline: view();
  animation-range: entry 0% cover 40%;
}

#features {
  animation-name: slide-in;
  animation-timeline: view();
  animation-range: entry 0% entry 100%;
}

#contact {
  animation-name: slide-in;
  animation-timeline: view();
  animation-range: entry 0% cover 40%;
}

All three sections use individual view() timelines. Only features uses the shorter entry-only range.

Your page now contains two matching triggers plus one deliberate outlier. Next, you'll open the debugger and inspect those ranges visually.

Inspect Trigger Ranges

Your three sections now use a view timeline for their entrance animation. The Features section finishes its movement much earlier than the other two sections.

Raw range values make that timing difference difficult to picture. The Scroll-Triggered Animations Debugger adds a visual minimap to Chrome DevTools so you can inspect the range behind each animation.

In this step, get ready to:
  • Install the Scroll-Triggered Animations Debugger extension.
  • Open the extension pane for your local landing page.
  • Compare the ranges for all three section animations.
Install the debugger extension

A DevTools extension can add debugging controls directly to the browser's existing developer tools. This extension visualizes each animated element alongside its activation and active ranges.

The browser asks you to confirm the installation before it adds the extension.

The debugger is now installed. Its animation pane becomes available inside DevTools.

Extension pane missing?

  • Confirm that you installed the extension in the same browser that displays your local landing page.
  • Close DevTools if it was open during the installation.
  • Reopen DevTools so it can load the new extension pane.

Need help loading the debugger pane?

Open the animation pane

The debugger follows the element selected in the Elements panel. Selecting a section connects its animation to the minimap.

  • Return to the local landing page from earlier.
  • Right-click inside the first content section.
  • Select Inspect from the browser menu.
  • Select the Elements panel if DevTools opens on another panel.
  • Select the #about element in the document tree.
  • Open the Scroll-Triggered Animations pane inside the Elements panel.

You should see a minimap representing the scroller plus the selected element. The pane also displays indicators for the activation and active ranges.

  • Scroll the landing page until the About section crosses the viewport.

The minimap moves with the page. Its status indicator updates as the selected section crosses its configured range.

Minimap not updating?

  • Confirm that #about remains selected in the Elements panel.
  • Refresh the local landing page if the pane does not display an animation.
  • Reopen DevTools if the extension pane remains unresponsive.

Why is the animation minimap not updating?

Compare the three trigger ranges

A shared minimap makes small timing differences visible. Each section can now be checked against the same viewport model.

Before you compare the readouts, which section do you expect to have a different range?

  • Select the #about element in the Elements panel.

The debugger shows entry 0% cover 40% for the About section.

  • Select the #features element in the Elements panel.

The Features readout changes to entry 0% entry 100%. Its minimap range ends while the section is still completing the entry phase.

  • Select the #contact element in the Elements panel.

The Contact readout returns to entry 0% cover 40%. The matching About and Contact ranges confirm that Features is the outlier.

What does the minimap reveal?

The range entry 0% entry 100% completes while the Features section moves through the entry phase. That compressed range explains its early entrance.

The range entry 0% cover 40% continues farther through the viewport. This gives the About and Contact sections more scroll distance for the same animation.

You found the mismatch using the animation's visual boundaries. The debugger now shows why Features behaves differently instead of leaving you to guess from the page alone.

The faulty range is now visible in the debugger. Next, you will test a corrected value and carry the working result back into your CSS.

Fix the Bad Trigger Range

The Scroll-Triggered Animations Debugger minimap has exposed the mismatch behind the early animation. The features range ends while the section is still entering the viewport.

You will test a better range in the live editor. You will then save that correction in your CSS so it survives a refresh.

In this step, get ready to:
  • Compare the bad range with the ranges used by the other sections.
  • Test the corrected range in the debugger.
  • Save the corrected range in styles.css.
Test the corrected range

A live range edit lets you test new boundaries before changing the stylesheet. This keeps the feedback loop focused on the animation timing.

  • Return to the debugger pane from earlier.
  • Locate the minimap entry for features.
  • Compare its current range with the matching range shown for the other two sections.
  • Replace entry 0% entry 100% with entry 0% cover 40% in the live range editor.

Before you test the new range, do you think the features animation will finish during entry or continue further through the viewport?

  • Scroll upward until the features section sits below the viewport.
  • Scroll downward until the features section enters the viewport.

You will see the features section animate through the same entry-to-cover range as the other sections.

Why Does This Range Work?

The entry 0% boundary starts the animation when the section begins entering. The cover 40% boundary gives the animation a later endpoint within the cover range.

The wider range gives slide-in enough scroll distance to complete at the intended moment.

Does Features Still Finish Early?

  • Confirm that the live value ends with cover 40%.
  • Move the features section fully below the viewport before repeating the test.

Ask for help with the live range test: Why does my features animation still finish early after changing its live range?

Save the corrected range

The debugger edit only tests the value against the current page. Saving the same value in styles.css makes the correction permanent.

  • Switch back to the code editor from earlier.
  • Find the #features rule in styles.css.
  • Find this declaration:
animation-range: entry 0% entry 100%;

Why Does This Fire Early?

This declaration ends at entry 100%. That endpoint compresses the animation into the section's entry phase.

  • Replace the declaration with this corrected version:
animation-range: entry 0% cover 40%;

What Does This Change?

The updated declaration gives #features the same range as #about and #contact.

All three sections now run slide-in through a consistent entry-to-cover window.

  • Save styles.css.
  • Return to the local landing page from earlier.
  • Refresh the browser.
  • Return to the debugger pane from earlier.

The minimap now shows entry 0% cover 40% for the features section after the refresh.

Did the Old Range Return?

  • Confirm that you edited the declaration inside the #features rule.
  • Confirm that styles.css is saved in the code editor.
  • Refresh the local landing page again.

Get help with the saved declaration: Why does the debugger still show the old features animation range after I edited styles.css?

Verify every section

A final scroll test confirms that the saved stylesheet controls the page. The minimap provides a second check by showing the range attached to each section.

  • Scroll to the top of the landing page.

Before you scroll down, do you expect any section to finish its animation earlier than the others?

  • Scroll through all three sections from top to bottom.
  • Compare their ranges in the debugger minimap.

You will see about, features, and contact use the slide-in animation through the intended entry-to-cover range.

That is the range bug fixed. Your three sections now animate with consistent timing backed by matching debugger ranges.

Secret mission

Audit a Trigger Range

Run a repeatable audit across all three scroll-triggered sections. The challenge is to spot a temporary contact timing mismatch in the minimap.

Clean Up Your Resources

Clean Up Your Resources

Your landing page runs locally. The browser extension creates no cloud resources or ongoing costs.

Decide whether to keep your resources available, pause your browser workspace, or delete the project entirely.

Resources you used:

  • The local index.html landing-page file.
  • The local styles.css stylesheet containing the corrected #features range.
  • The Scroll-Triggered Animations Debugger extension installed in your browser.

Keep everything running

No action needed. Choose this if you want to keep experimenting with scroll-triggered animations.

  • Keep index.html and styles.css in their current location.
  • Leave the Scroll-Triggered Animations Debugger installed so its DevTools panel remains available.
  • Use the landing page as a reference whenever you need to compare trigger ranges.

Pause - I'll come back to this later

Pausing clears your browser workspace. Your files and extension stay available for later.

  • Close the browser tab displaying your local landing page.
  • Close DevTools if it is still open.
  • Leave index.html in its current location.
  • Leave styles.css in its current location.
  • Leave the Scroll-Triggered Animations Debugger installed.
  • Resume later by opening index.html in the same browser.
  • Return to the debugger panel in DevTools to inspect the animation ranges again.

Delete - I don't want to use this again

This option permanently removes the local landing page. It also uninstalls the debugger extension from your browser.

  • Return to the code editor from earlier.
  • Find index.html in the file sidebar.
  • Delete index.html using its context menu.
  • Find styles.css in the file sidebar.
  • Delete styles.css using its context menu.
  • Return to the local browser tab from earlier.
  • Refresh the page to confirm the landing-page files are gone.

You'll see that the landing page no longer loads from its local file.

The debugger stays installed after you delete the page files.

Your browser may ask for confirmation when you remove the extension.

  • Open your browser's extensions menu from its toolbar.
  • Open the extension management view.
  • Find the Scroll-Triggered Animations Debugger in the installed extensions list.
  • Use the extension's removal control.
  • Approve the confirmation prompt if your browser displays one.
  • Check the installed extensions list to confirm the debugger is gone.

You should no longer see the Scroll-Triggered Animations Debugger in the installed extensions list.

Nice Work!

Nice Work!

You did it! Your landing page now uses CSS entrance animations that fire at the intended points during scrolling. You also diagnosed the faulty trigger range with the Scroll-Triggered Animations Debugger.

You've learned how to:

  • Build a scrollable landing page with three content sections that provide distinct animation checkpoints.
  • Configure view() timelines that play slide-in entrance animations as each section crosses its intended trigger range.
  • Use a visual trigger minimap to identify the section that fires too early. Copy the corrected range into styles.css so the saved page matches the visual test.
  • Secret Mission: Apply the same debugging workflow in an optional challenge that pushes your scroll-trigger skills further.

Ready to quiz yourself?