next-js/skills/skills/scroll-stop-builder/SKILL.md
Takes a video file (e.g. a product deconstruction/assembly animation) and builds a beautiful, performant website with scroll-driven animation. The video plays forward/backward as the user scrolls, creating a mesmerizing scroll-stopping effect. Uses frame extraction via FFmpeg, canvas-based rendering, and modern scroll-driven animation techniques. Includes: animated starscape background, annotation cards with snap-stop scroll, specs section with count-up animations, navbar with scroll-to-pill transform, loader, and full mobile responsiveness. Trigger when the user says "scroll-stop build", "scroll animation website", "scroll-driven video", "build the scroll-stop site", or provides a video file and asks to make it scroll-controlled. Also trigger if the user mentions "Apple-style scroll animation" or "video on scroll".
npx skillsauth add spuneiartur/claude-agent-specs scroll-stop-builderInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
You take a video file and build a production-quality website where the video playback is controlled by scroll position — creating a dramatic, Apple-style scroll-stopping effect.
Before building anything, you MUST gather information from the user through a brief interview. Do not assume any brand names, colors, or content — everything is customized per project.
Before touching any code or extracting any frames, ask the user these questions. Do not skip this step — the whole point of the skill is to build something tailored, not generic.
Ask these in a natural, conversational way — not as a numbered interrogation:
Ask the user how they want to provide the website content:
If the user provides a URL, use WebFetch to retrieve the page content and extract relevant
copy, product details, feature descriptions, spec numbers, and any other usable content.
Ask whether the user wants these included:
Only include these sections if the user explicitly opts in.
brew install ffmpeg if not)Once the interview is complete, construct the design system from the user's answers:
backdrop-filter: blur(20px), border-radius: 20pxThe most reliable approach for scroll-driven video:
This is the same technique Apple uses for their product pages.
Why not <video> with currentTime?
Browser video decoders aren't optimized for seeking on every scroll event. Canvas + pre-extracted
frames is buttery smooth and gives frame-perfect control.
ffprobe -v quiet -print_format json -show_streams -show_format "{VIDEO_PATH}"
Extract duration, fps, resolution, total frame count. Target 60-150 frames total.
mkdir -p "{OUTPUT_DIR}/frames"
ffmpeg -i "{VIDEO_PATH}" -vf "fps={TARGET_FPS},scale=1920:-2" -q:v 2 "{OUTPUT_DIR}/frames/frame_%04d.jpg"
Use -q:v 2 for high quality JPEG. Use JPEG not PNG for smaller files.
Create a single HTML file. The site has these sections (top to bottom):
For full implementation details of each section, read references/sections-guide.md.
Canvas rendering with Retina support:
canvas.width = window.innerWidth * window.devicePixelRatio;
canvas.height = window.innerHeight * window.devicePixelRatio;
canvas.style.width = window.innerWidth + 'px';
canvas.style.height = window.innerHeight + 'px';
Cover-fit drawing (desktop) — zoomed contain-fit (mobile): On desktop, use cover-fit so the frame fills edge-to-edge. On mobile, use a slightly zoomed contain-fit approach so the object stays centered and visible.
Annotation cards with snap-stop scroll: Annotation cards appear at specific scroll progress points (data-show/data-hide attributes). The scroll FREEZES briefly at each card position — creating a "boom, boom, boom" effect where each card pops up as you stop. Uses JS-based snap: detects when scroll progress enters a snap zone, scrolls to the exact position, locks the body overflow for ~600ms, then releases. The number of annotation cards is flexible — match it to the content the user provides.
Navbar scroll-to-pill transform: The navbar starts full-width, then on scroll shrinks to a centered pill shape (max-width ~820px) with rounded corners and glass-morphism background.
Count-up animation: Spec numbers animate from 0 to target with easeOutExpo easing, staggered 200ms apart. Numbers get an accent-color glow pulse while counting. Triggered by IntersectionObserver.
Animated starscape: A fixed canvas behind everything with ~180 stars that slowly drift and twinkle. Each star has random drift speed, twinkle speed/phase, and opacity. Creates a subtle living background.
All content comes from the interview (Step 0). Use the real brand name, real product details, and real copy — never use placeholder "Lorem ipsum" text. If content came from a website URL, use the actual text from that site. Adapt:
cd "{OUTPUT_DIR}" && python3 -m http.server 8080
Open http://localhost:8080 and test. Then open the browser URL for the user.
Key mobile adaptations:
requestAnimationFrame for drawing — Never draw directly in scroll handler{ passive: true } on scroll listener — Enables scroll optimizationsdevicePixelRatio — Crisp on Retina displaysdrawFrame when frame index changesscroll-behavior: smooth — Would interfere with frame-accurate scroll mappingposition: sticky keeps canvas viewport-fixed while scroll container moves| Issue | Solution |
| --- | --- |
| Frames don't load | Check file paths, ensure local server is running (can't load from file://) |
| Animation is choppy | Reduce frame count, ensure JPEG not PNG, check file sizes (<100KB each) |
| Canvas is blurry | Ensure devicePixelRatio scaling is applied |
| Scroll feels too fast/slow | Adjust .scroll-animation height (200vh=fast, 500vh=slow, 800vh=cinematic) |
| Mobile cards overlap content | Use compact single-line card design, position at bottom: 1.5vh |
| Snap-stop feels jarring | Reduce HOLD_DURATION to 400ms or increase SNAP_ZONE |
| Stars too bright/dim | Adjust starscape canvas opacity (default 0.6) |
| First frame isn't white | Ask user to re-export video with white opening frame |
When embedding scroll-driven animations into a Next.js Pages Router project:
Create a dedicated page or component that loads the scroll animation. Since the animation uses vanilla JS with canvas, wrap it in a useEffect to avoid SSR issues:
import { useEffect, useRef } from 'react';
import { PresentationLayout } from '@components';
const ScrollAnimationPage = () => {
const canvasRef = useRef(null);
useEffect(() => {
// Initialize canvas and scroll animation logic here
// This runs only on the client side
const canvas = canvasRef.current;
if (!canvas) return;
// Frame loading and scroll mapping logic...
return () => {
// Cleanup scroll listeners
};
}, []);
return (
<PresentationLayout>
<div className="scroll-animation" style={{ height: '500vh' }}>
<canvas ref={canvasRef} style={{ position: 'sticky', top: 0 }} />
</div>
</PresentationLayout>
);
};
export default ScrollAnimationPage;
Extract frames to the public/ directory so Next.js can serve them as static files:
# Extract frames to public/frames/
ffmpeg -i input.mp4 -vf "fps=30,scale=1920:-2" -q:v 5 public/frames/frame-%04d.jpg
Frame paths become: /frames/frame-0001.jpg, /frames/frame-0002.jpg, etc.
For preloading frame sequences with the project's lazy-blur system:
import { Image } from '@components';
// Preload critical frames (first few for immediate display)
const criticalFrames = Array.from({ length: 10 }, (_, i) =>
`/frames/frame-${String(i + 1).padStart(4, '0')}.jpg`
);
// Use <link rel="preload"> for critical frames in _document.js or via next/head
If the scroll animation code is large, use next/dynamic to avoid loading it on pages that don't need it:
import dynamic from 'next/dynamic';
const ScrollAnimation = dynamic(() => import('@components/ScrollAnimation'), {
ssr: false, // Canvas-based, client-only
loading: () => <div className="h-screen flex items-center justify-center"><Spinner /></div>,
});
tools
Replace with description of the skill and when Claude should use it.
tools
Comprehensive website performance audit and optimization skill. Identifies and automatically fixes performance issues including image optimization, video compression, lazy loading, Core Web Vitals, bundle size, and rendering strategy. Uses Lighthouse (via CLI or MCP when available), ffmpeg for media processing, and the project's existing Image component with blur-up lazy loading. Use this skill whenever the user mentions: website speed, page load time, performance audit, Core Web Vitals, Lighthouse, optimize images, compress videos, lazy loading, LCP, CLS, FID, INP, slow website, speed up, performance optimization, image compression, video optimization, blur placeholder, WebP conversion, media audit, bundle size, or wants to improve their website's loading performance. Also trigger when the user says "my site is slow", "optimize for speed", "reduce load time", "improve performance", or asks about image/video optimization in any context.
tools
Toolkit for interacting with and testing local web applications using Playwright. Supports verifying frontend functionality, debugging UI behavior, capturing browser screenshots, and viewing browser logs.
tools
Suite of tools for creating elaborate, multi-component claude.ai HTML artifacts using modern frontend web technologies (React, Tailwind CSS, shadcn/ui). Use for complex artifacts requiring state management, routing, or shadcn/ui components - not for simple single-file HTML/JSX artifacts.