
Ui Cloner
- 2 installs
- 2 repo stars
- Updated March 20, 2026
- drshailesh88/ui-cloner-skill
Clone a website's design and animations in five Chrome-driven phases, extracting CSS keyframes and GSAP configs, then rebuild adapted to your brand.
About
Clones a reference site by extracting its DOM, styles, and scroll/GSAP animations via Chrome, then rebuilds it adapted to the user's brand. A developer uses it to replicate a site's design and motion faithfully.
- Animation complexity tiers drive library-matched builds
- Five-phase flow from Site DNA extraction to visual iteration
Ui Cloner by the numbers
- 2 all-time installs (skills.sh)
- Ranked #1,858 of 2,244 Frontend Development skills by installs in the Skillselion catalog
- Data as of Jul 29, 2026 (Skillselion catalog sync)
npx skills add https://github.com/drshailesh88/ui-cloner-skill --skill ui-clonerAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 2 |
|---|---|
| repo stars | ★ 2 |
| Last updated | March 20, 2026 |
| Repository | drshailesh88/ui-cloner-skill ↗ |
What it does
Clone a website's design and animations in five Chrome-driven phases, extracting CSS keyframes and GSAP configs, then rebuild adapted to your brand.
Files
UI Cloner — Website Clone & Adapt System (v2)
Clone award-winning websites and adapt them to your brand in 5 phases. v2 adds deep animation extraction and library-matched builds for faithful reproduction of complex scroll animations, GSAP timelines, parallax effects, and micro-interactions.
Prerequisites
- Chrome access is required. The user must run
claude --chromeor have browser control enabled. - This skill creates all working files in a
./ui-clone-workspace/directory.
Quick Start
When the user provides a URL (or says "clone [URL]"), immediately begin Phase 1. If they haven't provided a URL yet, ask for one.
Command format the user will typically use:
Clone this site: https://example.comAnimation Complexity Tiers
During Phase 1, classify the reference site into one of these tiers. This determines the build approach for Phase 4.
| Tier | Name | What It Means | Build Approach |
|---|---|---|---|
| 1 | Simple | CSS transitions, basic fade-in on scroll, hover color/scale changes | Vanilla JS IntersectionObserver + CSS transitions |
| 2 | Intermediate | GSAP core tweens, staggered animations, Lenis smooth scroll, text splitting, parallax backgrounds | GSAP + Lenis CDNs, GSAP tweens with ScrollTrigger basic |
| 3 | Complex | GSAP ScrollTrigger pinning, scrub animations, horizontal scroll, Lottie, SVG path drawing, clip-path reveals | Full GSAP suite + Lottie/SplitType as needed |
| 4 | Extreme | Three.js, WebGL, shader effects, physics simulations, custom canvas | Warn user. Implement Tier 3 for standard elements, provide CSS fallbacks for WebGL |
The tier determines which CDN libraries to include and what animation code pattern to use in the build prompt. NEVER use a lower-tier approach for a higher-tier site.
Workflow Overview
| Phase | Name | What happens | Output file |
|---|---|---|---|
| 1 | Site DNA Extraction | Chrome crawls the reference site, screenshots every scroll-viewport, extracts CSS @keyframes, GSAP configs, scroll behavior map, hover rules | site-dna.md |
| 2 | Brand Interview | Ask the user 8-10 targeted questions about their project | brand-interview.md |
| 3 | Merge & Map | Combine Site DNA + Brand Interview into a section-by-section build specification with concrete animation code | build-spec.md |
| 4 | Build Prompt Generation | Convert the build spec into a prompt that includes library-matched animation code, CDN links, and exact easing/timing values | build-prompt.md |
| 5 | Iterator | Compare reference vs implementation visually AND behaviorally (scroll behavior, hover states, animation timing) | iteration-N.md |
---
Phase 1 — Site DNA Extraction
Read `references/phase1-site-dna.md` for the full extraction protocol.
High-level steps: 1. Create ./ui-clone-workspace/ and ./ui-clone-workspace/screenshots/ directories 2. Navigate to the target URL in Chrome 3. Wait for full page load (wait for network idle) 4. Classify animation tier (check for GSAP, ScrollTrigger, Lottie, Three.js, Lenis, etc.) 5. Extract the full page metadata: title, meta description, favicon 6. Enhanced scroll-and-screenshot loop: scroll in viewport-sized increments, at each position:
- Record element positions/opacity/transforms BEFORE scrolling
- Scroll and wait for animations
- Record element positions/opacity/transforms AFTER scrolling
- Take screenshot
- Log which elements changed state (this is the scroll motion map)
7. Extract the full DOM HTML 8. Extract ALL @keyframes rules from stylesheets 9. Extract animation/transition CSS properties from all animated elements (opacity, transform, clip-path, transition, animation, will-change) 10. If GSAP detected: Extract ScrollTrigger instances (trigger, start, end, pin, scrub, snap, animation vars) 11. If GSAP detected: Extract active tweens from global timeline (targets, duration, ease, vars) 12. If Lenis/Locomotive detected: Extract smooth scroll config (duration, easing) 13. Extract all :hover CSS rules from stylesheets 14. Record hover transition properties from interactive elements (buttons, cards, links) 15. Identify: color palette, typography, spacing system, border radii, shadows 16. Map every visible section/component with ENHANCED animation fields (entry from-state, to-state, duration, easing, trigger, extracted code) 17. Document navigation and footer 18. Write everything to ./ui-clone-workspace/site-dna.md
Critical: The animation extraction (steps 8-14) is what makes v2 different. Without it, you're guessing at animations instead of replicating them. This is the difference between an 80% clone and a 95% clone.
After Phase 1 completes, immediately proceed to Phase 2.
---
Phase 2 — Brand Interview
Read `references/phase2-interview.md` for the full question set.
(Same as v1 — no changes needed)
After collecting answers, write to ./ui-clone-workspace/brand-interview.md and immediately proceed to Phase 3.
---
Phase 3 — Merge & Map
Read `references/phase3-merge.md` for the full merge protocol.
Key v2 change: The animation specification must include concrete code, not vague descriptions.
For each section's animation, include:
- The animation library to use (matching what was detected in Phase 1)
- The EXACT code — GSAP timeline, CSS @keyframes, or IntersectionObserver setup
- The EXACT easing curve (cubic-bezier values or GSAP ease name)
- The EXACT duration, delay, and stagger values
Wrong (v1 style):
Animations: fade-up on scroll, 600ms, ease-outRight (v2 style):
Animations:
Library: GSAP + ScrollTrigger
Code:
gsap.from('.feature-card', {
scrollTrigger: { trigger: '.features', start: 'top 80%' },
y: 40, opacity: 0, duration: 0.6,
ease: 'power2.out', stagger: 0.15
});Write to ./ui-clone-workspace/build-spec.md and proceed to Phase 4.
---
Phase 4 — Build Prompt Generation
Read `references/phase4-build.md` for the prompt template.
Key v2 changes: 1. Library matching: If reference uses GSAP, build with GSAP. If CSS-only, build with CSS. Never downgrade. 2. Include CDN links as actual HTML: Not "use GSAP" but the exact <script> tag 3. Include Lenis + ScrollTrigger sync boilerplate if both are used (this is the #1 bug source) 4. Paste extracted animation code into each section's specification 5. Include all extracted @keyframes in the CSS section 6. Include all extracted :hover rules in the CSS section 7. Add animation-specific quality checklist items: page load sequence, scroll triggers, pinning, parallax, hover states, easing feel
After building, test scroll behavior by scrolling through the entire page in Chrome before showing the user.
---
Phase 5 — Iterator (Post-Build Refinement)
Read `references/phase5-iterator.md` for the iteration protocol.
Key v2 additions:
Behavioral Comparison (NEW)
After visual screenshot comparison, also compare:
1. Scroll behavior: Run scroll recording script on both reference and build. Compare which elements animate, when they trigger, pin behavior, parallax speeds 2. Hover states: Programmatically hover over interactive elements on both sites. Compare what changes (transform, shadow, color) 3. Page load animation: Hard-reload both sites. Compare the entry animation sequence 4. Smooth scroll feel: Does the build's scroll feel as smooth as the reference? (Lenis/Locomotive config)
Animation-Specific Fix Patterns (NEW)
- Animations not firing → Check GSAP loaded, ScrollTrigger registered, Lenis synced
- Pinned section scrolls through → Check parent overflow:hidden, call ScrollTrigger.refresh()
- Wrong parallax speed → Adjust y value in scrub tween
- Stagger not working → Verify multiple targets exist
- Easing feels wrong → Consult GSAP ease reference, match to site DNA
---
File Structure
(Same as v1)
Important Notes
- Never skip the animation extraction in Phase 1. This is the entire point of v2. Without it, you're building animations from guesswork.
- Match the animation library. If the reference uses GSAP, the build MUST use GSAP. IntersectionObserver cannot replicate ScrollTrigger pinning, scrubbing, or timelines.
- Always include the Lenis + ScrollTrigger sync code if both are present. Omitting this causes 90% of "my animations broke" issues.
- Test by scrolling. After every build and every iteration, slowly scroll through the entire page in Chrome. This catches animation issues that screenshots miss.
- Default tech stack: Single
index.htmlwith Tailwind CSS via CDN, the animation libraries detected in Phase 1, Google Fonts. - The build prompt must be self-contained. Include ALL CDN links, ALL animation code, ALL CSS rules. Someone with zero context should be able to paste it and get a working, animated site.
UI Cloner — Animation Failure Analysis & Fixes
Executive Summary
Your 5-phase architecture is solid for static design cloning, but it has 6 critical blind spots that cause it to fail on sites with complex animations. The root cause: you're capturing what things LOOK LIKE (screenshots), but not what things DO (runtime behavior).
---
The 6 Root Causes
1. Phase 1 Detects Libraries But Never Extracts Animation Code
What happens now: Phase 1 checks for window.gsap, looks for script tags containing "gsap", etc. This tells you "GSAP is loaded" — but it never extracts:
- The actual GSAP timelines and their configurations
- ScrollTrigger pin/scrub/snap parameters
@keyframesdefinitions from stylesheets- CSS
animationandtransitionproperties on specific elements - Lottie JSON animation data
- Custom JS animation functions
Why this kills animation cloning: Knowing "GSAP is loaded" is like knowing "a piano is in the room" — it tells you nothing about what song is being played. The build phase gets a vague instruction like "uses GSAP animations" instead of the actual timeline code.
2. Screenshots Are Static — They Cannot Capture Motion
What happens now: Phase 1 takes screenshots AFTER animations settle. Phase 5 compares reference screenshots vs. build screenshots.
Why this kills animation cloning: Animations are defined by 4 things: start state → end state → timing → easing. Screenshots only capture end states. You lose 75% of the animation data. You can never tell from a screenshot whether an element faded up over 300ms with ease-out or slid in from the left over 1200ms with a custom cubic-bezier.
3. Build Phase Only Knows One Pattern: IntersectionObserver + Fade-Up
What happens now: Phase 4's animation section template is hardcoded to a single pattern:
// Observe all .animate-on-scroll elements
// When intersecting (threshold 0.2), add .is-visible classWhat award-winning sites actually use:
- GSAP ScrollTrigger with pinning (entire sections freeze while content transforms)
- Scrub-linked animations (animation progress tied to scroll position, not triggered on entry)
- Clip-path reveals, SVG path morphing, text character splitting
- Staggered timelines with 5-10 sequential animations
- Lottie/Rive animations synced to scroll
- CSS
scroll-timelineandanimation-timeline(new spec) - 3D transforms with perspective
- Cursor-following parallax layers
- Spring physics (Framer Motion, GSAP's physics plugins)
The current template can't express any of these.
4. No Extraction of CSS Animation Properties
What happens now: Phase 1 extracts color, background-color, font-family, padding, etc. from computed styles.
What's missing: It never extracts: transition, animation, animation-name, animation-duration, animation-timing-function, animation-delay, animation-iteration-count, animation-fill-mode, transform, will-change, perspective, transform-origin, clip-path.
These CSS properties ARE the animation. Without them, you've extracted the paint but not the motion.
5. No Video/Recording of Scroll Behavior
There's no mechanism to record what happens DURING a scroll — how fast do elements move relative to each other? Do things pin? Do backgrounds parallax at different rates? Do elements scrub (continuously animate with scroll position)?
6. The Build Prompt Is Library-Agnostic When It Shouldn't Be
What happens now: Build prompt defaults to "CSS animations + IntersectionObserver" regardless of what the reference site uses.
What should happen: If the reference uses GSAP, the build prompt should include GSAP CDN links and produce GSAP code. If it uses Lottie, include the Lottie player and the JSON data. Match the tools to the source.
---
Phase-by-Phase Fixes
PHASE 1 FIX: Add Deep Animation Extraction
Add these extraction steps AFTER the current Step 5 (DOM Extraction):
Step 5b: CSS Animation Extraction
// Extract all @keyframes from all stylesheets
const keyframes = [];
for (const sheet of document.styleSheets) {
try {
for (const rule of sheet.cssRules) {
if (rule instanceof CSSKeyframesRule) {
keyframes.push({
name: rule.name,
css: rule.cssText
});
}
}
} catch(e) {} // cross-origin sheets will throw
}
// Extract animation/transition properties from all visible elements
const animatedElements = [];
document.querySelectorAll('*').forEach(el => {
const cs = getComputedStyle(el);
const hasAnimation = cs.animationName !== 'none';
const hasTransition = cs.transitionProperty !== 'all' &&
cs.transitionProperty !== 'none' &&
cs.transitionDuration !== '0s';
const hasTransform = cs.transform !== 'none';
const hasClipPath = cs.clipPath !== 'none';
const hasWillChange = cs.willChange !== 'auto';
if (hasAnimation || hasTransition || hasTransform || hasClipPath || hasWillChange) {
animatedElements.push({
selector: getCSSSelector(el), // helper to get unique selector
tag: el.tagName,
classes: [...el.classList],
animation: hasAnimation ? {
name: cs.animationName,
duration: cs.animationDuration,
timingFunction: cs.animationTimingFunction,
delay: cs.animationDelay,
iterationCount: cs.animationIterationCount,
direction: cs.animationDirection,
fillMode: cs.animationFillMode,
} : null,
transition: hasTransition ? {
property: cs.transitionProperty,
duration: cs.transitionDuration,
timingFunction: cs.transitionTimingFunction,
delay: cs.transitionDelay,
} : null,
transform: hasTransform ? cs.transform : null,
transformOrigin: cs.transformOrigin,
clipPath: hasClipPath ? cs.clipPath : null,
opacity: cs.opacity,
willChange: hasWillChange ? cs.willChange : null,
perspective: cs.perspective,
});
}
});Step 5c: GSAP/ScrollTrigger Extraction
// If GSAP is present, extract all active tweens and ScrollTriggers
if (window.gsap) {
const gsapData = {
version: gsap.version,
tweens: gsap.globalTimeline.getChildren(true).map(t => ({
targets: t.targets?.()?.map(el => getCSSSelector(el)),
duration: t.duration(),
vars: t.vars, // contains all the animation properties
delay: t.delay(),
})),
scrollTriggers: window.ScrollTrigger ?
ScrollTrigger.getAll().map(st => ({
trigger: getCSSSelector(st.trigger),
start: st.vars.start,
end: st.vars.end,
pin: st.vars.pin,
scrub: st.vars.scrub,
snap: st.vars.snap,
markers: st.vars.markers,
animation: st.animation ? {
duration: st.animation.duration(),
vars: st.animation.vars,
} : null,
})) : [],
};
}Step 5d: Lottie Extraction
// If Lottie is present, extract animation data
if (window.lottie) {
const lottieAnims = document.querySelectorAll('[data-lottie], lottie-player, .lottie');
// Also check for lottie.getRegisteredAnimations() if available
}Step 5e: Scroll Behavior Recording
Instead of just taking screenshots, record a scroll behavior log:
For each scroll position:
1. Record all element positions (getBoundingClientRect for key elements)
2. Scroll by 100px
3. Wait 50ms
4. Record all element positions again
5. Calculate: which elements moved, by how much, relative to scroll distance
- Elements that moved LESS than scroll distance = sticky or pinned
- Elements that moved MORE than scroll distance = parallax (foreground)
- Elements that moved between 0-1x scroll = parallax (background)
- Elements that changed opacity/transform = scroll-triggered animationThis produces a motion map — a data structure that captures the scroll-linked behavior.
---
PHASE 3 FIX: Animation Specification Must Be Concrete
Replace the current vague animation tables with a concrete animation blueprint:
## Animation Blueprint
### Library Requirements
- GSAP 3.12 (CDN: https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/gsap.min.js)
- ScrollTrigger plugin (CDN: https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/ScrollTrigger.min.js)
- [Any other libraries detected in Phase 1]
### Animation 1: Hero Text Reveal
**Type**: Page load sequence (not scroll-triggered)
**Elements**: h1 > each word wrapped in span
**Technique**: Split text into words, animate each from y:40, opacity:0
**Timeline**:
- Word 1: delay 0.3s, duration 0.6s, ease: power3.out
- Word 2: delay 0.4s, duration 0.6s, ease: power3.out
- ... (stagger: 0.1s)
**Extracted code** (from reference):gsap.from(".hero-word", { y: 40, opacity: 0, duration: 0.6, stagger: 0.1, ease: "power3.out", delay: 0.3 });
### Animation 2: Features Section Pin + Scrub
**Type**: ScrollTrigger with pin and scrub
**Trigger element**: .features-section
**Pin**: true (section stays fixed while scroll progresses)
**Scrub**: 1 (animation tied to scroll position, smoothed over 1 second)
**Start**: "top top"
**End**: "+=200%" (user scrolls 2x the viewport height while section is pinned)
**What animates during scrub**:
- Card 1 fades in at 0-20% progress
- Card 2 fades in at 20-40% progress
- Card 3 fades in at 40-60% progress
- Background gradient shifts from blue to purple at 60-100%The key change: include the actual code and parameters, not just descriptions like "fade-up animation."
---
PHASE 4 FIX: Match the Animation Library to the Source
The build prompt template needs a conditional animation section:
If reference uses GSAP:
<!-- Include in <head> -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/gsap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/ScrollTrigger.min.js"></script>If reference uses Lottie:
<script src="https://cdnjs.cloudflare.com/ajax/libs/lottie-web/5.12.2/lottie.min.js"></script>If reference uses CSS-only animations: Include the extracted @keyframes rules directly.
If reference uses scroll-linked animations (modern CSS):
@supports (animation-timeline: scroll()) {
/* Modern scroll-driven animations */
}The build prompt should NEVER default to IntersectionObserver if the reference uses GSAP. Use the same tool the reference uses.
---
PHASE 5 FIX: Add Behavioral Comparison (Not Just Visual)
The iterator currently only compares static screenshots. Add a behavioral comparison step:
Step 3b: Scroll Behavior Comparison
1. Open reference site → run scroll behavior recording script (from Phase 1 fix)
2. Open build site → run same scroll behavior recording script
3. Compare the two motion maps:
- Are the same elements animated?
- Do pinned sections pin at the same scroll positions?
- Is parallax speed ratio matching?
- Do stagger delays match?
- Are easing curves visually similar?Step 3c: Interaction Comparison
1. On reference: hover over each interactive element, record the computed style change
2. On build: hover over each interactive element, record the computed style change
3. Compare: transform, box-shadow, background-color, scale, opacity changes on hover---
New Section to Add to SKILL.md: Animation Complexity Tiers
Add a classification system so the skill knows how much effort animations need:
## Animation Complexity Tiers
### Tier 1: Simple (IntersectionObserver + CSS transitions)
- Fade-in on scroll
- Slide-up on scroll
- Hover scale/color changes
- Simple CSS transitions
**Build approach**: Vanilla JS + CSS is fine
### Tier 2: Intermediate (GSAP or advanced CSS)
- Staggered scroll animations
- Parallax backgrounds
- Text split animations
- Smooth scroll (Lenis/Locomotive)
- Nav background transitions on scroll
**Build approach**: Include GSAP + ScrollTrigger CDN
### Tier 3: Complex (GSAP ScrollTrigger advanced, Lottie, WebGL)
- Pinned sections with scrub
- Horizontal scroll sections
- Lottie animations (synced to scroll)
- SVG path drawing/morphing
- Clip-path reveals
- 3D transforms with perspective
- Cursor-following effects
**Build approach**: Full GSAP suite + specific libraries. May need multiple JS files.
### Tier 4: Extreme (Custom WebGL, Three.js, Shader-based)
- WebGL backgrounds or heroes
- Particle systems
- Shader-based distortion effects
- Physics simulations
- Custom canvas animations
**Build approach**: Warn user this may require simplification. Extract what's possible, suggest alternatives for the rest.During Phase 1, classify the reference site's animation tier. This sets expectations and determines the build approach.
---
Summary of Changes Needed Per File
| File | Change | Priority |
|---|---|---|
SKILL.md | Add animation tiers, add library-matching rule | HIGH |
phase1-site-dna.md | Add Steps 5b-5e (CSS animation extraction, GSAP extraction, Lottie extraction, scroll behavior recording) | CRITICAL |
phase3-merge.md | Replace vague animation tables with concrete animation blueprint format (include actual code) | HIGH |
phase4-build.md | Make animation library conditional (match reference), include CDN links, remove hardcoded IntersectionObserver default | CRITICAL |
phase5-iterator.md | Add behavioral comparison (scroll behavior + hover interaction comparison) | MEDIUM |
---
Quick Win: The Single Biggest Impact Fix
If you only change ONE thing, change Phase 1 to extract @keyframes rules and the animation/transition CSS properties from all animated elements. This alone will give the build phase actual data to work with instead of guessing.
UI Cloner — Claude Code Skill
Clone any award-winning website and adapt it to your brand using Claude Code with Chrome.
Installation
Option 1: Manual install (recommended)
1. Extract the ui-cloner folder 2. Copy it into your Claude Code skills directory:
# Find or create your skills directory
mkdir -p ~/.claude/skills
# Copy the skill
cp -r ui-cloner ~/.claude/skills/3. In your project's .claude/settings.json (or global settings), add the skill path:
{
"skills": ["~/.claude/skills/ui-cloner"]
}Option 2: Project-local skill
Place the ui-cloner/ folder directly inside your project's .claude/skills/ directory:
mkdir -p .claude/skills
cp -r ui-cloner .claude/skills/Claude Code will automatically detect it.
Prerequisites
- Claude Code with Chrome enabled. Start with:
claude --chrome- That's it. No other dependencies.
Usage
Full workflow (recommended)
Clone this site: https://example.comThis kicks off the entire 5-phase pipeline: 1. Site DNA — Claude opens Chrome, scrolls through the site, screenshots everything, extracts all design tokens 2. Brand Interview — Claude asks you ~10 questions about your project 3. Merge — Combines the site's design with your brand into a build specification 4. Build Prompt — Generates a self-contained prompt, then asks if you want to build it 5. Iterator — After the build, compare reference vs implementation and refine
Just extract site DNA
Extract the design DNA from: https://example.comRun the iterator on an existing build
Compare my build against the reference and fix the differencesor just:
IterateOutput Files
Everything is created in ./ui-clone-workspace/:
ui-clone-workspace/
├── screenshots/ # Reference + build screenshots
├── site-dna.md # Full forensic audit of reference site
├── brand-interview.md # Your answers to the brand interview
├── build-spec.md # Merged specification
├── build-prompt.md # Copy-pasteable prompt for any AI tool
├── iteration-1.md # First iteration report
└── output/
└── index.html # The built websiteTips
- Pick a good reference site. Check Awwwards, Godly, or Land-book for inspiration.
- Be specific in the interview. The more detail you give about your brand, the better the output.
- Default stack is a single HTML file with Tailwind CDN + vanilla JS. Override by specifying your preferred stack in the interview.
- Run 2-3 iterations for best results. First pass gets ~80%, iterations close the gap.
- The build prompt is portable. You can paste it into Cursor, Windsurf, Bolt, v0, or any other AI coding tool.
How it compares to the video workflow
This skill replicates the exact workflow from the video:
- Phase 1 = "UI Cloner" (forensic site audit via Chrome)
- Phase 2 = "Brand Interview" (your project details)
- Phase 3 = "Merge" (combining DNA + interview)
- Phase 4 = "Build" (generating + optionally executing the prompt)
- Phase 5 = "Iterator" (side-by-side comparison and refinement)
Phase 1 — Site DNA Extraction Protocol (v2 — Deep Animation Capture)
Overview
This phase uses Chrome (via claude --chrome) to perform a forensic audit of the target website. The goal is to extract every visual, structural, AND behavioral detail so it can be replicated — including all animation code, scroll behaviors, and interaction states.
Step-by-Step Extraction
1. Setup
mkdir -p ./ui-clone-workspace/screenshots2. Navigate and Wait
- Navigate to the target URL
- Wait for the page to fully load:
- Wait for network idle (no pending requests for 500ms)
- Wait an additional 2 seconds for JS-driven animations to initialize
- If the page has a loading screen/animation, wait for it to complete
3. Animation Tier Classification
Do this FIRST, before deep extraction. Run this in the browser console:
const tier = {
hasGSAP: !!window.gsap,
hasScrollTrigger: !!window.ScrollTrigger,
hasLottie: !!window.lottie || !!document.querySelector('lottie-player'),
hasThreeJS: !!window.THREE,
hasFramerMotion: !!document.querySelector('[data-framer-appear-id]'),
hasBarba: !!window.barba,
hasLenis: !!window.Lenis || !!window.__lenis,
hasLocomotive: !!window.LocomotiveScroll,
hasSplitText: !!window.SplitText || !!window.SplitType,
hasSwiper: !!window.Swiper,
hasAOS: !!window.AOS,
cssAnimationCount: [...document.styleSheets].reduce((count, sheet) => {
try {
return count + [...sheet.cssRules].filter(r => r instanceof CSSKeyframesRule).length;
} catch(e) { return count; }
}, 0),
scrollTriggeredElements: document.querySelectorAll('[data-aos], [data-scroll], [data-gsap], .animate-on-scroll, .scroll-animate, [data-animate]').length,
};
// Classify
let animationTier = 1;
if (tier.hasGSAP && tier.hasScrollTrigger) animationTier = Math.max(animationTier, 3);
else if (tier.hasGSAP) animationTier = Math.max(animationTier, 2);
if (tier.hasLottie) animationTier = Math.max(animationTier, 3);
if (tier.hasThreeJS) animationTier = 4;
if (tier.hasLenis || tier.hasLocomotive) animationTier = Math.max(animationTier, 2);
if (tier.hasSplitText) animationTier = Math.max(animationTier, 2);
if (tier.cssAnimationCount > 5) animationTier = Math.max(animationTier, 2);
console.log('Animation Tier:', animationTier, tier);Record the tier in the Site DNA document. This determines the build approach in Phase 4:
- Tier 1 (Simple): CSS transitions + IntersectionObserver
- Tier 2 (Intermediate): GSAP core + Lenis/smooth scroll
- Tier 3 (Complex): GSAP + ScrollTrigger + Lottie + text splitting
- Tier 4 (Extreme): Three.js / WebGL / custom shaders — warn user about limitations
4. Viewport and Page Dimensions
- Record the viewport size (default: 1440×900 for desktop)
- Get the full page height via
document.body.scrollHeightordocument.documentElement.scrollHeight - Calculate number of scroll positions needed:
Math.ceil(pageHeight / viewportHeight)
5. Scroll-and-Screenshot Loop (Enhanced)
For each scroll position (starting at top):
1. Record pre-scroll state: For all key elements, capture getBoundingClientRect(), opacity, transform via computed style 2. Scroll to position: window.scrollTo(0, position) 3. Wait 800ms for scroll-triggered animations to fire 4. Record post-scroll state: Same elements, capture rect + styles again 5. Take a screenshot → save as screenshots/reference-{NN}.png 6. Log state changes: Any element whose opacity, transform, position, or clip-path changed between pre-scroll and post-scroll is a scroll-animated element. Record:
- Which element (CSS selector)
- What changed (opacity 0→1, translateY 40px→0, etc.)
- At what scroll position it triggered
Critical: This scroll behavior log is the motion map. It captures WHAT ANIMATES and WHEN, which screenshots alone cannot.
6. DOM Extraction
Extract the full rendered DOM. Focus on:
- document.documentElement.outerHTML (full HTML)
- All <link> and <style> tags (stylesheets)
- All <script> tags (identify animation/UI libraries)Identify loaded libraries by checking:
window.gsapor scripts containing "gsap" → GSAP animationswindow.ScrollTrigger→ GSAP ScrollTrigger pluginwindow.Lottieor lottie scripts → Lottie animations- Scripts containing "framer-motion" → Framer Motion
- Scripts containing "aos" → AOS (Animate On Scroll)
- Scripts containing "swiper" → Swiper.js sliders
- Scripts containing "locomotive" or "lenis" → Smooth scroll libraries
- Scripts containing "three" or "webgl" → 3D/WebGL effects
- Scripts containing "splitting" or "splittype" or "SplitText" → Text splitting
window.SplitTypeorwindow.SplitText→ Text character/word animation- Scripts containing "barba" → Page transition library
- Any
IntersectionObserverusage → Scroll-triggered behavior
7. CSS Animation Extraction (NEW — CRITICAL)
Execute this in the browser to extract ALL animation-related CSS:
// === STEP 7a: Extract all @keyframes ===
const allKeyframes = [];
for (const sheet of document.styleSheets) {
try {
for (const rule of sheet.cssRules) {
if (rule instanceof CSSKeyframesRule) {
allKeyframes.push({
name: rule.name,
cssText: rule.cssText
});
}
}
} catch(e) { /* cross-origin sheets — skip */ }
}
// === STEP 7b: Extract animation/transition properties from ALL elements ===
function getUniqueSelector(el) {
if (el.id) return '#' + el.id;
let path = [];
while (el && el.nodeType === 1) {
let selector = el.tagName.toLowerCase();
if (el.id) { path.unshift('#' + el.id); break; }
if (el.className && typeof el.className === 'string') {
const classes = el.className.trim().split(/\s+/).filter(c =>
!c.match(/^(is-|has-|js-|active|visible|loaded|animated)/) // skip state classes
).slice(0, 3);
if (classes.length) selector += '.' + classes.join('.');
}
const parent = el.parentElement;
if (parent) {
const siblings = [...parent.children].filter(s => s.tagName === el.tagName);
if (siblings.length > 1) {
selector += ':nth-child(' + ([...parent.children].indexOf(el) + 1) + ')';
}
}
path.unshift(selector);
el = el.parentElement;
}
return path.join(' > ');
}
const animatedElements = [];
document.querySelectorAll('*').forEach(el => {
const cs = getComputedStyle(el);
const hasAnimation = cs.animationName && cs.animationName !== 'none';
const hasTransition = cs.transitionProperty !== 'all' &&
cs.transitionProperty !== 'none' &&
cs.transitionDuration !== '0s';
const hasTransform = cs.transform && cs.transform !== 'none';
const hasClipPath = cs.clipPath && cs.clipPath !== 'none';
const hasWillChange = cs.willChange && cs.willChange !== 'auto';
const hasFilter = cs.filter && cs.filter !== 'none';
const lowOpacity = parseFloat(cs.opacity) < 1;
if (hasAnimation || hasTransition || hasTransform || hasClipPath || hasWillChange) {
animatedElements.push({
selector: getUniqueSelector(el),
tag: el.tagName,
classes: [...el.classList].join(' '),
// Animation properties
animation: hasAnimation ? {
name: cs.animationName,
duration: cs.animationDuration,
timingFunction: cs.animationTimingFunction,
delay: cs.animationDelay,
iterationCount: cs.animationIterationCount,
direction: cs.animationDirection,
fillMode: cs.animationFillMode,
playState: cs.animationPlayState,
} : null,
// Transition properties
transition: hasTransition ? {
property: cs.transitionProperty,
duration: cs.transitionDuration,
timingFunction: cs.transitionTimingFunction,
delay: cs.transitionDelay,
} : null,
// Transform
transform: hasTransform ? cs.transform : null,
transformOrigin: cs.transformOrigin !== '50% 50%' ? cs.transformOrigin : null,
// Clip path
clipPath: hasClipPath ? cs.clipPath : null,
// Opacity
opacity: lowOpacity ? cs.opacity : null,
// Will change hints
willChange: hasWillChange ? cs.willChange : null,
// Perspective (for 3D)
perspective: cs.perspective !== 'none' ? cs.perspective : null,
// Filter
filter: hasFilter ? cs.filter : null,
});
}
});
console.log('Keyframes:', JSON.stringify(allKeyframes, null, 2));
console.log('Animated elements:', JSON.stringify(animatedElements, null, 2));8. GSAP/ScrollTrigger Deep Extraction (NEW — if GSAP detected)
if (window.gsap) {
const gsapData = {
version: gsap.version,
// All registered plugins
plugins: Object.keys(gsap.plugins || {}),
};
// Extract ScrollTrigger instances
if (window.ScrollTrigger) {
gsapData.scrollTriggers = ScrollTrigger.getAll().map(st => {
const data = {
trigger: st.trigger ? getUniqueSelector(st.trigger) : null,
start: st.vars.start,
end: st.vars.end,
pin: !!st.vars.pin,
pinSpacing: st.vars.pinSpacing,
scrub: st.vars.scrub,
snap: st.vars.snap,
toggleClass: st.vars.toggleClass,
toggleActions: st.vars.toggleActions,
};
// Try to extract the associated animation
if (st.animation) {
try {
const targets = st.animation.targets?.();
data.animation = {
targets: targets ? targets.map(t => t.nodeType ? getUniqueSelector(t) : String(t)) : [],
duration: st.animation.duration(),
vars: JSON.parse(JSON.stringify(st.animation.vars || {})),
};
} catch(e) {
data.animation = { error: 'Could not serialize' };
}
}
return data;
});
}
// Extract active tweens from global timeline
try {
const children = gsap.globalTimeline.getChildren(true, true, true);
gsapData.tweenCount = children.length;
gsapData.sampleTweens = children.slice(0, 20).map(t => {
try {
return {
targets: t.targets?.()?.map(el => el.nodeType ? getUniqueSelector(el) : String(el)),
duration: t.duration(),
delay: t.delay(),
ease: t.vars?.ease,
vars: Object.keys(t.vars || {}).filter(k =>
!['onComplete','onStart','onUpdate','callbackScope','id','lazy'].includes(k)
),
};
} catch(e) { return { error: 'Could not serialize' }; }
});
} catch(e) {}
console.log('GSAP Data:', JSON.stringify(gsapData, null, 2));
}9. Smooth Scroll Library Extraction (NEW — if Lenis/Locomotive detected)
// Lenis
if (window.Lenis || window.__lenis) {
const lenis = window.__lenis || document.querySelector('[data-lenis]')?.__lenis;
if (lenis) {
console.log('Lenis config:', JSON.stringify({
duration: lenis.options?.duration,
easing: lenis.options?.easing?.toString(),
smooth: lenis.options?.smooth,
smoothTouch: lenis.options?.smoothTouch,
direction: lenis.options?.direction,
}));
}
}10. Hover State Extraction (NEW)
For every interactive element (buttons, links, cards), capture the hover state:
// Identify interactive elements
const interactiveEls = document.querySelectorAll('a, button, [role="button"], .card, [class*="card"], [class*="btn"]');
const hoverStates = [];
for (const el of interactiveEls) {
// Capture base state
const baseStyles = {
transform: getComputedStyle(el).transform,
boxShadow: getComputedStyle(el).boxShadow,
backgroundColor: getComputedStyle(el).backgroundColor,
color: getComputedStyle(el).color,
borderColor: getComputedStyle(el).borderColor,
opacity: getComputedStyle(el).opacity,
scale: getComputedStyle(el).scale,
};
// Simulate hover via :hover pseudo-class check isn't possible programmatically,
// but we can check for transition properties which indicate hover animation exists
const cs = getComputedStyle(el);
if (cs.transitionProperty !== 'none' && cs.transitionProperty !== 'all' ||
cs.transitionDuration !== '0s') {
hoverStates.push({
selector: getUniqueSelector(el),
text: el.textContent?.trim().slice(0, 50),
transition: {
property: cs.transitionProperty,
duration: cs.transitionDuration,
timing: cs.transitionTimingFunction,
delay: cs.transitionDelay,
},
baseStyles,
// Also extract :hover rules from stylesheets
});
}
}
// Extract :hover rules from stylesheets
const hoverRules = [];
for (const sheet of document.styleSheets) {
try {
for (const rule of sheet.cssRules) {
if (rule.selectorText && rule.selectorText.includes(':hover')) {
hoverRules.push({
selector: rule.selectorText,
cssText: rule.cssText
});
}
}
} catch(e) {}
}
console.log('Hover states:', JSON.stringify(hoverStates, null, 2));
console.log('Hover CSS rules:', JSON.stringify(hoverRules, null, 2));11. Design Token Extraction
(Same as original Phase 1 Step 6 — colors, typography, spacing, visual effects)
12. Component/Section Mapping
(Same as original Phase 1 Step 7, but with ENHANCED animation fields)
For EACH section, record:
### Section N: [Name] (e.g., "Hero", "Features Grid", "Testimonials")
**Viewport position**: Screenshots reference-03 to reference-04
**HTML structure**: [e.g., "section > div.container > div.grid.grid-cols-3 > div.card × 6"]
**Layout**: [flex-row | grid-3col | absolute-positioned | sticky | etc.]
**Background**: [color/gradient/image/video/pattern]
**Content elements**:
- Heading: [text, font-size, font-weight, color, max-width]
- Subheading: [text, styles]
- Body text: [text, styles]
- CTA buttons: [text, styles, hover styles]
- Images/media: [dimensions, object-fit, border-radius]
- Icons: [library (Lucide/Heroicons/custom SVG), size, color]
**Animations (DETAILED)**:
- **Entry type**: [CSS transition triggered by class | GSAP tween | GSAP ScrollTrigger | CSS @keyframes | Lottie]
- **Entry from-state**: [opacity:0, y:40px, clipPath: inset(100% 0 0 0)] — EXACT values
- **Entry to-state**: [opacity:1, y:0, clipPath: inset(0)] — EXACT values
- **Duration**: [exact ms]
- **Easing**: [exact cubic-bezier or GSAP ease name like "power3.out"]
- **Delay**: [exact ms, or stagger value]
- **Trigger**: [page load | scroll into view at N% | scroll position | parent animation complete]
- **Scroll behavior**: [none | parallax (speed ratio) | pin (start/end positions) | scrub (scrub value)]
- **Extracted code**: [if GSAP, paste the actual tween/timeline code. If CSS, paste the @keyframes + animation property]
- **Hover effects**:
- Property changes: [transform: scale(1.03), boxShadow: 0 20px 40px rgba(0,0,0,0.15)]
- Transition: [duration, easing]
- Extracted CSS rule: [paste the :hover rule]
- **Micro-interactions**: [button press scale, card tilt, icon spin, input focus glow, etc.]
- **Continuous animations**: [floating elements, rotating gradients, pulsing dots, blinking cursors]
**Responsive hints**: [any visible responsive classes or media query behavior]13. Navigation Analysis
(Same as original)
14. Footer Analysis
(Same as original)
15. Write the Site DNA Document
Compile everything into ./ui-clone-workspace/site-dna.md with this structure:
# Site DNA: [Site Name]
**Source URL**: [url]
**Crawled**: [date/time]
**Screenshots**: [N] viewport captures in ./screenshots/
**Animation Tier**: [1-4] — [Simple/Intermediate/Complex/Extreme]
## 1. Identity & Vibe
[2-3 sentence summary of the site's personality, target audience feel, overall impression]
## 2. Design System Tokens
### Colors
[Full palette with hex values, organized by role]
### Typography
[Font families, size scale, weight scale, line heights]
### Spacing
[Spacing scale, container max-width, section padding]
### Visual Effects
[Border radii, shadows, blurs, gradients]
## 3. Animation System (DETAILED)
### Libraries Detected
| Library | Version | CDN URL | Purpose |
|---------|---------|---------|---------|
| GSAP | 3.12.5 | [url] | Core animation engine |
| ScrollTrigger | 3.12.5 | [url] | Scroll-linked animations |
| Lenis | 1.0.42 | [url] | Smooth scrolling |
| SplitType | 0.3.4 | [url] | Text character splitting |
### Global Animation Defaults
- Default easing: [exact value, e.g., cubic-bezier(0.16, 1, 0.3, 1) or "power3.out"]
- Default duration: [ms]
- Scroll trigger threshold: [when elements trigger — e.g., 20% visible]
- Stagger default: [ms between items]
- Smooth scroll: [Lenis/Locomotive config — duration, easing]
### Extracted @keyframes
[Paste ALL @keyframes rules found in Step 7a]
### Extracted ScrollTrigger Configurations
[Paste ALL ScrollTrigger instances from Step 8 — trigger, start, end, pin, scrub, animation details]
### Scroll Motion Map
[From Step 5 — which elements move/change during scroll, at what positions, by how much]
| Scroll Position | Element | Property Changed | From | To | Behavior |
|-----------------|---------|-----------------|------|-----|----------|
| 0-500px | .hero-bg | translateY | 0 | -100px | Parallax (0.2x) |
| 800px | .features | position | relative | fixed | Pin start |
| 800-2400px | .feature-card:nth(1) | opacity | 0 | 1 | Scrub |
| ... | ... | ... | ... | ... | ... |
### Extracted Hover Rules
[Paste ALL :hover CSS rules from Step 10]
## 4. Page Structure — Section by Section
[Each section documented per the ENHANCED template in Step 12]
## 5. Navigation
[Full nav documentation]
## 6. Footer
[Full footer documentation]
## 7. Technical Notes
[Libraries loaded with CDN URLs, performance observations, any quirks]Timing Expectation
Phase 1 typically takes 5-12 minutes depending on animation complexity. Do not rush it — the animation extraction is the difference between a $500 clone and a $5,000 clone.
Phase 2 — Brand Interview Protocol
Overview
This phase collects everything needed to adapt the reference site's design to the user's brand. The interview should feel like a productive creative brief session, not a boring form.
Interview Approach
Adapt to the user's style:
- If they're detailed and engaged → go question by question, discuss each
- If they're quick/impatient → present all questions at once, let them fill in
- If they say "make something up" → generate a compelling fictional brand, show it to them for approval
Be opinionated. If the user gives vague answers, suggest specific options based on what would work well with the reference site's design. Example:
- User: "I don't know, something modern?"
- You: "Based on the reference site's dark, techy vibe, I'd suggest: 'Precision-engineered. Quietly confident. Like a Bloomberg terminal meets Apple.com.' Does that resonate, or should we go a different direction?"
Core Questions
1. Identity
Question: "What's the name of your app / brand / project?" If blank: Generate a punchy name that fits the reference site's vibe.
2. Tagline
Question: "Give me a one-liner tagline or value proposition — the thing that goes in the hero section." If blank: Write 3 options, let them pick. Good taglines: Specific, benefit-driven, memorable. Not generic fluff.
3. Product Description
Question: "What does your product/service do? What problem does it solve? Give me 2-3 sentences." Why it matters: This drives all body copy, feature descriptions, and CTA language.
4. Target Audience
Question: "Who is your ideal user/customer? Be as specific as you can — age, profession, tech comfort, pain points." Why it matters: Determines tone, complexity of language, visual sophistication level.
5. Brand Personality
Question: "Pick 3-5 adjectives that describe how your brand should FEEL. Examples: sleek, playful, clinical, rebellious, premium, approachable, futuristic, earthy, bold, minimalist, warm, edgy." If blank: Suggest adjectives based on the reference site's vibe + the user's product.
6. Color Direction
Question: "Any color preferences? Or should I match the reference site's vibe?" Options to offer:
- Keep the reference site's exact palette
- Keep the vibe but with your brand colors (provide hex codes)
- Completely different palette (provide hex codes or describe)
- Let me suggest something based on your brand personality
7. Section Structure
Question: "The reference site has these sections: [list from Site DNA]. Do you want to: A) Keep the exact same section structure (recommended for best results) B) Keep most sections but add/remove some (tell me which) C) Only keep the visual style, completely restructure the content"
Recommendation: Always recommend option A for the first pass — it produces the best results. The user can iterate later.
8. Content Readiness
Question: "Do you have specific content ready for any of these? (headlines, feature names, testimonial quotes, pricing tiers, etc.) Or should I generate placeholder content that fits your brand?"
If they have content: Collect it organized by section. If they don't: You'll generate it in Phase 3. Note this.
9. Must-Haves and Must-Not-Haves
Question: "Anything you specifically want included or excluded? (e.g., 'I need a pricing section', 'No testimonials', 'Must have a demo video embed', 'No cookie banners')"
10. Tech Stack
Question: "What tech stack do you want the build in?" Default recommendation: Single index.html with Tailwind CSS (CDN), vanilla JS, Google Fonts. This is the most portable, easiest to deploy, and works in any AI coding tool. Other common options:
- React + Tailwind (if they want components)
- Next.js (if they need routing/SSR)
- Astro (if they want performance)
- HTML + custom CSS (if they prefer no frameworks)
Writing the Brand Interview Document
After collecting all answers, write ./ui-clone-workspace/brand-interview.md:
# Brand Interview Results
## Identity
**Name**: [name]
**Tagline**: [tagline]
**Product Description**: [description]
## Audience
**Target**: [audience description]
**Audience Persona**: [synthesized persona: name, age, job, pain points, what they value]
## Brand Personality
**Adjectives**: [3-5 adjectives]
**Brand Voice**: [1-2 sentences describing how the brand speaks — formal? casual? technical? friendly?]
**Brand Feeling**: [what should someone FEEL when they land on this site?]
## Visual Direction
**Color Approach**: [keep reference / adapt / custom]
**Color Details**: [any specific colors or preferences]
**Overall Vibe**: [synthesis of personality + reference site aesthetic]
## Section Structure
**Approach**: [keep all / adapt / restructure]
**Sections to include**:
1. [Section name] — [brief content description]
2. [Section name] — [brief content description]
...
**Sections to exclude**: [if any]
**Sections to add**: [if any]
## Content
**Ready content**: [list what they provided]
**Needs generation**: [list what needs to be created]
## Specific Requirements
**Must have**: [list]
**Must not have**: [list]
## Tech Stack
**Choice**: [stack]
**Dependencies**: [CDN links, libraries needed]After the Interview
Once the document is written, confirm with the user: "Here's what I've captured — anything you want to change before I merge this with the site DNA?"
If they approve (or say nothing), immediately proceed to Phase 3.
Phase 3 — Merge & Map Protocol
Overview
This is the critical synthesis phase. Take the Site DNA (what the reference site does) and the Brand Interview (what the user wants) and produce a section-by-section build specification that maps the user's brand onto the reference site's design patterns.
Merge Process
Step 1: Design System Translation
Map the reference site's design system to the user's brand:
## Design System
### Color Palette
| Role | Reference Value | Your Brand Value | CSS Variable |
|------|----------------|-----------------|--------------|
| Primary | #reference | #yours | --color-primary |
| Secondary | #reference | #yours | --color-secondary |
| Accent | #reference | #yours | --color-accent |
| Background | #reference | #yours | --color-bg |
| Surface | #reference | #yours | --color-surface |
| Text Primary | #reference | #yours | --color-text |
| Text Secondary | #reference | #yours | --color-text-muted |
| Border | #reference | #yours | --color-border |
### Typography
| Role | Reference | Your Brand | CSS Variable |
|------|-----------|-----------|--------------|
| Display Font | [font] | [font] | --font-display |
| Body Font | [font] | [font] | --font-body |
| Mono Font | [font] | [font] | --font-mono |
[Include full size scale, weight scale, line heights]
### Spacing & Layout
[Container max-width, section padding, component gaps — keep identical to reference unless user specified otherwise]
### Effects
[Border radii, shadows, gradients, blurs — adapt colors but keep structure identical]Step 2: Section-by-Section Mapping
For EACH section identified in the Site DNA, create a detailed mapping:
## Section [N]: [Section Name]
### Reference Behavior
[Copy from Site DNA — what the reference site does in this section]
### Your Version
**Layout**: [Identical to reference / Adapted — describe changes]
**Content**:
- Heading: "[Exact text to use]"
- Subheading: "[Exact text to use]"
- Body: "[Exact text to use]"
- CTA Button: "[Button text]" → [link destination]
- [List items, feature names, stats, whatever this section contains]
**Images/Media**:
- [Description of what each image should show]
- [Dimensions and treatment: rounded corners, overlay, etc.]
- [Use placeholder services: picsum.photos, placehold.co, or descriptive SVG placeholders]
**Animations** (replicate from reference):
- Entry: [exact animation description with timing]
- Scroll: [parallax/sticky/pin behavior]
- Hover: [interaction details]
- Micro: [any micro-interactions]
**Responsive**:
- Desktop: [layout description]
- Tablet: [layout changes]
- Mobile: [layout changes]Step 3: Generate Missing Content
For any content the user didn't provide, generate it now. The content must:
1. Match the brand voice from the interview 2. Mirror the content TYPE of the reference (if reference has 3 feature cards, generate 3 feature cards) 3. Be specific and credible — no generic "Lorem ipsum" or "We provide solutions." Write real copy. 4. Match the content LENGTH of the reference — if the reference hero has a 6-word headline, yours should be ~6 words too
Content generation guidelines:
- Headlines: Short, punchy, benefit-driven
- Feature descriptions: Specific, concrete, focus on outcomes not features
- Testimonials: Realistic names, titles, specific praise (not generic "Great product!")
- Stats/numbers: Plausible, impressive but not absurd
- CTA text: Action-oriented, urgent but not pushy
Step 4: Animation Specification
Create a dedicated animation reference that the builder can follow:
## Animation Specifications
### Global Settings
- Default easing: [cubic-bezier or named easing from reference]
- Default duration: [ms]
- Scroll trigger offset: [when elements enter viewport — e.g., "when 20% visible"]
- Stagger delay between items: [ms]
### Page Load Sequence
1. [Element] — [animation] — delay [ms]
2. [Element] — [animation] — delay [ms]
...
### Scroll-Triggered Animations
| Section | Element | Animation | Duration | Easing | Trigger |
|---------|---------|-----------|----------|--------|---------|
| Hero | Heading | fade-up 30px | 800ms | ease-out | page load |
| Hero | Subtext | fade-up 30px | 800ms | ease-out | 200ms delay |
| Features | Cards | fade-up staggered | 600ms | ease-out | scroll into view |
...
### Hover Interactions
| Element | Effect | Duration | Easing |
|---------|--------|----------|--------|
| Nav links | underline slide-in | 300ms | ease |
| Cards | scale(1.02) + shadow | 300ms | ease-out |
| Buttons | background shift + scale(1.05) | 200ms | ease |
...
### Special Effects
[Parallax layers, particle systems, gradient animations, cursor followers, etc.]Step 5: Responsive Breakpoints
## Responsive Breakpoints
- Desktop: ≥1280px — full layout as designed
- Laptop: 1024px-1279px — [changes]
- Tablet: 768px-1023px — [changes]
- Mobile: <768px — [changes]
### Navigation Responsive Behavior
- Desktop: [full nav bar]
- Mobile: [hamburger → slide-in menu / bottom sheet / etc.]
### Section-Specific Responsive Changes
[For each section, note layout changes at breakpoints]Write the Build Spec
Compile everything into ./ui-clone-workspace/build-spec.md. This document should be comprehensive enough that someone who has never seen the reference site could build an accurate version.
Quality Self-Check
Before saving, verify:
- [ ] Every section from the Site DNA has a corresponding mapping
- [ ] All content fields have actual text (no "[TODO]" or "[placeholder]")
- [ ] Color palette is complete with CSS variable names
- [ ] Typography is fully specified with Google Fonts URLs
- [ ] Every animation has timing, easing, and trigger specified
- [ ] Responsive behavior is noted for all major sections
- [ ] Tech stack dependencies are listed (CDN links, libraries)
Immediately proceed to Phase 4 after saving.
Phase 4 — Build Prompt Generation (v2 — Animation-Aware)
Overview
Convert the build spec into a single, self-contained prompt that any AI coding tool can execute to produce a working website. The prompt must be complete enough that someone with ZERO context about the reference site can build an accurate clone — including all animations, scroll behaviors, and interactions.
Critical Rule: Match the Animation Library
NEVER default to IntersectionObserver + CSS transitions if the reference site uses a more powerful animation system.
| Reference Uses | Build Must Use | CDN to Include |
|---|---|---|
| CSS-only animations | CSS @keyframes + transitions + IntersectionObserver | None |
| AOS library | AOS library | https://cdnjs.cloudflare.com/ajax/libs/aos/2.3.4/aos.js + CSS |
| GSAP (no ScrollTrigger) | GSAP core | https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/gsap.min.js |
| GSAP + ScrollTrigger | GSAP + ScrollTrigger plugin | gsap.min.js + ScrollTrigger.min.js |
| GSAP + SplitText | GSAP + SplitType (free alternative) | gsap.min.js + SplitType via CDN |
| Lottie | lottie-web | https://cdnjs.cloudflare.com/ajax/libs/lottie-web/5.12.2/lottie.min.js |
| Lenis smooth scroll | Lenis | https://unpkg.com/lenis@1.0.42/dist/lenis.min.js |
| Locomotive Scroll | Locomotive or Lenis (simpler) | Choose based on complexity |
| Swiper | Swiper | https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.js + CSS |
| Three.js / WebGL | Warn user, simplify or include Three.js | Case-by-case |
Prompt Structure
The generated prompt should follow this template. The animation section is now expanded significantly.
---
PROMPT TEMPLATE START
# Build Instruction: [Brand Name] Website
## Identity
You are building a landing page / website for **[Brand Name]** — [tagline].
[1-2 sentence product description].
Target audience: [audience].
Brand feeling: [adjectives]. The site should make visitors feel [emotion].
## Tech Stack
- [Primary: e.g., Single HTML file]
- [CSS: e.g., Tailwind CSS via CDN (https://cdn.tailwindcss.com)]
- [Fonts: e.g., Google Fonts — link tags for [Font1] and [Font2]]
- [Animation engine: GSAP 3.12 + ScrollTrigger — SEE CDN LINKS BELOW]
- [Smooth scroll: Lenis 1.0]
- [Icons: e.g., Lucide Icons via CDN, or inline SVGs]
### Required CDN Links (include ALL of these in <head>)<!-- Tailwind --> <script src="https://cdn.tailwindcss.com"></script>
<!-- Google Fonts --> <link rel="preconnect" href="https://fonts.googleapis.com"> <link href="https://fonts.googleapis.com/css2?family=[Font1]:wght@[weights]&family=[Font2]:wght@[weights]&display=swap" rel="stylesheet">
<!-- GSAP (if Tier 2+) --> <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/gsap.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/ScrollTrigger.min.js"></script>
<!-- Lenis Smooth Scroll (if detected) --> <script src="https://unpkg.com/lenis@1.0.42/dist/lenis.min.js"></script>
<!-- Lottie (if detected) --> <script src="https://cdnjs.cloudflare.com/ajax/libs/lottie-web/5.12.2/lottie.min.js"></script>
<!-- Swiper (if detected) --> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.css"> <script src="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.js"></script>
[Include ONLY the CDNs that the reference site actually uses]
## Design System
### CSS Custom Properties:root { / Colors / --color-primary: [hex]; --color-secondary: [hex]; --color-accent: [hex]; --color-bg: [hex]; --color-surface: [hex]; --color-text: [hex]; --color-text-muted: [hex]; --color-border: [hex];
/ Typography / --font-display: '[Font Name]', [fallback]; --font-body: '[Font Name]', [fallback]; --font-mono: '[Font Name]', monospace;
/ Spacing / --space-xs: [val]; --space-sm: [val]; --space-md: [val]; --space-lg: [val]; --space-xl: [val]; --space-2xl: [val];
/ Effects / --radius-sm: [val]; --radius-md: [val]; --radius-lg: [val]; --radius-full: 9999px; --shadow-sm: [val]; --shadow-md: [val]; --shadow-lg: [val];
/ Animation tokens / --ease-default: cubic-bezier(0.16, 1, 0.3, 1); / from Site DNA / --ease-smooth: cubic-bezier(0.22, 1, 0.36, 1); --duration-fast: 300ms; --duration-normal: 600ms; --duration-slow: 1000ms; }
### Typography Scale
[Same as v1]
### Container
[Same as v1]
## ========================================
## ANIMATION SYSTEM (CRITICAL SECTION)
## ========================================
### Animation Tier: [1-4]
This site is Animation Tier [N]. Build animations accordingly.
---
### IF TIER 1 (CSS-only):
Use IntersectionObserver + CSS transitions:
// Initialize scroll observer const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { entry.target.classList.add('is-visible'); // Stagger children const children = entry.target.querySelectorAll('.stagger-child'); children.forEach((child, i) => { child.style.transitionDelay = ${i * 100}ms; child.classList.add('is-visible'); }); } }); }, { threshold: 0.2 });
document.querySelectorAll('.animate-on-scroll').forEach(el => observer.observe(el));
/ Base hidden state / .animate-on-scroll { opacity: 0; transform: translateY(30px); transition: opacity [duration] [easing], transform [duration] [easing]; } .animate-on-scroll.is-visible { opacity: 1; transform: translateY(0); }
---
### IF TIER 2 (GSAP core):
// Register plugins gsap.registerPlugin(ScrollTrigger);
// Smooth scroll (if Lenis detected) const lenis = new Lenis({ duration: [extracted duration, e.g., 1.2], easing: (t) => Math.min(1, 1.001 - Math.pow(2, -10 t)), // or extracted easing smooth: true, }); function raf(time) { lenis.raf(time); requestAnimationFrame(raf); } requestAnimationFrame(raf); // Sync Lenis with ScrollTrigger lenis.on('scroll', ScrollTrigger.update); gsap.ticker.add((time) => lenis.raf(time 1000)); gsap.ticker.lagSmoothing(0);
// Page load sequence // [PASTE EXACT EXTRACTED TIMELINE FROM SITE DNA] gsap.from("[hero heading selector]", { y: [extracted value], opacity: 0, duration: [extracted duration], ease: "[extracted ease]", delay: [extracted delay] }); // ... continue for each element in the load sequence
// Scroll-triggered animations // [FOR EACH SCROLL ANIMATION FROM SITE DNA] gsap.from("[selector]", { scrollTrigger: { trigger: "[trigger selector]", start: "[extracted start, e.g., 'top 80%']", toggleActions: "[extracted, e.g., 'play none none none']", }, y: [extracted], opacity: 0, duration: [extracted], ease: "[extracted]", stagger: [extracted] });
---
### IF TIER 3 (GSAP + ScrollTrigger advanced):
Include all Tier 2 code PLUS:
// Pinned sections with scrub // [FOR EACH PINNED SECTION FROM SITE DNA] const [sectionName]Timeline = gsap.timeline({ scrollTrigger: { trigger: "[extracted trigger]", start: "[extracted, e.g., 'top top']", end: "[extracted, e.g., '+=200%']", pin: true, scrub: [extracted, e.g., 1], snap: [extracted, if any], } });
// Add animations to the pinned timeline [sectionName]Timeline .from("[card1]", { opacity: 0, y: 50 }, 0) .from("[card2]", { opacity: 0, y: 50 }, 0.2) .from("[card3]", { opacity: 0, y: 50 }, 0.4) .to("[background]", { backgroundColor: "[color]" }, 0.6);
// Text splitting animations // [If SplitType detected] const splitTexts = document.querySelectorAll('[data-split]'); splitTexts.forEach(el => { const split = new SplitType(el, { types: 'words,chars' }); gsap.from(split.chars, { scrollTrigger: { trigger: el, start: 'top 80%' }, y: [extracted], opacity: 0, duration: [extracted], stagger: [extracted], ease: "[extracted]" }); });
// Parallax layers // [FOR EACH PARALLAX ELEMENT FROM MOTION MAP] gsap.to("[selector]", { scrollTrigger: { trigger: "[parent section]", start: "top bottom", end: "bottom top", scrub: true, }, y: [extracted offset, e.g., -100], // negative = moves up slower (background parallax) });
// Horizontal scroll section (if detected) // [If motion map shows horizontal movement] const horizontalSections = gsap.utils.toArray('.horizontal-panel'); gsap.to(horizontalSections, { xPercent: -100 * (horizontalSections.length - 1), ease: "none", scrollTrigger: { trigger: ".horizontal-container", pin: true, scrub: 1, end: () => "+=" + document.querySelector(".horizontal-container").offsetWidth, }, });
---
### IF TIER 4 (WebGL / Three.js):
**Warn the user**: "The reference site uses WebGL/Three.js effects that are extremely complex to replicate. I'll implement the core layout and standard animations faithfully, and provide a simplified CSS/canvas alternative for the WebGL effects. For a pixel-perfect WebGL match, you'd need a dedicated Three.js developer."
Then implement Tier 3 animations for everything non-WebGL, and add a CSS gradient/noise/animation fallback for the WebGL sections.
---
### Hover Interactions (ALL TIERS)
/ [PASTE EXTRACTED :hover RULES FROM SITE DNA] /
/ Example — adapt to what was actually extracted: / .card { transition: transform [extracted]ms [extracted-easing], box-shadow [extracted]ms [extracted-easing]; } .card:hover { transform: [extracted, e.g., scale(1.02) translateY(-4px)]; box-shadow: [extracted, e.g., 0 20px 40px rgba(0,0,0,0.12)]; }
.btn-primary { transition: [extracted]; } .btn-primary:hover { [extracted hover properties] }
/ Nav links / .nav-link { position: relative; } .nav-link::after { / [extracted underline animation — width, height, color, transition] / } .nav-link:hover::after { / [extracted] / }
### @keyframes (ALL TIERS)
/ [PASTE ALL EXTRACTED @keyframes FROM SITE DNA] /
@keyframes fadeUp { from { opacity: 0; transform: translateY(30px); } to { opacity: 1; transform: translateY(0); } }
/ [Paste every extracted @keyframes rule] /
### Continuous/Ambient Animations
/ [Floating elements, rotating gradients, pulsing indicators, etc.] / / [Paste from Site DNA continuous animation section] /
## ========================================
## END ANIMATION SYSTEM
## ========================================
## Page Sections — Build These In Order
### Section 1: Navigation
[EXACT specification — same as v1 but with extracted hover CSS rules for nav links]
### Section 2: Hero
[EXACT specification — includes extracted page-load animation sequence code]
[Continue for EVERY section... Each section MUST include its specific animation code, not just "fade in on scroll"]
### Section N: Footer
[EXACT specification]
## Responsive Requirements
- **≥1280px**: Full desktop layout as specified above
- **1024-1279px**: [specific changes]
- **768-1023px**: [specific changes]
- **<768px**: [mobile layout — disable parallax, simplify scroll animations, hamburger nav]
### Responsive Animation Notes
- Disable parallax on mobile (scrub: false, pin: false for < 768px)
- Reduce stagger delays by 50% on mobile (faster perceived loading)
- Disable smooth scroll on touch devices OR use Lenis with `smoothTouch: false`
- Simplify or disable horizontal scroll sections on mobile
## Quality Checklist
Before considering this done, verify:
- [ ] All sections render correctly at desktop, tablet, and mobile widths
- [ ] **Page load animation sequence fires correctly** (elements animate in order with correct delays)
- [ ] **All scroll-triggered animations fire** (test by scrolling slowly through the entire page)
- [ ] **Pinned sections work** (if any — section stays fixed while scrolling through it)
- [ ] **Parallax effects work** (background elements move at different speeds)
- [ ] **Smooth scroll works** (if Lenis/Locomotive is specified — scroll should feel buttery)
- [ ] All hover effects work on buttons, cards, and links
- [ ] Navigation is responsive (hamburger on mobile)
- [ ] Typography is consistent with the design system
- [ ] Colors match the design system exactly
- [ ] No horizontal scrollbar at any viewport width
- [ ] All content is present (no placeholder text like "Lorem ipsum")
- [ ] **Animation easing curves feel right** (not linear or default ease — use the specified cubic-bezier)
- [ ] **Stagger timing feels natural** (items animate in sequence, not simultaneously)
- [ ] GSAP ScrollTrigger.refresh() is called after all content loads (if using GSAP)
- [ ] Lenis is properly synced with ScrollTrigger (if using both)
## Do NOT:
- Use placeholder text like "Lorem ipsum"
- Use generic stock photo URLs
- Skip animations — they define the experience
- Use default Tailwind colors — use CSS custom properties
- Use IntersectionObserver if GSAP ScrollTrigger is specified (they conflict)
- Add a cookie banner or popup unless specified
- Use linear easing unless the site DNA specifically says linear
- Forget to call ScrollTrigger.refresh() after DOM is readyPROMPT TEMPLATE END
---
Writing Guidelines
1. Be absurdly specific. "A nice hero section" → bad. "A full-viewport hero with a 64px heading in Syne Bold, #0a0a0a background, with a GSAP tween: gsap.from('.hero-heading', { y:40, opacity:0, duration:0.8, ease:'power3.out', delay:0.3})" → good.
2. Include exact text. Every heading, every button label, every feature description.
3. Paste extracted animation code. Don't describe "a scroll animation." Paste the GSAP timeline or CSS @keyframes that was extracted in Phase 1. The builder should be able to copy-paste animation code, not interpret vague descriptions.
4. Give HTML structure hints. Not full HTML — just the nesting pattern.
5. No emojis in the prompt.
6. Include ALL CDN links as actual HTML tags. Don't just say "use GSAP" — include the exact <script> tags.
7. Include the Lenis + ScrollTrigger sync code. This is the #1 source of "smooth scroll broke my animations" bugs. Always include the sync boilerplate if both are used.
After Writing the Prompt
Save to ./ui-clone-workspace/build-prompt.md.
Then ask the user what they want to do:
- Execute now: Build the site yourself using the prompt as instructions.
- Copy for external use: Just provide the prompt file.
- Review first: Let them read and edit before proceeding.
If Executing the Build
1. Create ./ui-clone-workspace/output/ directory 2. Follow the build prompt exactly as written 3. For default stack (single HTML): create ./ui-clone-workspace/output/index.html 4. Test scroll behavior: After building, scroll through the entire page in Chrome to verify all scroll-triggered animations fire 5. Ask if they want to run the iterator (Phase 5)
Phase 5 — Iterator Protocol (v2 — Behavioral Comparison)
Overview
The iterator compares the reference site against the built implementation, identifies discrepancies — both visual AND behavioral — and fixes them. v2 adds animation/interaction comparison that v1 was missing entirely.
When to Run
(Same triggers as v1)
Iteration Process
Step 1: Screenshot the Reference Site
(Same as v1 — reuse Phase 1 screenshots if available)
Step 2: Screenshot the Current Build
(Same as v1)
Step 3: Visual Comparison (Same as v1)
For each pair of screenshots (reference vs build), analyze layout, color, typography, content discrepancies.
Step 3b: Animation Behavior Comparison (NEW — CRITICAL)
This is what v1 was missing entirely. Screenshots can't capture motion.
3b-1: Scroll Behavior Comparison
Run the scroll behavior recording script on BOTH sites:
// Run this on reference AND on build
async function recordScrollBehavior() {
const elements = document.querySelectorAll('section, [class*="hero"], [class*="feature"], [class*="card"], nav, footer, h1, h2, h3');
const log = [];
// Record state at each scroll position
for (let scrollY = 0; scrollY < document.body.scrollHeight; scrollY += 100) {
window.scrollTo(0, scrollY);
await new Promise(r => setTimeout(r, 100)); // Let animations settle
const snapshot = {};
elements.forEach(el => {
const rect = el.getBoundingClientRect();
const cs = getComputedStyle(el);
snapshot[getUniqueSelector(el)] = {
top: rect.top,
opacity: parseFloat(cs.opacity),
transform: cs.transform,
position: cs.position,
clipPath: cs.clipPath,
};
});
log.push({ scrollY, snapshot });
}
return log;
}Then compare the two logs:
Check for each element: 1. Does it animate at all? If reference shows opacity changing from 0→1 at scrollY=800 but build shows opacity always at 1 → animation is missing 2. Does it pin? If reference shows an element's top staying at 0 for scrollY 800-2000 but build shows it scrolling normally → pin is missing 3. Is parallax speed right? Calculate element_movement / scroll_distance for both. If reference ratio is 0.5 but build is 1.0 → parallax not implemented 4. Does it trigger at the right position? If reference element fades in at scrollY=600 but build fades in at scrollY=200 → trigger threshold is wrong
3b-2: Hover Behavior Comparison
For each interactive element:
// Programmatically dispatch hover events and compare
async function compareHover(el) {
const before = captureStyles(el); // transform, shadow, bg, color, etc.
el.dispatchEvent(new MouseEvent('mouseenter', { bubbles: true }));
await new Promise(r => setTimeout(r, 500)); // Wait for transition
const after = captureStyles(el);
el.dispatchEvent(new MouseEvent('mouseleave', { bubbles: true }));
return { before, after, changed: diff(before, after) };
}Run on both reference and build. Compare:
- Does the element change on hover at all?
- Do the same properties change?
- Is the magnitude similar? (e.g., scale(1.02) vs scale(1.05))
3b-3: Page Load Animation Check
1. Hard-reload the build page 2. Observe the first 3 seconds: do elements animate in with a sequence? 3. Compare against the reference's page load sequence documented in Site DNA 4. Common issues:
- All elements appear instantly (no load animation)
- Elements animate but simultaneously (missing stagger/delays)
- Wrong easing (feels mechanical vs. smooth)
Step 4: Prioritize Fixes (Enhanced)
Rank issues by impact:
P0 — Critical (completely breaks the feel):
- Missing sections or major elements
- Completely wrong colors or fonts
- Broken layout
- No animations at all (nothing animates on scroll)
- GSAP/ScrollTrigger not loading (check console for errors)
- Smooth scroll not working (page scrolls normally instead of smooth)
P1 — Important (noticeable difference):
- Spacing/sizing mismatches
- Pinned sections not pinning (scroll right through)
- Parallax not working (all layers move at same speed)
- Scroll animations trigger at wrong position (too early/too late)
- Hover effects missing or wrong
- Stagger animation not staggering (all items animate simultaneously)
- Responsive behavior broken
P2 — Polish (subtle but professional):
- Easing curve doesn't feel right (too snappy, too slow, too bouncy)
- Animation duration off (too fast or too slow)
- Stagger delay off (items animate too close together or too far apart)
- Gradient angle/stop adjustments
- Border radius fine-tuning
- Shadow depth/spread adjustments
- Letter-spacing and line-height refinements
- Smooth scroll speed/momentum doesn't match (Lenis duration parameter)
Step 5: Apply Fixes (Enhanced)
Fix issues in priority order. Common animation fixes:
Fix: Animations Not Firing At All
// Check 1: Is GSAP loaded?
console.log('GSAP:', typeof gsap); // Should not be undefined
// Check 2: Is ScrollTrigger registered?
console.log('ST:', typeof ScrollTrigger); // Should not be undefined
// Check 3: Are animations defined AFTER DOM is ready?
// WRONG: <script> in <head> runs before DOM exists
// RIGHT: <script> at end of <body> or DOMContentLoaded
// Check 4: If using Lenis, is it synced with ScrollTrigger?
// This is the #1 bug — Lenis handles scroll events, ScrollTrigger doesn't see them
// FIX:
lenis.on('scroll', ScrollTrigger.update);
gsap.ticker.add((time) => lenis.raf(time * 1000));
gsap.ticker.lagSmoothing(0);Fix: Pinned Section Not Pinning
// Check 1: Does the trigger element exist?
console.log(document.querySelector('.features-section')); // Should not be null
// Check 2: Is the pin section's parent overflow:hidden?
// FIX: Remove overflow:hidden from parent containers
// Check 3: Call ScrollTrigger.refresh() after all images/fonts load
window.addEventListener('load', () => ScrollTrigger.refresh());Fix: Parallax Speed Wrong
// Adjust the y value in the scrub tween
// Lower y = less parallax movement
// Higher y = more parallax movement
gsap.to('.bg-layer', {
y: -150, // Adjust this number to match reference
scrollTrigger: { scrub: true }
});Fix: Stagger Not Working
// Check: Are targets an array/NodeList, not a single element?
// WRONG: gsap.from('.card', { stagger: 0.1 }) when there's only 1 .card
// RIGHT: gsap.from('.card', { stagger: 0.1 }) when there are multiple .card elements
// Check: Is stagger value reasonable?
// 0.05-0.1 = fast cascade
// 0.1-0.2 = noticeable sequence
// 0.3+ = dramatic one-by-oneFix: Easing Feels Wrong
// Common GSAP easings and when to use them:
// "power1.out" — gentle deceleration (subtle)
// "power2.out" — moderate deceleration (most common)
// "power3.out" — strong deceleration (dramatic entrance)
// "power4.out" — very strong (elements feel like they slam into place)
// "back.out(1.7)" — slight overshoot (bouncy, playful)
// "elastic.out(1, 0.3)" — spring physics (very bouncy)
// "expo.out" — sharp start, long ease (premium feel)
// CSS equivalent:
// power2.out ≈ cubic-bezier(0.16, 1, 0.3, 1)
// power3.out ≈ cubic-bezier(0.33, 1, 0.68, 1)
// expo.out ≈ cubic-bezier(0.16, 1, 0.3, 1) (close enough)Step 6: Re-Test and Verify
After applying fixes: 1. Reload the built site in Chrome 2. Scroll through the ENTIRE page slowly — verify every animation triggers 3. Hover over every interactive element — verify hover states 4. Hard-reload the page — verify page load animation sequence 5. Take new screenshots 6. If animations are still off, re-run the scroll behavior comparison (Step 3b-1)
Step 7: Write Iteration Report (Enhanced)
Save to ./ui-clone-workspace/iteration-{N}.md:
# Iteration [N] Report
## Issues Found: [count]
- P0 Critical: [count]
- P1 Important: [count]
- P2 Polish: [count]
## Visual Issues
[Layout, color, typography, content issues — same as v1]
## Animation/Behavior Issues (NEW)
### Missing Animations
- [ ] [Element] should [animate how] on [trigger] but doesn't animate at all
### Wrong Animation Parameters
- [ ] [Element] fades in over 300ms but reference is 800ms
- [ ] [Element] uses ease-in-out but reference uses power3.out
### Missing Interactions
- [ ] [Element] has no hover effect but should [scale/glow/shift]
### Scroll Behavior Mismatches
- [ ] [Section] should pin for 200vh but scrolls normally
- [ ] [Element] parallax speed is [actual] but should be [expected]
## Fixes Applied
### P0 Fixes
1. **[Issue]**: [What was wrong] → [What was changed, with code]
...
## Remaining Issues
[List anything not fixed and why]
## Recommendation
[Run another iteration? Focus on what?]Multi-Pass Strategy (Updated)
- Iteration 1: Focus on P0 — layout, structure, major visual, animations loading at all
- Iteration 2: Focus on P1 — spacing, scroll trigger positions, pin/scrub, parallax, hover states
- Iteration 3+: Focus on P2 — easing curves, durations, stagger timing, micro-polish
Typically 2-3 iterations get to ~90% match. Animation polish (easing, timing) is where the last 5-10% lives.