AIStackDesignWorkflowGrowthAbout
Work With Me
Work With Me

AI Blueprints to Leverage Your Business. Strategies. Systems. Execution.

hi@omidsaffari.com
Instagram·X·LinkedIn·GitHub
Navigation
  • HomeHome
  • AboutAbout
  • BlogBlog
  • NewsletterNewsletter
  • Work With MeWork With Me
  • ContactContact
Legal
  • PrivacyPrivacy
  • TermsTerms
  • DisclaimerDisclaimer
  • SitemapSitemap
  • RSS FeedRSS Feed
Categories
  • AIAI
  • StackStack
  • DesignDesign
  • WorkflowWorkflow
  • GrowthGrowth
Topics
  • AI AgentsAI Agents
  • PromptsPrompts
  • Next.jsNext.js
  • n8nn8n
  • NotionNotion
Formats
  • GuidesGuides
  • LabsLabs
  • ToolsTools
  • TrendsTrends
  • ResourcesResources
More Formats
  • TutorialsTutorials
  • Case StudiesCase Studies
  • ComparisonsComparisons
  • TemplatesTemplates
  • ChecklistsChecklists
Empire
  • DaVinci HorizonDaVinci Horizon
  • Imperfeqt AIImperfeqt AI
  • DVNC StudioDVNC Studio
  • DVNC.aeDVNC.ae
  • With LidaWith Lida
Connect
  • YouTubeYouTube
  • Twitter/XTwitter/X
  • LinkedInLinkedIn
  • GitHubGitHub
  • InstagramInstagram
© 2026 omidsaffari.comBuilt with Next.js · Vercel
  1. Blog
  2. Stack

Build a Premium Anime.js Interactive Site Using a Claude Workflow

Most solo devs skip motion entirely because hiring an animator is expensive and the Anime.js docs feel abstract. This is the exact Claude workflow that gets you scroll-triggered timelines, hover effects, and SVG animations in an afternoon.

LevelIntermediate
Tools
anime.js
A
claude
C
javascript
J
vite
V
Build a Premium Anime.js Interactive Site Using a Claude Workflow
Omid Saffari

Founder & CEO, AI Entrepreneur

Share
Stay updated

Get weekly AI blueprints and insights.

Julian Garnier — the guy who wrote Anime.js — replied "lol" to a thread by @oluwaphilemon1 showing Claude generating production-grade Anime.js animation code from scratch. That thread got 10k views in a day. The reason it spread: most indie devs know their sites feel flat but can't justify the cost of a motion designer, and nobody had written down the actual workflow. This guide is that workflow, with the exact prompts, the file structure, and the code that comes out the other side.

Why Anime.js and why now

The JavaScript animation ecosystem has settled. GSAP is the professional standard but its commercial license costs $150/year for revenue-generating sites. CSS animations cap out fast once you need sequencing or scroll-driven timelines. Framer Motion is React-only. Anime.js is MIT-licensed, framework-agnostic, weighs 17kb gzipped, and has the cleanest timeline API in the category. It also happens to be one of the libraries Claude knows thoroughly — the training data includes the full docs, a large number of CodePen examples, and years of Stack Overflow answers. That combination matters: you get a model that can reason about anime.timeline(), stagger delays, and SVG morphing without hallucinating the API surface.

The Claude angle here is not "AI writes my code so I don't have to think." It's more specific: Claude is good at translating a visual description into a sequenced animation timeline, which is the part that takes experienced developers the longest. Figuring out that the hero text should fade in at 0ms, the subtitle at 200ms, and the CTA at 400ms, with an easing curve that feels premium — that's where an hour disappears. Claude compresses that hour into a prompt.

The project setup

You don't need a framework for this. A Vite vanilla project keeps the feedback loop tight and keeps the output portable. If your actual project is a Next.js app, everything here drops in — you'll just wrap the Anime.js calls in a useEffect with a ref.

terminal
bash
1npm create vite@latest motion-site -- --template vanilla
2cd motion-site
3npm install
4npm install animejs

Anime.js v4 was released in early 2025. If you're installing fresh today you're getting v4, which changed the import path. The old import anime from 'animejs/lib/anime.esm.js' is gone. In v4:

src/main.js
js
1import { animate, stagger, createTimeline, onScroll } from 'animejs';

Pin to the exact version in your package.json before you start prompting Claude. Tell Claude the version number in every prompt. This single step eliminates 90% of hallucination problems with this library — v3 and v4 have different APIs and the training data contains both.

package.json (relevant excerpt)
json
1{
2 "dependencies": {
3 "animejs": "4.0.2"
4 }
5}

The two-phase Claude workflow

@oluwaphilemon1's core insight is the order of operations: get Claude to plan the motion system before writing any animation code, and keep the HTML/CSS deliberately minimal so the motion layer has something clean to work with. A cluttered baseline DOM makes animation targeting messy, and Claude can't clean up your existing markup while simultaneously building animation timelines.

Phase 1 is structure. You describe the site, Claude gives you semantic HTML with stable class names, and minimal CSS that covers layout and typography only — no transitions, no transforms, nothing that will conflict with Anime.js later. You review it, adjust it by hand or with a follow-up prompt, and only then move to Phase 2.

Phase 2 is motion. You give Claude the exact HTML structure from Phase 1 and ask for the animation layer. Because Claude wrote Phase 1, it knows every class name and DOM shape, and the generated animations will actually target the right elements.

Here's what Phase 1 looks like for a SaaS landing page:

Phase 1 prompt
text
1I'm building a SaaS landing page using Vite (vanilla JS) and Anime.js v4.
2Generate clean, semantic HTML and minimal CSS for a page with these sections:
3- Hero: headline, subheadline, two CTAs, a dashboard mockup image placeholder
4- Features: three columns, each with an icon placeholder, heading, and two lines of text
5- Social proof: five logo placeholders in a row
6- CTA footer: one-line headline, email input, button
7
8Rules:
9- Every animated element gets a unique, descriptive class name (e.g. `.hero-headline`, `.feature-card`)
10- No CSS transitions, transforms, or opacity rules — Anime.js owns all motion
11- Elements that will animate in should start visible in the HTML so I can verify layout, then I'll set them to opacity 0 before adding animations
12- Output HTML and CSS only, no JavaScript

The output is clean enough that you can paste it into index.html and style.css without editing. The class names it generates — .hero-headline, .hero-subhead, .hero-cta-primary, .feature-card — become the vocabulary for Phase 2.

Phase 2: generating the animation layer

Once you have stable HTML, this is the prompt that generates a full motion system:

Phase 2 prompt
text
1Here is the HTML structure from Phase 1: [paste your index.html]
2
3Using Anime.js v4, generate a JavaScript file (src/animations.js) that implements:
4
51. PAGE LOAD SEQUENCE
6 - Hero headline: fade up from y:30, opacity 0→1, duration 700ms, easing 'easeOutExpo'
7 - Hero subheadline: same, 200ms delay after headline
8 - Hero CTAs: fade up, 150ms stagger, 200ms delay after subheadline
9 - Dashboard mockup: scale from 0.95→1 and fade in, 300ms after CTAs
10
112. SCROLL-TRIGGERED FEATURE CARDS
12 - Each .feature-card animates when 20% of it enters the viewport
13 - Use onScroll from Anime.js v4
14 - Stagger: 100ms between cards
15 - Motion: translateY(40px)→0, opacity 0→1, duration 600ms
16
173. SOCIAL PROOF LOGOS
18 - Continuous horizontal scroll loop using Anime.js (not CSS scroll)
19 - Speed: 40px/second
20 - Pause on hover
21
224. HOVER EFFECTS ON FEATURE CARDS
23 - On mouseenter: scale to 1.03, shadow deepens (use a CSS class toggle — don't animate box-shadow directly)
24 - On mouseleave: scale back to 1, duration 200ms
25
26Export a single `initAnimations()` function. I will call it from main.js after DOM load.

The output from this prompt is the kind of code that used to take me three hours to sequence correctly. It comes back in two minutes. The key is the specificity — easing names, exact delay values, scroll thresholds. When you give Claude vague requests ("make it feel smooth"), the output is generic. When you specify easeOutExpo at 700ms, you get exactly that.

What the generated timeline looks like

This is representative of what Claude produces for the page load sequence, and it's worth reading to understand the pattern:

src/animations.js
js
1import { animate, stagger, createTimeline, onScroll } from 'animejs';
2
3export function initAnimations() {
4 // Set initial states before animating
5 const heroEls = document.querySelectorAll(
6 '.hero-headline, .hero-subhead, .hero-cta-primary, .hero-cta-secondary, .hero-mockup'
7 );
8 heroEls.forEach(el => { el.style.opacity = '0'; });
9
10 // Page load timeline
11 const tl = createTimeline({ defaults: { ease: 'easeOutExpo' } });
12
13 tl.add('.hero-headline', {
14 translateY: [30, 0],
15 opacity: [0, 1],
16 duration: 700,
17 })
18 .add('.hero-subhead', {
19 translateY: [30, 0],
20 opacity: [0, 1],
21 duration: 600,
22 }, 200)
23 .add(['.hero-cta-primary', '.hero-cta-secondary'], {
24 translateY: [20, 0],
25 opacity: [0, 1],
26 duration: 500,
27 delay: stagger(150),
28 }, 400)
29 .add('.hero-mockup', {
30 scale: [0.95, 1],
31 opacity: [0, 1],
32 duration: 800,
33 }, 600);
34
35 // Scroll-triggered feature cards
36 document.querySelectorAll('.feature-card').forEach((card, i) => {
37 card.style.opacity = '0';
38 card.style.transform = 'translateY(40px)';
39
40 onScroll({
41 target: card,
42 enter: 'bottom 80%',
43 onEnter: () => {
44 animate(card, {
45 translateY: [40, 0],
46 opacity: [0, 1],
47 duration: 600,
48 delay: i * 100,
49 ease: 'easeOutExpo',
50 });
51 },
52 once: true,
53 });
54 });
55
56 // Hover effects on feature cards
57 document.querySelectorAll('.feature-card').forEach(card => {
58 card.addEventListener('mouseenter', () => {
59 animate(card, { scale: 1.03, duration: 200, ease: 'easeOutQuad' });
60 card.classList.add('feature-card--elevated');
61 });
62 card.addEventListener('mouseleave', () => {
63 animate(card, { scale: 1, duration: 200, ease: 'easeOutQuad' });
64 card.classList.remove('feature-card--elevated');
65 });
66 });
67}

Note what Claude does correctly here without being told: it sets initial opacity: '0' programmatically before the timeline starts, so there's no flash of visible content. That's a detail that bites people the first time they use Anime.js. The model knows it.

The SVG animation layer

This is where the workflow earns back the most time. SVG path animations — drawing lines, morphing shapes, pulsing icons — are painful to write by hand. The math for stroke-dasharray and stroke-dashoffset is fiddly. Claude handles it cleanly.

For feature section icons, here's a prompt pattern that works:

SVG animation prompt
text
1I have three feature icons as inline SVGs in my HTML. Each has a class of
2.feature-icon and its internal paths have class .icon-path.
3
4Using Anime.js v4, generate an SVG draw animation that:
5- Triggers when each icon scrolls into view (same onScroll pattern as the cards)
6- Draws each path from 0 to full length using stroke-dashoffset
7- Duration: 800ms per icon
8- Easing: easeInOutSine
9- After drawing completes, fill fades in from opacity 0 to the path's original fill color
10
11Assume each SVG path element has a CSS fill set; don't hardcode colors.
12Also include a JS helper function that sets the correct stroke-dasharray and
13stroke-dashoffset on each path before the animation runs.

The output includes the helper function that calculates getTotalLength() at runtime, which is exactly right — you can't hardcode those values because they depend on the rendered path geometry.

The minimal-base principle in practice

The most important structural decision in this workflow is restraint in Phase 1. Every CSS transition you put on an element in Phase 1 will fight Anime.js in Phase 2. Every transform: translateX() in your base CSS will confound the animation starting values. The discipline is: CSS handles color, typography, layout, and sizing. Anime.js handles all motion.

This also means keeping your HTML shallow. A feature card that has five nested wrapper divs for styling reasons is hard to animate — Anime.js targets elements and animates their transform, so the element you animate needs to be the element that moves visually. Before Phase 1, sketch which elements move and make sure they're direct parents of visible content, not intermediary layout containers.

The opacity: 0 trap

If you set opacity: 0 in CSS on an element before animating it in, screen readers will skip it and it'll be invisible if JavaScript fails to load. Set the initial state programmatically in JavaScript immediately before the animation, not in the stylesheet. The code above does this correctly — check that Claude's output follows the same pattern, and add a no-js fallback class via <script>document.documentElement.classList.remove('no-js')</script> in your <head> if accessibility matters for your project.

Iterating with follow-up prompts

The first pass from Claude is rarely final. The timelines are correct but the feel might be off — too fast, too stiff, wrong easing for the brand. The follow-up prompt pattern is simple:

Iteration prompt
text
1The page load timeline feels too mechanical.
2Current: all elements use easeOutExpo at 700ms.
3Target feel: "premium but playful, like Linear's homepage"
4Adjust the easing and duration values. Keep all the delay offsets the same.
5Also: the feature cards feel too jumpy — reduce translateY from 40 to 20 and
6increase duration from 600 to 800ms.

Referencing a real site as a feel benchmark works better than adjectives alone. "Like Linear" or "like Stripe's homepage" gives Claude a concrete visual vocabulary to draw from. It knows what those sites feel like in motion.

After two or three iteration rounds, the diff between what Claude generates and what you'd ship is small enough to hand-edit directly. At that point you own the animation file and understand every value in it.

What this doesn't replace

Claude cannot watch your browser. It doesn't know that your .hero-mockup image is 2400px wide and causes layout shift on mobile, or that the social proof scroll animation glitches in Safari because of a compositing bug. The motion system it generates is correct JavaScript, but visual QA is yours. Test on real devices, check the Chrome Performance panel for jank (you want 60fps with no main thread blocking during animations — Anime.js uses requestAnimationFrame so this is usually fine), and verify that prefers-reduced-motion is respected.

For the reduced motion case, one additional prompt finishes the job:

Reduced motion prompt
text
1Add a prefers-reduced-motion check at the top of initAnimations().
2If the user has reduced motion enabled, skip all translate/scale animations
3and only run the opacity fade-ins at 50% speed.

Claude will wrap the motion-heavy parts in if (!window.matchMedia('(prefers-reduced-motion: reduce)').matches) correctly.

  1. 1

    Scaffold the project and pin Anime.js v4

    bash
    1npm create vite@latest motion-site -- --template vanilla
    2cd motion-site && npm install
    3npm install animejs@4.0.2

    Add "animejs": "4.0.2" explicitly to your package.json so Claude always knows which API to target.

  2. 2

    Run Phase 1: generate the base HTML and CSS

    Use the Phase 1 prompt from the workflow above. Tell Claude the site type, the sections, and the element names you want. Review the output in the browser — at this point everything should be visible and laid out correctly. Make any structural edits now, before you add motion.

  3. 3

    Run Phase 2: generate the animation file

    Paste your final index.html into the Phase 2 prompt. Specify every animation with concrete values: easing name, duration in ms, delay, translate distance. Ask Claude to export a single initAnimations() function. Import it in main.js and call it on DOMContentLoaded.

  4. 4

    Iterate on feel with benchmark references

    Run the dev server, watch the animations, and identify what feels wrong. Send a follow-up prompt describing the delta — "too fast," "wrong easing" — and name a reference site whose motion you want to emulate. Two or three rounds is usually enough to get to something you'd ship.

  5. 5

    Add SVG draw animations and the reduced motion fallback

    Use the SVG animation prompt pattern for any icon or illustration paths. Then run the reduced motion prompt to wrap everything correctly. Ship.

Key Takeaways

  • Pin Anime.js to an exact version before prompting Claude. v3 and v4 have different APIs and Claude knows both — ambiguity produces wrong code.
  • The two-phase order matters: HTML structure first, animation layer second. Never mix them in one prompt.
  • Give Claude concrete values in every animation prompt — easing name, duration, delay, translate distance. Vague prompts return generic output.
  • Keep CSS out of the motion domain entirely. CSS handles layout and color. Anime.js handles all transforms and opacity.
  • Set initial animation states (opacity: 0) programmatically in JavaScript, not in CSS, to preserve accessibility and graceful degradation.
  • Reference real sites (Linear, Stripe) as feel benchmarks in iteration prompts. It works better than adjectives.
Last Updated

May 9, 2026

Category

Stack

Omid Saffari

Founder & CEO, AI Entrepreneur

Digital marketing specialist with expertise in AI, automation, and web development. Helping businesses build strong online presences that drive results.

X.com
Instagram
LinkedIn
WhatsApp
Email

More from Stack

Three Weeks with Claude Opus 4.7 in Production Agent Loops: What Actually Changed vs Sonnet 4.6
Three Weeks with Claude Opus 4.7 in Production Agent Loops: What Actually Changed vs Sonnet 4.6

Real cost-per-completion numbers, retry rates, and tool-call accuracy from three weeks of Opus 4.7 vs Sonnet 4.6 inside a six-agent Cloudflare Durable Object stack with a hard $20/day cap and per-call USD logging.

May 10, 2026
Production Claude Code in May 2026: the Skills, Hooks, and Orchestration Layout That Actually Ships
Production Claude Code in May 2026: the Skills, Hooks, and Orchestration Layout That Actually Ships

Claude Code 2.1.130-138 shipped skills effort levels, plugin URL flags, and MCP OAuth fixes in nine days. Here is the production .claude/ directory layout, the hooks pattern that catches 80% of QA failures, and where the whole thing breaks.

May 10, 2026
View all Stack articles