
The variable weight menu
Menus, Text, Hover, Physics
NewFree
The card of an essay in a small online magazine, with a single illustration: a vermilion moon set in the text. The pointer carries it through the excerpt (a finger drags it or puts it where it taps, the arrow keys move it) and every line makes room for it: the text is laid out by the script line by line around the circle, the words glide to their new places on springs without ever bumping, those that wrap come in from the right side, and the excerpt ends with an ellipsis when the moon takes too much room. Crossing the column the moon changes phase, a crescent on the left and full on the right.
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>The article card that makes room</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/newsreader@5.3.0/standard.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/inter-tight@5.3.0/index.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="stage">
<!-- the card of an essay in a small online magazine. Its only picture is a moon, set in the text: the pointer (or a
finger, or the arrow keys) carries it through the excerpt and every line makes room for it, the words
gliding to their new places on springs, the nearest first. The moon changes phase as it crosses the column,
from a crescent on the left to full on the right. The words are laid out by the script, line by line around
the moon -->
<article class="essay" aria-labelledby="essay-title" data-essay>
<p class="essay__meta"><span class="essay__mag">Nightfall</span><span>Essay · 6 min read</span></p>
<h2 class="essay__title" id="essay-title">Everything leans toward the moon</h2>
<div class="essay__body" data-body>
<p class="sr" data-text>The sea is not pulled up so much as gathered. Twice a day the water leans toward the moon, and the whole coast leans with it: the harbour empties, the boats settle on their keels, the gulls walk where an hour ago they swam. Nothing here was asked to move, and everything did. I have lived by this water for eleven years and I still check the tide table before I check the weather, because the weather only changes the day, and the moon changes the shape of the town. This is an essay about living somewhere that rearranges itself around a light in the sky.</p>
<div class="words" aria-hidden="true" data-words></div>
<button class="moon" type="button" data-moon aria-label="The moon. Move it through the text with the arrow keys">
<svg viewBox="-60 -60 120 120" aria-hidden="true">
<circle class="moon__ring" r="48" data-ring />
<circle class="moon__dark" r="38" data-dark />
<path class="moon__lit" data-lit />
</svg>
</button>
</div>
<footer class="essay__foot">
<span class="essay__by">Words by Ines Marlow</span>
<a class="read" href="#essay-title" data-read>
<span class="read__word">Read the essay</span>
<svg viewBox="0 0 18 12" aria-hidden="true"><path d="M1 6h15M11.5 1.5 16 6l-4.5 4.5" /></svg>
</a>
</footer>
</article>
</main>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/gsap.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.
23 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 “The article card that makes room” from Animorio and fit it into my siteThe real text sits in a paragraph hidden for screen readers; the script makes one span per word, measures them once in their font (it waits for Newsreader with document.fonts.load) and measures the space and the ellipsis too. From then on the layout is only arithmetic, quick enough to do again at every frame.
For each line the script finds where the moon, widened by ROOM, is widest within the line’s height: the chord of the circle at the smallest distance from its centre. The line becomes one or two pieces, left and right of the moon, and pieces narrower than 30 pixels are skipped. The words fill the pieces in reading order; if they do not all fit the last piece is filled again keeping room for the ellipsis.
Each word has a spring towards its place, stiffer near the moon and softer far away (RIPPLE), so the text gives way from the moon outwards. After each step a constraint keeps the words of the same piece in order, half a space apart and inside the piece, with one pass forwards and one backwards: they squeeze but never overlap.
A word that changes line, with a single spring, would fly diagonally over the others: instead it jumps at once to the new line, sixteen pixels before or after its place (on the side it was pushed from), at 0.3 opacity, and from there the spring takes it to its place as it comes back to full.
The moon is a button with an SVG: a disc in shadow and the lit part, a path of two arcs (half a circle and the terminator, an ellipse with horizontal radius |1 − 2f|·R). The lit fraction f goes from 22% on the left to 100% on the right (PHASES). It follows the pointer on a spring (FOLLOW) and, when nobody holds it, goes back to the top right; the GSAP ticker stops when the moon and the words are still.
Tab reaches the moon (its circle becomes a solid line): the arrows move it 14 pixels, 40 with Shift, Escape and Home send it back. A finger drags it (touch-action: none only on the moon, the rest of the card scrolls the page) or taps the text and the moon goes there; a tap outside the card sends it back.
| Name | File | Default | What it changes |
|---|---|---|---|
RADIUS | script.js | 36 | The moon’s radius, in pixels. |
ROOM | script.js | 10 | The room the words leave around the moon, in pixels. |
FOLLOW | script.js | [120, 0.8] | The moon’s spring: how stiff it is and how much it is held back (1 = no bounce). |
WORDS | script.js | [320, 0.72] | Each word’s spring near the moon: stiffness and damping. |
RIPPLE | script.js | 0.3 | How much softer the springs of the words far from the moon are (0 = all the same). |
PHASES | script.js | true | The moon changes phase as it crosses the column; off, it is always full. |
LINES | script.js | 9 | How many lines the excerpt has. |
For an article preview on a blog, in an online magazine or a studio newsletter: the card of an essay, a short story, an interview. The illustration can be any round shape: a plate for a recipe, a record for a review, a sun for a travel diary.
The card is 340 pixels wide, or the screen minus 32, and the layout is redone on the real width. A finger drags the moon, and the page underneath stops scrolling only when the finger starts on the moon; a tap on the text takes it there and it stays until you tap outside the card. The springs are the same as on a computer.
The moon does not follow the pointer: it moves with a click on the text, a finger or the arrow keys, and the text is laid out at once behind a short fade, already in place. No springs and no entrance.
GSAP 3.15.0, Newsreader (font variabile) 5.3.0, Inter Tight (font variabile) 5.3.0.
Compressed with gzip, the three demo files weigh 8.0 KB; the libraries: GSAP 28 KB.
Tested on 19 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.