
Words that melt into each other
Text, Scroll, SVG
NewFree
Scroll inside the preview
A method in four steps in a section that holds still while you scroll: at every step the text changes, the drawing leaves to the right and the new one comes in from there, two stars spin three turns, and in the bar of pills the one of the current step stretches to half the bar and fills up as you scroll.
A folder that runs on its own: index.html, style.css, script.js and the libraries from a CDN. The React and Vue tabs hold the same demo as a ready component, which switches its animations off when it leaves the page.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Pill steps</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/bricolage-grotesque@5.3.0/index.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource/dm-serif-display@5.3.0/400-italic.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<header class="bar">
<a class="brand" href="#">Greenfold Gardens</a>
<nav class="bar__nav" aria-label="Sections">
<a href="#">Gardens</a>
<a href="#">Method</a>
<a href="#">Contact</a>
</nav>
</header>
<section class="intro">
<h1 class="intro__title">Gardens that grow <em>with you.</em></h1>
<p class="intro__hint">Scroll through how we work.</p>
</section>
<!-- the steps: the section holds still while you scroll, and changes step every window height -->
<section class="steps" data-steps>
<div class="steps__inner">
<div class="copy">
<p class="copy__kicker">How we work</p>
<div class="copy__texts">
<div class="copy__text" data-text>
<h2>We <em>plan</em> on paper first.</h2>
<p>A visit, a tape measure and a cup of tea: we draw the garden you have, then the one you want, bed by bed.</p>
</div>
<div class="copy__text" data-text>
<h2>Then we <em>plant</em>, by hand.</h2>
<p>Native plants for the shade, hardy ones for the wind, and the soil turned before anything goes in.</p>
</div>
<div class="copy__text" data-text>
<h2>We come back to <em>tend</em>.</h2>
<p>Every season for the first two years: watering, pruning, and a list of what to watch out for.</p>
</div>
<div class="copy__text" data-text>
<h2>And you <em>harvest</em>.</h2>
<p>Beans in June, tomatoes in August, and a garden that by now knows how to look after itself.</p>
</div>
</div>
<!-- the pills: the step you are on is wide, the others narrow; inside each one a bar fills while you scroll its step -->
<div class="pills" role="list" aria-label="Steps">
<button class="pill" type="button" role="listitem" data-pill><i class="pill__fill" data-fill></i><span>Plan</span></button>
<button class="pill" type="button" role="listitem" data-pill><i class="pill__fill" data-fill></i><span>Plant</span></button>
<button class="pill" type="button" role="listitem" data-pill><i class="pill__fill" data-fill></i><span>Tend</span></button>
<button class="pill" type="button" role="listitem" data-pill><i class="pill__fill" data-fill></i><span>Harvest</span></button>
</div>
</div>
<div class="stage" aria-hidden="true">
<!-- the two stars that spin at every change -->
<svg class="star star--a" data-star viewBox="0 0 100 100"><path d="M50 2c5 24 14 33 38 38-24 5-33 14-38 38-5-24-14-33-38-38C36 35 45 26 50 2z" fill="#f2b84b"/></svg>
<svg class="star star--b" data-star viewBox="0 0 100 100"><path d="M50 2c5 24 14 33 38 38-24 5-33 14-38 38-5-24-14-33-38-38C36 35 45 26 50 2z" fill="#e2683c"/></svg>
<!-- plan: a sheet with the beds drawn on it, and a pencil -->
<svg class="art" data-art viewBox="0 0 400 400">
<g transform="rotate(-6 200 200)">
<rect x="70" y="60" width="260" height="290" rx="14" fill="#fbf6ea"/>
<rect x="96" y="90" width="92" height="62" rx="10" fill="#9bd18a"/>
<rect x="204" y="90" width="100" height="62" rx="10" fill="#58a36b"/>
<rect x="96" y="168" width="208" height="20" rx="10" fill="#e9dcc2"/>
<rect x="96" y="204" width="60" height="118" rx="10" fill="#58a36b"/>
<circle cx="214" cy="248" r="30" fill="#2f6b4f"/>
<circle cx="274" cy="282" r="22" fill="#9bd18a"/>
<path d="M96 336h208" stroke="#d9c7a4" stroke-width="3" stroke-dasharray="8 8"/>
</g>
<g transform="rotate(34 320 300)">
<rect x="300" y="190" width="26" height="170" rx="6" fill="#f2b84b"/>
<path d="M300 360h26l-13 30z" fill="#f3dcb0"/>
<path d="M309 380h8l-4 10z" fill="#2a2620"/>
<rect x="300" y="190" width="26" height="22" rx="6" fill="#e2683c"/>
</g>
</svg>
<!-- plant: a mound of soil, a seedling and a trowel -->
<svg class="art" data-art viewBox="0 0 400 400">
<ellipse cx="200" cy="300" rx="150" ry="46" fill="#8a5a3c"/>
<ellipse cx="200" cy="290" rx="120" ry="30" fill="#a8714b"/>
<path d="M200 290c0-60 4-100 0-140" fill="none" stroke="#2f6b4f" stroke-width="10" stroke-linecap="round"/>
<path d="M200 200c-40 0-70-20-80-56 40-4 72 14 80 56z" fill="#58a36b"/>
<path d="M202 170c34-6 64-32 70-66-38 0-66 22-70 66z" fill="#9bd18a"/>
<g transform="rotate(-28 300 220)">
<rect x="288" y="120" width="22" height="90" rx="10" fill="#e2683c"/>
<path d="M282 206h34l-4 70c-2 14-24 14-26 0z" fill="#b9c3c6"/>
</g>
</svg>
<!-- tend: a watering can with its drops -->
<svg class="art" data-art viewBox="0 0 400 400">
<g transform="rotate(-14 200 200)">
<path d="M120 150h150l-12 140c-2 16-14 26-30 26h-66c-16 0-28-10-30-26z" fill="#3f8ab0"/>
<path d="M270 190l80-56 10 14-74 64z" fill="#3f8ab0"/>
<rect x="340" y="120" width="34" height="22" rx="6" transform="rotate(-36 357 131)" fill="#2d6f90"/>
<path d="M140 150c0-50 36-70 64-70s64 20 64 70" fill="none" stroke="#2d6f90" stroke-width="16" stroke-linecap="round"/>
<rect x="120" y="150" width="150" height="20" fill="#2d6f90"/>
</g>
<g fill="#7cc4e6">
<path d="M330 220c8 14 12 22 12 28a12 12 0 0 1-24 0c0-6 4-14 12-28z"/>
<path d="M360 262c6 10 9 16 9 21a9 9 0 0 1-18 0c0-5 3-11 9-21z"/>
<path d="M318 290c6 10 9 16 9 21a9 9 0 0 1-18 0c0-5 3-11 9-21z"/>
</g>
<path d="M290 360c0-24 4-40 0-56M340 362c0-18 3-30 0-42" fill="none" stroke="#58a36b" stroke-width="8" stroke-linecap="round"/>
<path d="M290 330c-20 0-30-10-34-26 18-2 32 8 34 26zM340 338c16-2 26-12 28-26-16 0-26 10-28 26z" fill="#9bd18a"/>
</svg>
<!-- harvest: a basket with carrots and tomatoes -->
<svg class="art" data-art viewBox="0 0 400 400">
<path d="M120 150l24-50h22l-16 50zM280 150l-24-50h-22l16 50z" fill="#2f6b4f"/>
<path d="M150 150l-30-70 18-6 26 76zM248 150l14-74 18 4-16 70z" fill="#58a36b"/>
<path d="M120 140l40 118 14-118zM206 140l34 120 30-120z" fill="#e2683c"/>
<circle cx="196" cy="168" r="40" fill="#d43d2f"/>
<path d="M196 132l8 10 12-4-6 12 10 8-14 2-2 12-8-10-8 10-2-12-14-2 10-8-6-12 12 4z" fill="#58a36b"/>
<circle cx="272" cy="182" r="30" fill="#e8553f"/>
<path d="M70 180h260l-26 150c-4 18-18 30-36 30H132c-18 0-32-12-36-30z" fill="#c89257"/>
<path d="M84 220h232M92 260h216M100 300h200" stroke="#a87439" stroke-width="7"/>
<path d="M130 180l14 180M200 180v180M270 180l-14 180" stroke="#a87439" stroke-width="7"/>
<rect x="62" y="168" width="276" height="26" rx="13" fill="#b07a42"/>
</svg>
</div>
</div>
</section>
<section class="outro">
<h2 class="outro__title">Ready when <em>the soil is.</em></h2>
<a class="outro__link" href="#">Book a garden visit</a>
</section>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/gsap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/ScrollTrigger.min.js"></script>
<script src="script.js"></script>
</body>
</html>Change values, colours and text with the controls and watch the animation respond live. Or write in the code yourself. Then download the folder with your changes.
42 values to change
With the Animorio connector, Claude Code takes it from here, guide included, and fits it into your project. Paste this request:
Take “Pill steps” from Animorio and fit it into my siteA ScrollTrigger pins the section for LENGTH window heights for every step after the first, plus half. The step is the progress times the number of steps, rounded down: it changes only when you cross the border, and every change starts some tweens.
The four texts sit on top of each other in the same grid cell, so the section does not jump when they change. The leaving one rises 20 pixels and vanishes in 0.3 seconds; the new one comes from 24 pixels lower in 0.55, with power3.out, a moment later.
The drawings are four SVGs on top of each other. The leaving one slides SLIDE pixels to the right while vanishing, with power2.in; the new one arrives from the same distance in 0.75 seconds with power3.out. Those are the original’s 400 pixels, where the unicorn appears in place, though, and slides to the right only to leave.
At every change the two stars turn by SPIN degrees, 1080 as on the site, in 1.1 seconds with power2.inOut, one one way and one the other, and meanwhile they start again from 0.6 of their size with a little bounce.
The pills’ width is a percentage of the bar: WIDE for the current step, NARROW for the others, in PILLS seconds with expo.in, like the original (49%, 15%, half a second). The name appears in the wide pill once it has finished stretching. Inside every pill a bar has its horizontal scale tied to the scroll: full for the steps behind you, empty for those ahead, and for the current step as full as you have scrolled through it. A pressed pill scrolls the page to the middle of its step’s stretch.
| Name | File | Default | What it changes |
|---|---|---|---|
WIDE | script.js | 49 | The width of the current step’s pill, as a percentage of the bar. |
NARROW | script.js | 15 | The width of the other pills, as a percentage. |
PILLS | script.js | 0.5 | The seconds the pills take to change. |
SLIDE | script.js | 400 | How far to the right the drawings come from and go to, in pixels. |
SPIN | script.js | 1080 | How far the stars turn at every change, in degrees. |
LENGTH | script.js | 1 | How much scrolling each step takes, in window heights. |
For a method, a journey or a service in stages: four short steps, each with its drawing. With more than five steps the narrow pills get too small and the section too long.
The drawing goes on top and the text below, the title is smaller and the pills lower. Scrolling with a finger changes step like the wheel, and the pills can be tapped.
No sliding and no spinning: text and drawing change with a fade, the stars hold still and the pills change width at once. The bar inside the pills still follows the scroll: it tells you where you are, it is not extra motion.
GSAP 3.15.0, Bricolage Grotesque (font variabile) 5.3.0, DM Serif Display 5.3.0.
Compressed with gzip, the three demo files weigh 6.3 KB; the libraries: GSAP + ScrollTrigger 45 KB.
Tested on 2 October 2026
Use it in your own projects and in your clients’ projects. Do not resell it or redistribute it as is. GSAP stays under its own Standard license. Full license.
With your account’s email and password.
or with your email
You’re signed in as .
With your account, you can download the zip of every animation and every pack, for free.