How ai-website-cloner-template Handles Scroll-Driven Animations: A Complete Implementation Guide

The template treats scroll-driven interactions as first-class "interaction models" that require detecting the trigger mechanism, capturing computed styles at both initial and scrolled states, then implementing the specific trigger—whether IntersectionObserver, CSS scroll-snap, position:sticky, animation-timeline, or a library like Lenis—in the cloned React components.

The ai-website-cloner-template by JCodesMore provides a systematic workflow for reproducing scroll-driven animations when cloning websites. Unlike static layouts, scroll-triggered effects require capturing both the trigger mechanism and the visual transition states to ensure behavioral fidelity. According to the inspection guide in docs/research/INSPECTION_GUIDE.md, the template encodes scroll-driven interactions as a core "interaction model" that developers must identify before writing any clone code.

Detecting the Scroll Interaction Model

Before implementing any animation, you must identify which mechanism drives the scroll behavior. The workflow documented in .opencode/commands/clone-website.md and .windsurf/workflows/clone-website.md requires manually scrolling through the target page and categorizing the interaction into one of five trigger types.

IntersectionObserver Patterns

Look for elements that appear, fade in, or transform when entering the viewport. These typically rely on the IntersectionObserver API rather than scroll event listeners. Check for libraries like GSAP ScrollTrigger or native observers that toggle visibility classes.

CSS Scroll-Snap and Sticky Positioning

Identify containers with scroll-snap-type properties or headers that become fixed after scrolling past a certain point. Scroll-snap creates a carousel-like experience where the viewport locks to specific sections, while position:sticky elements transition from relative to fixed positioning based on scroll position.

CSS Animation-Timeline

Modern browsers support scroll-driven animations using animation-timeline: scroll() in CSS. These animations progress based on scroll position without JavaScript.

JavaScript Scroll Libraries

Heavy smooth-scrolling effects often use Lenis or Locomotive Scroll. These libraries replace native scrolling with inertia-based smooth scrolling and require specific initialization in the React root.

Capturing Computed Styles at Scroll Positions 0 and Threshold

Once you identify the trigger, record the element's computed styles at two critical points. According to INSPECTION_GUIDE.md lines 103-108, use getComputedStyle to capture:

  1. Scroll position 0: The initial state before any scroll interaction
  2. Scroll past threshold: The final state after the trigger activates (e.g., 120px scroll)

Store these values in the component specification as a JSON block under states: {initial: …, scrolled: …}. This data feeds directly into the builder agent that generates the clone components.

Implementing Scroll Triggers in Next.js 18/React 19

The template targets Next.js 18/React 19 for implementations. Place your code in the appropriate file based on the trigger type:

Trigger type Implementation approach File location
IntersectionObserver Create a useEffect hook that observes the target element and toggles state (isVisible). Apply scrolled CSS via className or style. src/components/<YourComponent>.tsx
scroll-snap Add scroll-snap-type: y mandatory on the container and scroll-snap-align: start on children. No JavaScript required. src/app/globals.css
position:sticky Apply position: sticky; top: 0 and optionally change background/shadow when scrollY > threshold. Component CSS or globals.css
CSS animation-timeline Use @keyframes with animation-timeline: scroll() for modern browsers. globals.css
JS scroll library Initialize Lenis/Locomotive in a useEffect and add a CSS class (e.g., .lenis) to the root element. src/app/layout.tsx for initialization, globals.css for class styling

IntersectionObserver Hook Example

import { useEffect, useState } from "react";

export default function ScrollFade() {
  const [visible, setVisible] = useState(false);
  useEffect(() => {
    const target = document.querySelector("#fade-target");
    if (!target) return;
    const observer = new IntersectionObserver(
      ([entry]) => setVisible(entry.isIntersecting),
      { rootMargin: "0px", threshold: 0.1 }
    );
    observer.observe(target);
    return () => observer.disconnect();
  }, []);

  return (
    <div
      id="fade-target"
      className={visible ? "opacity-100" : "opacity-0"}
      style={{ transition: "opacity 0.5s ease" }}
    >
      {/* content that appears on scroll */}
    </div>
  );
}

Source: Pattern described in docs/research/INSPECTION_GUIDE.md lines 86-89.

Lenis Smooth Scroll Initialization

// src/app/layout.tsx
"use client";
import { useEffect } from "react";
import Lenis from "@studio-freight/lenis";

export default function RootLayout({ children }: { children: React.ReactNode }) {
  useEffect(() => {
    const lenis = new Lenis({
      smooth: true,
      lerp: 0.1,
    });
    function raf(time: number) {
      lenis.raf(time);
      requestAnimationFrame(raf);
    }
    requestAnimationFrame(raf);
    return () => lenis.destroy();
  }, []);

  return <html>{children}</html>;
}
/* src/app/globals.css */
html.lenis {
  scroll-behavior: auto; /* Lenis handles smoothing */
}

Source: Library recommendation in .opencode/commands/clone-website.md lines 78-83.

Scroll-Snap CSS Implementation

/* src/app/globals.css */
.scroll-snap-container {
  scroll-snap-type: y mandatory;
  overflow-y: scroll;
  height: 100vh;
}
.scroll-snap-item {
  scroll-snap-align: start;
  height: 100vh;
}
// src/components/ui/ScrollSnap.tsx
export default function ScrollSnap() {
  return (
    <div className="scroll-snap-container">
      <section className="scroll-snap-item bg-red-200">Page 1</section>
      <section className="scroll-snap-item bg-blue-200">Page 2</section>
    </div>
  );
}

Source: Scroll-snap guidance in .windsurf/workflows/clone-website.md lines 65-68.

Configuring Global Styles and Dependencies

Add all scroll-related keyframes, utility classes, and global behaviors to src/app/globals.css. If the original site uses Lenis or Locomotive Scroll, install the dependency via npm i lenis and update package.json. The README.md (lines 90-98) specifically reminds developers to note any libraries that require installation.

Verifying the Cloned Scroll Experience

Run the development server with npm run dev and manually scroll through the entire page. Verify that headers, sticky sidebars, scroll-snap sections, and parallax layers match the original both visually and behaviorally. Check that thresholds trigger at the same scroll positions and that smooth-scroll libraries maintain the same inertia and damping as the source.

Summary

  • Identify the interaction model first using docs/research/INSPECTION_GUIDE.md before writing implementation code
  • Record both pre- and post-trigger CSS values using getComputedStyle at scroll positions 0 and threshold
  • Implement the exact trigger mechanism—IntersectionObserver, scroll-snap, sticky, animation-timeline, or JS library—in the appropriate file (src/components/*.tsx, src/app/layout.tsx, or src/app/globals.css)
  • Install required dependencies like Lenis and add global CSS classes to src/app/globals.css
  • Test the scrolling experience end-to-end to ensure behavioral fidelity with the original site

Frequently Asked Questions

How does ai-website-cloner-template identify scroll-driven animations?

The template uses a systematic inspection process documented in docs/research/INSPECTION_GUIDE.md that requires manually scrolling the target page and categorizing the behavior into one of five trigger types: IntersectionObserver, scroll-snap, position:sticky, CSS animation-timeline, or JavaScript scroll libraries like Lenis. This classification determines where the implementation code belongs and which dependencies are required.

Where should I initialize smooth scroll libraries like Lenis?

Initialize Lenis and similar libraries in src/app/layout.tsx using a useEffect hook that creates a new instance and sets up a requestAnimationFrame loop. Apply the library's CSS class (e.g., .lenis) to the root html element in the same file, and add corresponding styles to src/app/globals.css.

What is the difference between handling scroll-snap and IntersectionObserver?

Scroll-snap is a pure CSS solution that requires adding scroll-snap-type to a container and scroll-snap-align to children in src/app/globals.css, with no JavaScript needed. IntersectionObserver requires a React useEffect hook in src/components/<YourComponent>.tsx that observes elements and toggles state to apply CSS classes when elements enter the viewport.

How do I store scroll states for the builder agent?

Capture getComputedStyle values at scroll position 0 and at the trigger threshold (e.g., 120px), then store them in the component specification as a JSON object with the structure states: {initial: {…}, scrolled: {…}}. This allows the builder agent to generate components with the correct initial and animated styles.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →