
The inchworm menu
Menus, Hover, Transitions
NewFree
The app switcher of an office suite: the nine little squares of the button fly out one after the other, each on its own curve, and grow into the nine app icons, taking their colour on the way. Closing, they fly back into the button and are its little squares again.
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 app grid menu</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/geist@5.3.0/index.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="stage">
<!-- the app switcher of a work suite. The button carries the usual nine little squares; pressed, the nine squares
fly out of it, each on its own curve, and grow into the nine app icons of the menu, taking their colour on the
way. Closing, they fly back into the button and are its icon again -->
<div class="suite" data-frame>
<button class="apps" type="button" aria-expanded="false" aria-controls="app-menu" data-toggle data-autoclic>
<span class="apps__grid" aria-hidden="true" data-mini-grid>
<i data-mini></i><i data-mini></i><i data-mini></i>
<i data-mini></i><i data-mini></i><i data-mini></i>
<i data-mini></i><i data-mini></i><i data-mini></i>
<svg class="apps__cross" viewBox="0 0 16 16" data-cross><path d="M3.5 3.5l9 9M12.5 3.5l-9 9"/></svg>
</span>
<span class="apps__words" aria-hidden="true">
<span class="apps__word" data-word-open>Apps</span>
<span class="apps__word" data-word-close>Close</span>
</span>
<span class="sr" data-toggle-name>Open the apps</span>
</button>
<nav class="board" id="app-menu" aria-label="Apps" data-board inert>
<span class="board__ground" aria-hidden="true" data-ground></span>
<ul class="board__grid" data-board-grid>
<li><a class="tile" href="#mail" data-tile><span class="tile__icon" style="--tint: #3b6cf6" data-icon><svg viewBox="0 0 24 24"><rect x="3.5" y="6" width="17" height="12" rx="2"/><path d="M4 7l8 6 8-6"/></svg></span><span class="tile__name">Mail</span></a></li>
<li><a class="tile" href="#calendar" data-tile aria-current="page"><span class="tile__icon" style="--tint: #e8513f" data-icon><svg viewBox="0 0 24 24"><rect x="4" y="5.5" width="16" height="14" rx="2"/><path d="M4 10h16M8.5 3.5v4M15.5 3.5v4M8 14h3"/></svg></span><span class="tile__name">Calendar</span></a></li>
<li><a class="tile" href="#files" data-tile><span class="tile__icon" style="--tint: #e5952a" data-icon><svg viewBox="0 0 24 24"><path d="M3.5 7.5v10a2 2 0 0 0 2 2h13a2 2 0 0 0 2-2v-8a2 2 0 0 0-2-2h-6.5l-2-2.5h-4.5a2 2 0 0 0-2 2z"/></svg></span><span class="tile__name">Files</span></a></li>
<li><a class="tile" href="#notes" data-tile><span class="tile__icon" style="--tint: #c9a227" data-icon><svg viewBox="0 0 24 24"><path d="M6.5 3.5h8l4 4v12a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1v-15a1 1 0 0 1 1-1z"/><path d="M14.5 3.5v4h4M8.5 12h7M8.5 15.5h5"/></svg></span><span class="tile__name">Notes</span></a></li>
<li><a class="tile" href="#chat" data-tile><span class="tile__icon" style="--tint: #22a06b" data-icon><svg viewBox="0 0 24 24"><path d="M5 5.5h14a1.5 1.5 0 0 1 1.5 1.5v8.5a1.5 1.5 0 0 1-1.5 1.5h-8l-4.5 3.5v-3.5h-1.5a1.5 1.5 0 0 1-1.5-1.5v-8.5a1.5 1.5 0 0 1 1.5-1.5z"/></svg></span><span class="tile__name">Chat</span></a></li>
<li><a class="tile" href="#photos" data-tile><span class="tile__icon" style="--tint: #8b5cf6" data-icon><svg viewBox="0 0 24 24"><rect x="3.5" y="5" width="17" height="14" rx="2"/><path d="M3.5 16l5-5 4 4 2.5-2.5 5.5 5.5"/><circle cx="15.5" cy="9" r="1.6"/></svg></span><span class="tile__name">Photos</span></a></li>
<li><a class="tile" href="#tasks" data-tile><span class="tile__icon" style="--tint: #1597a8" data-icon><svg viewBox="0 0 24 24"><rect x="4" y="4" width="16" height="16" rx="3"/><path d="M8 12.5l2.5 2.5 5.5-6"/></svg></span><span class="tile__name">Tasks</span></a></li>
<li><a class="tile" href="#meet" data-tile><span class="tile__icon" style="--tint: #ec6a2c" data-icon><svg viewBox="0 0 24 24"><rect x="3.5" y="7" width="12" height="10" rx="2"/><path d="M15.5 10.5l5-3v9l-5-3"/></svg></span><span class="tile__name">Meet</span></a></li>
<li><a class="tile" href="#settings" data-tile><span class="tile__icon" style="--tint: #5b6170" data-icon><svg viewBox="0 0 24 24"><path d="M4 7h9M17 7h3M4 12h3M11 12h9M4 17h11M19 17h1"/><path d="M15 5v4M9 10v4M17 15v4"/></svg></span><span class="tile__name">Settings</span></a></li>
</ul>
<a class="board__all" href="#all" data-all>All apps<svg viewBox="0 0 16 16" aria-hidden="true"><path d="M3 8h9.5M9 4.5 12.5 8 9 11.5"/></svg></a>
</nav>
</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.
36 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 app grid menu” from Animorio and fit it into my siteThe icons already sit in their tiles. When the menu opens each one is measured at home and so is the little square of the button that matches it (the same place in the three by three grid); a transform sends it exactly onto the square, at the square’s scale, and from there it goes home. It is measured after the button has gone up, so it starts from where the square really is.
Every icon has a single number, its progress from 0 to 1, which GSAP moves on. At each step the position is the straight line from the square to home, plus a sideways bow along sin(π·t), outwards for the left and right columns (ARC); the scale follows a back.out (POP), so the icon arrives a little bigger and settles; the rotation starts a few degrees off and straightens. They leave one every SPREAD seconds.
The ground of each icon starts with the black of the button and goes to its colour (the variable --tint) as it flies; the white drawing appears half way, the name once it has landed. Closing, all the other way round: the drawing goes at once, the ground turns black again, the icon shrinks and when it reaches its square it lets the square show and disappears.
Under the pointer (or with the keyboard focus) the tile lights up and its icon lifts LIFT pixels with a bounce; every other tile moves PUSH pixels in the direction from the icon pointed at to itself, less if it is far. The arrows move the focus across the grid, Enter chooses, Esc closes and brings the focus back to the button.
| Name | File | Default | What it changes |
|---|---|---|---|
FLIGHT | script.js | 0.72 | The seconds a little square flies before it becomes an icon. |
ARC | script.js | 0.8 | How much the squares curve as they fly (0 straight, 1.6 wide). |
SPREAD | script.js | 0.04 | The seconds between one square leaving and the next. |
POP | script.js | 1.8 | How far an icon overshoots its size as it lands (0 not at all, 4 a lot). |
LIFT | script.js | 4 | How many pixels the icon under the pointer lifts. |
PUSH | script.js | 3 | How many pixels the other tiles make room for it. |
For a web app with several products, a dashboard, a business tool or a suite: everybody knows the button with the nine squares, and here it keeps its promise. It works with six, nine or twelve apps.
The board narrows to the width of the screen and the squares fly the same way; a tap chooses the app. Without a pointer the tiles do not step aside.
No flight: the board fades in with the icons already in place, and the cross takes the place of the squares at once. The tiles light up under the pointer without moving.
GSAP 3.15.0, Geist (font variabile) 5.3.0.
Compressed with gzip, the three demo files weigh 8.3 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.