Skip to content
animorio
IT
All components

The app grid menu

Menus, Transitions, HoverGSAP, CSS onlyAdvancedCompressed weight: Demo 8.3 KBGSAP 28 KBFreeDownload the zip

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.

The code

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>

Playground

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

Or ask Claude Code for it

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 site

What it does

On a white page there is a white button with «Apps» and the usual nine black squares, which spread a hair from the middle under the pointer. Pressed, the button goes up and its word becomes «Close»; the nine squares leave the button one after the other and fly down, each on a curve that opens outwards, turning a little: they grow into the icons of Mail, Calendar, Files, Notes, Chat, Photos, Tasks, Meet and Settings, going from black to their colour, and land with a small bounce while the white board appears under them. On each icon its drawing appears, under it its name; in the button a cross takes the place of the squares. The app you are in, Calendar, has its name in full black and a short line of its colour under the icon. Under the pointer a tile lights up, its icon lifts and the others step aside a little. Choosing an app its icon bounces as if starting, then the icons fly back into the button, turning black and small again, the chosen one last, and the button goes back to the middle. At the bottom there is All apps. It also closes with Esc, with the button or by pressing outside.

How it works

A FLIP for every icon

The 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.

The flight on a curve

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 colour on the way

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.

The tiles that step aside

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.

Values to change

NameFileDefaultWhat it changes
FLIGHTscript.js0.72The seconds a little square flies before it becomes an icon.
ARCscript.js0.8How much the squares curve as they fly (0 straight, 1.6 wide).
SPREADscript.js0.04The seconds between one square leaving and the next.
POPscript.js1.8How far an icon overshoots its size as it lands (0 not at all, 4 a lot).
LIFTscript.js4How many pixels the icon under the pointer lifts.
PUSHscript.js3How many pixels the other tiles make room for it.

Try them in the Playground

When to use 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.

On phones

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.

With reduced motion

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.

Pitfalls

  • The button sits above the board: the flying icons passed under it and vanished behind it before reaching the squares. While they fly the board goes above the button.
  • The icons must be measured after the button has gone up: measured at the click, they started from where the button was before and the squares were left alone.
  • Before take-off the icons were already visible at home for an instant, coloured and without their drawing: now they stay hidden until they have been sent back onto the squares.
  • The short line under the icon of the app you are in flew with the icon, shrunk: during the flight it is off.
  • The CSS transition on the squares’ transform (for the spread under the pointer) held back their arrival animated by GSAP: for the arrival the transition is removed and then put back.
  • The pointer crossing the tiles while the menu closes lit them up: while closing the tiles no longer listen to the pointer.

Libraries

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.

Testing

Tested on 19 October 2026

  • Desktop: Done
  • Phone: Done
  • Reduced motion: Done
  • Safari on iPhone: To do

License

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.

All components