
The vacuum-sealed chat
Chat, Physics, SVG, Transitions, Text
NewFree
The sidebar of a plant nursery's app, where nothing slides and nothing fades: everything rolls up and unrolls like fern fronds. The names wind letter by letter into a tighter and tighter spiral, the highlight unrolls out of the chosen page's icon with its tip still curled, the line of the greenhouses unrolls downwards and each greenhouse unrolls its name as the line reaches it; folding the bar, every name rolls up into its icon.
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 curling sidebar</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/commissioner@5.3.0/index.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="stage">
<!-- the sidebar of a plant nursery's desktop app. Nothing in it slides or fades: the names, the highlight under
the page chosen and the line of the greenhouses roll up and unroll like the fronds of a fern. The script
draws the moving parts (the letters, the highlight, the line) on the two layers of the body; the names in the
rows stay for screen readers -->
<div class="fr-wrap" data-wrap>
<nav class="fr" aria-label="Nursery" data-nav data-autoclic>
<div class="fr__head">
<button class="fr__toggle" type="button" aria-expanded="true" aria-controls="fr-list" aria-label="Collapse sidebar" data-toggle>
<svg viewBox="0 0 24 24" aria-hidden="true"><rect x="3" y="4.5" width="18" height="15" rx="3.2"/><path d="M9 4.5V19.5"/><path class="fr__toggle-chev" d="M15.6 9.6L13.2 12L15.6 14.4"/></svg>
</button>
</div>
<div class="fr__body" data-body>
<!-- under the rows: the highlights and the line of the greenhouses -->
<svg class="fr__ground" aria-hidden="true" data-ground>
<defs>
<linearGradient id="fr-frond" gradientUnits="userSpaceOnUse" x1="0" y1="0" x2="236" y2="0">
<stop class="fr__stop-base" offset="0"/>
<stop class="fr__stop-tip" offset="1"/>
</linearGradient>
<clipPath id="fr-clip-ground"><rect data-clip/></clipPath>
</defs>
<g data-ribbons></g>
<g clip-path="url(#fr-clip-ground)" data-ribbons-sub></g>
<path class="fr__guide" d="M0 0" data-guide/>
</svg>
<ul class="fr__list" id="fr-list">
<li><a class="fr__item" href="#" data-item>
<svg class="fr__icon" viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="3.9"/><path d="M12 2.8V4.9M12 19.1V21.2M2.8 12H4.9M19.1 12H21.2M5.5 5.5L7 7M17 17L18.5 18.5M5.5 18.5L7 17M17 7L18.5 5.5"/></svg>
<span class="fr__label" data-label>Today</span></a></li>
<li><a class="fr__item" href="#" aria-current="page" data-item>
<svg class="fr__icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M6.8 14.2H17.2L16 20.2C15.9 20.7 15.5 21 15 21H9C8.5 21 8.1 20.7 8 20.2Z"/><path d="M12 14.2V9.6"/><path d="M12 11.2C12 7.9 9.9 5.9 6.4 5.9C6.4 9.2 8.5 11.2 12 11.2Z"/><path d="M12 9.6C12 6.6 13.9 4.5 17.4 4.5C17.4 7.6 15.4 9.6 12 9.6Z"/></svg>
<span class="fr__label" data-label>Plants</span></a></li>
<li class="fr__group">
<button class="fr__item fr__item--parent" type="button" aria-expanded="true" aria-controls="fr-houses" data-item data-parent>
<svg class="fr__icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M3.6 20.4V10.8L12 4.2L20.4 10.8V20.4Z"/><path d="M12 4.2V20.4M3.6 14.8H20.4M7.8 7.5V20.4M16.2 7.5V20.4"/></svg>
<span class="fr__label" data-label>Greenhouses</span>
</button>
<ul class="fr__sub" id="fr-houses" data-sub>
<li><a class="fr__item fr__item--child" href="#" data-item><span class="fr__label" data-label>North house</span></a></li>
<li><a class="fr__item fr__item--child" href="#" data-item><span class="fr__label" data-label>Propagation</span></a></li>
<li><a class="fr__item fr__item--child" href="#" data-item><span class="fr__label" data-label>Shade tunnel</span></a></li>
</ul>
</li>
<li><a class="fr__item" href="#" data-item>
<svg class="fr__icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M4.4 10.2H12.8V18.4C12.8 19.6 11.8 20.6 10.6 20.6H6.6C5.4 20.6 4.4 19.6 4.4 18.4Z"/><path d="M5.8 10.2C5.8 5.4 11.4 5.4 11.4 10.2"/><path d="M12.8 15.6L19 10"/><path d="M17.8 8.6L20.4 11.4"/></svg>
<span class="fr__label" data-label>Watering</span>
<span class="fr__count" data-count>3<span> plants to water</span></span></a></li>
<li><a class="fr__item" href="#" data-item>
<svg class="fr__icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M12 3.6L20 7.6V16.4L12 20.4L4 16.4V7.6Z"/><path d="M4 7.6L12 11.6L20 7.6M12 11.6V20.4M8 5.6L16 9.6"/></svg>
<span class="fr__label" data-label>Orders</span></a></li>
<li><a class="fr__item" href="#" data-item>
<svg class="fr__icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M6.6 3.6H17.4C18.4 3.6 19.2 4.4 19.2 5.4V18.6C19.2 19.6 18.4 20.4 17.4 20.4H6.6C5.6 20.4 4.8 19.6 4.8 18.6V5.4C4.8 4.4 5.6 3.6 6.6 3.6Z"/><path d="M8.6 3.6V20.4"/><path d="M12.4 8.2H16M12.4 11.4H15"/></svg>
<span class="fr__label" data-label>Journal</span></a></li>
</ul>
<ul class="fr__list fr__list--foot">
<li><a class="fr__item" href="#" data-item>
<svg class="fr__icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M4 7.5H8.2M13 7.5H20M4 16.5H11M15.8 16.5H20"/><path d="M10.6 5.2C11.9 5.2 13 6.2 13 7.5S11.9 9.8 10.6 9.8 8.2 8.8 8.2 7.5 9.3 5.2 10.6 5.2Z"/><path d="M13.4 14.2C14.7 14.2 15.8 15.2 15.8 16.5S14.7 18.8 13.4 18.8 11 17.8 11 16.5 12.1 14.2 13.4 14.2Z"/></svg>
<span class="fr__label" data-label>Settings</span></a></li>
</ul>
<!-- over the rows: the letters, the count and the arrow of the greenhouses -->
<svg class="fr__ink" aria-hidden="true" data-ink>
<defs><clipPath id="fr-clip-ink"><rect data-clip/></clipPath></defs>
<g data-words></g>
<g clip-path="url(#fr-clip-ink)" data-words-sub></g>
</svg>
</div>
</nav>
<!-- the name of an icon, beside the folded bar -->
<svg class="fr__tags" aria-hidden="true" data-tags></svg>
</div>
</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.
34 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 curling sidebar” from Animorio and fit it into my siteEvery moving part (a name, the pill, the line of the greenhouses, the tag) is a strip of length L with a front f. Along the strip u is a length of name: up to the front it is straight and full size; past the front it turns by 1/COIL radians for every pixel of name while the size falls as 2^(−(u − f)/TIGHT), and the step along the curve is the size times du. So the radius falls with the letters and the tip is a logarithmic spiral, a fern's fiddlehead, with the letters packed tight all the way in. The points are added up from the start in steps of 2 pixels, with the turn and the size taken in the middle of each step, only on the frames the strip moves.
The front moves with c, from 0 (straight) to 1 (all in the spiral): f = L·(1 − c). Unrolling, c follows a spring damped so that it overshoots zero by FLICK (the damping comes from −ln FLICK / √(π² + ln² FLICK)), and while c is below zero the last stretch bends the other way: the tip flicks. Rolling up the spring is critically damped and stops at the spiral; in the last hundredths the spiral shrinks into the front and is gone. The spring takes four steps a frame, with the frame capped at 1/30 of a second, and stops once the front is within a twentieth of a pixel.
Each name is split into SVG letters measured with a canvas in the same typeface (the width of each prefix, so the kerning stays), and each letter stands on the strip where its middle falls: moved to the point, turned with the curve and as big as the spiral is there. The 3 and Greenhouses' arrow are the last letters of their strip, which runs to the end of the row: they go into the spiral first and fade early, before they turn into dots. The real names stay in the rows, hidden from view, for screen readers.
The highlight is a wide strip drawn as a band: full up to the front, then it rounds off like the end of a pill down to a thin stem that rolls up and shrinks with the spiral; the stem only starts to turn at the end of the round end. One is needed for the page chosen and one for the row under the mouse, which stops PEEK pixels past the name; the ones let go roll up, shrink from the root and are thrown away. Folded, the front stops at 4 pixels: what is left is the round end, a bud round the icon.
The line of the greenhouses is a strip that comes down from the icon and rolls up to the right; the section is as tall as the line has unrolled, and a house's name unrolls when the line's front passes its height. Hidden houses are inert. Folding, the names roll up from the top with STAGGER seconds between them (the ones with the 3 or the arrow a little earlier, as they have the empty stretch to cross) and every frame the bar is as wide as the rightmost point still unrolled; opening again, the width comes back on a spring and the names unroll behind it.
The pages are links with aria-current, Greenhouses is a button with aria-expanded and aria-controls, the button at the top has aria-expanded and says Collapse or Expand sidebar; everything works with Tab and has the green focus ring. Folded, the mouse or Tab on an icon unrolls its name in a dark tag beside the bar (after a tap it stays 1.3 seconds), Esc rolls it up. A click from a script on the bar (the library's preview) plays the next step of a tour: Watering, Propagation, close the greenhouses, fold, Journal with its name, open, open the greenhouses, Plants.
| Name | File | Default | What it changes |
|---|---|---|---|
UNFURL | script.js | 0.6 | The seconds a name takes to unroll, up to the flick of its tip. |
CURL | script.js | 0.42 | The seconds a name takes to roll back up. |
STAGGER | script.js | 0.04 | The seconds between one row and the next as the bar folds or opens. |
COIL | script.js | 9 | The radius of the outer turn of a roll, in pixels. |
TIGHT | script.js | 30 | The pixels of name over which a roll gets half as big: less closes it sooner. |
FLICK | script.js | 0.18 | How far the tip flicks past straight as it unrolls (0: it just stops). |
PEEK | script.js | 10 | How far the grey pill of a row under the mouse unrolls, in pixels past the name (0: none). |
For the sidebar of a desktop app with character: a nursery or plant app, a journal, a creative tool, a home or garden app, the dashboard of a shop that wants a signature of its own.
The bar is 256 pixels wide open and 64 folded, 520 tall: at 390 pixels it fits the screen whole. With no hover there is no grey pill; folded, a tap on an icon chooses the page and unrolls its name for 1.3 seconds.
Nothing rolls: the names, the line of the greenhouses and the tags appear and disappear with a fade, the pill moves from row to row by fading, the greenhouses' section opens at once and the bar narrows only once the names are gone, with the pill turning into the bud; on arrival everything is already in place.
GSAP 3.15.0, Commissioner (font variabile) 5.3.0.
Compressed with gzip, the three demo files weigh 14 KB; the libraries: GSAP 28 KB.
Tested on 6 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.