Skip to content
animorio
IT
All components

The tile mega menu

Menus, Hover, 3D, TransitionsCSS only, GSAPAdvancedCompressed weight: Demo 10 KBGSAP 28 KBFreeDownload the zip

A bar with Products, Solutions and Resources and a big panel under it, made of tiles that turn over in 3D like flip cards: pointing at a menu the tiles flip in a wave that starts right under it and spreads across the panel, and the mega menu appears on their backs. Moving to another menu the wave starts again from there and the tiles turn once more; leaving the bar they turn away edge on, in a wave as well.

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 tile mega menu</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/manrope@5.3.0/index.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage">
    <!-- the mega menu: a bar with three menus and one panel under it. The panel is a grid of tiles, each a card with two
         faces; each face holds its own slice of the same drawing of the menu. Opening, or going from one menu to
         another, the tiles turn over in a wave that starts right under the menu you point at -->
    <div class="mega" data-mega>
      <nav class="mega__nav" aria-label="Main" data-nav>
        <div class="mega__barwrap">
          <span class="mega__glow" aria-hidden="true" data-glow></span>
          <ul class="mega__bar">
            <li><button class="mega__trigger" type="button" aria-expanded="false" aria-controls="mega-products" data-trigger data-autoclic>Products<svg class="mega__caret" viewBox="0 0 14 14" aria-hidden="true"><path d="M3.5 5.5 7 9l3.5-3.5"/></svg></button></li>
            <li><button class="mega__trigger" type="button" aria-expanded="false" aria-controls="mega-solutions" data-trigger data-autoclic>Solutions<svg class="mega__caret" viewBox="0 0 14 14" aria-hidden="true"><path d="M3.5 5.5 7 9l3.5-3.5"/></svg></button></li>
            <li><button class="mega__trigger" type="button" aria-expanded="false" aria-controls="mega-resources" data-trigger data-autoclic>Resources<svg class="mega__caret" viewBox="0 0 14 14" aria-hidden="true"><path d="M3.5 5.5 7 9l3.5-3.5"/></svg></button></li>
          </ul>
        </div>

        <div class="mega__panel" data-panel>
          <span class="mega__shadow" aria-hidden="true" data-shadow></span>
          <!-- the tiles: built by the script, each face a slice of the menu's picture -->
          <div class="mega__tiles" aria-hidden="true" data-tiles></div>

          <!-- the real menus: the links you click and tab to, shown once the tiles have turned -->
          <div class="mega__content" data-content>
            <section class="mega__section" id="mega-products" aria-label="Products" data-section>
              <div class="mega__col">
                <p class="mega__heading">Platform</p>
                <a class="mega__link" href="#"><span class="mega__icon" style="--tint: #ffe6db; --mark: #e2511f"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5 19V11M12 19V5M19 19v-5"/></svg></span><span class="mega__text"><b>Analytics</b><small>See every visit as it happens</small></span></a>
                <a class="mega__link" href="#"><span class="mega__icon" style="--tint: #e3f4ea; --mark: #18835a"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M17 3l3 3-3 3M20 6H9a5 5 0 0 0-5 5M7 21l-3-3 3-3M4 18h11a5 5 0 0 0 5-5"/></svg></span><span class="mega__text"><b>Automations</b><small>Let the busywork run itself</small></span></a>
                <a class="mega__link" href="#"><span class="mega__icon" style="--tint: #e7e6ff; --mark: #5146d8"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M7 3h7l4 4v14H7zM14 3v4h4M10 12h5M10 16h5"/></svg></span><span class="mega__text"><b>Reports</b><small>Numbers your whole team can read</small></span></a>
              </div>
              <div class="mega__col">
                <p class="mega__heading">Add-ons</p>
                <a class="mega__link" href="#"><span class="mega__icon" style="--tint: #fff1cf; --mark: #b97a06"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5 4h14v16H5zM9 9h6M9 13h6M9 17h3"/></svg></span><span class="mega__text"><b>Forms</b><small>Collect answers without code</small></span></a>
                <a class="mega__link" href="#"><span class="mega__icon" style="--tint: #e1f0ff; --mark: #1f6fd1"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M3 7h18v11H3zM3 11h18M7 15h3"/></svg></span><span class="mega__text"><b>Payments</b><small>Take cards in two clicks</small></span></a>
                <a class="mega__link" href="#"><span class="mega__icon" style="--tint: #ffe4ef; --mark: #c8336f"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 13l2.5-8h11L20 13v6H4zM4 13h5l1 2h4l1-2h5"/></svg></span><span class="mega__text"><b>Inbox</b><small>Every message in one place</small></span></a>
              </div>
              <a class="mega__card" href="#" style="--card: #1c1a2b; --glow: #ff7a45">
                <svg class="mega__art" viewBox="0 0 240 120" aria-hidden="true">
                  <rect x="14" y="14" width="140" height="92" rx="10" fill="#26243a"/>
                  <path d="M24 88L48 70L70 76L94 52L118 58L144 32V96H24Z" fill="#ff7a45" fill-opacity="0.2"/>
                  <path d="M24 88L48 70L70 76L94 52L118 58L144 32" fill="none" stroke="#ff7a45" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
                  <rect x="164" y="14" width="62" height="42" rx="10" fill="#26243a"/>
                  <rect x="174" y="36" width="8" height="12" rx="2" fill="#8f86ff"/><rect x="188" y="28" width="8" height="20" rx="2" fill="#8f86ff"/><rect x="202" y="22" width="8" height="26" rx="2" fill="#ffd166"/>
                  <rect x="164" y="64" width="62" height="42" rx="10" fill="#26243a"/>
                  <path d="M176 92a19 19 0 0 1 38 0" fill="none" stroke="#3a3752" stroke-width="6" stroke-linecap="round"/>
                  <path d="M176 92a19 19 0 0 1 30-15.5" fill="none" stroke="#5fd3a1" stroke-width="6" stroke-linecap="round"/>
                </svg>
                <span class="mega__tag">New</span>
                <b>Live dashboards</b>
                <small>Watch your numbers move in real time.</small>
              </a>
            </section>

            <section class="mega__section" id="mega-solutions" aria-label="Solutions" data-section hidden>
              <div class="mega__col">
                <p class="mega__heading">By team</p>
                <a class="mega__link" href="#"><span class="mega__icon" style="--tint: #ffe4ef; --mark: #c8336f"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 10v4h3l6 4V6L7 10zM17 9a4 4 0 0 1 0 6"/></svg></span><span class="mega__text"><b>Marketing</b><small>Campaigns that measure themselves</small></span></a>
                <a class="mega__link" href="#"><span class="mega__icon" style="--tint: #fff1cf; --mark: #b97a06"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 18l5-6 4 3 7-8M15 7h5v5"/></svg></span><span class="mega__text"><b>Sales</b><small>Know which leads are warm</small></span></a>
                <a class="mega__link" href="#"><span class="mega__icon" style="--tint: #e1f0ff; --mark: #1f6fd1"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5 5h14v10H10l-5 4z"/></svg></span><span class="mega__text"><b>Support</b><small>Answer faster, with context</small></span></a>
              </div>
              <div class="mega__col">
                <p class="mega__heading">By size</p>
                <a class="mega__link" href="#"><span class="mega__icon" style="--tint: #e3f4ea; --mark: #18835a"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 20V10M12 10c0-4 3-6 7-6 0 4-3 6-7 6zM12 13c0-3-2.5-5-6-5 0 3.5 2.5 5 6 5z"/></svg></span><span class="mega__text"><b>Startups</b><small>Free until you find your feet</small></span></a>
                <a class="mega__link" href="#"><span class="mega__icon" style="--tint: #e7e6ff; --mark: #5146d8"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 8h16v11H4zM9 8V5h6v3M4 13h16"/></svg></span><span class="mega__text"><b>Agencies</b><small>One login for every client</small></span></a>
                <a class="mega__link" href="#"><span class="mega__icon" style="--tint: #ffe6db; --mark: #e2511f"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 3l7 3v5c0 5-3 8-7 10-4-2-7-5-7-10V6z"/></svg></span><span class="mega__text"><b>Enterprise</b><small>Security reviews, done for you</small></span></a>
              </div>
              <a class="mega__card" href="#" style="--card: #10261f; --glow: #5fd3a1">
                <svg class="mega__art" viewBox="0 0 240 120" aria-hidden="true">
                  <rect x="14" y="14" width="150" height="92" rx="10" fill="#173a2f"/>
                  <g fill="#21503f">
                    <rect x="24" y="26" width="18" height="16" rx="4"/><rect x="47" y="26" width="18" height="16" rx="4"/><rect x="70" y="26" width="18" height="16" rx="4"/><rect x="93" y="26" width="18" height="16" rx="4"/><rect x="116" y="26" width="18" height="16" rx="4"/><rect x="139" y="26" width="18" height="16" rx="4"/>
                    <rect x="24" y="47" width="18" height="16" rx="4"/><rect x="70" y="47" width="18" height="16" rx="4"/><rect x="116" y="47" width="18" height="16" rx="4"/>
                    <rect x="24" y="68" width="18" height="16" rx="4"/><rect x="47" y="68" width="18" height="16" rx="4"/><rect x="93" y="68" width="18" height="16" rx="4"/><rect x="139" y="68" width="18" height="16" rx="4"/>
                  </g>
                  <g fill="#5fd3a1">
                    <rect x="47" y="47" width="18" height="16" rx="4"/><rect x="93" y="47" width="18" height="16" rx="4"/><rect x="139" y="47" width="18" height="16" rx="4"/>
                    <rect x="70" y="68" width="18" height="16" rx="4"/><rect x="116" y="68" width="18" height="16" rx="4"/>
                  </g>
                  <rect x="174" y="14" width="52" height="92" rx="10" fill="#173a2f"/>
                  <rect x="184" y="70" width="12" height="26" rx="3" fill="#2b6b54"/><rect x="204" y="40" width="12" height="56" rx="3" fill="#5fd3a1"/>
                </svg>
                <span class="mega__tag">Case study</span>
                <b>Twice the bookings</b>
                <small>How a six-person studio doubled its calendar in a season.</small>
              </a>
            </section>

            <section class="mega__section" id="mega-resources" aria-label="Resources" data-section hidden>
              <div class="mega__col">
                <p class="mega__heading">Learn</p>
                <a class="mega__link" href="#"><span class="mega__icon" style="--tint: #e7e6ff; --mark: #5146d8"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 6l8-3 8 3-8 3zM4 6v6l8 3 8-3V6M12 9v12"/></svg></span><span class="mega__text"><b>Guides</b><small>Step by step, from setup to scale</small></span></a>
                <a class="mega__link" href="#"><span class="mega__icon" style="--tint: #ffe6db; --mark: #e2511f"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M3 6h13v12H3zM16 10l5-3v10l-5-3"/></svg></span><span class="mega__text"><b>Webinars</b><small>Live sessions with our team</small></span></a>
                <a class="mega__link" href="#"><span class="mega__icon" style="--tint: #e3f4ea; --mark: #18835a"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 4h7v7H4zM13 4h7v7h-7zM4 13h7v7H4zM13 13h7v7h-7z"/></svg></span><span class="mega__text"><b>Templates</b><small>Start from something that works</small></span></a>
              </div>
              <div class="mega__col">
                <p class="mega__heading">Help</p>
                <a class="mega__link" href="#"><span class="mega__icon" style="--tint: #e1f0ff; --mark: #1f6fd1"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M8 8l-4 4 4 4M16 8l4 4-4 4M13.5 5l-3 14"/></svg></span><span class="mega__text"><b>Docs</b><small>Every feature, explained</small></span></a>
                <a class="mega__link" href="#"><span class="mega__icon" style="--tint: #fff1cf; --mark: #b97a06"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 6h11v8H8l-4 3zM15 10h5v8l-3-2h-6v-2"/></svg></span><span class="mega__text"><b>Community</b><small>Ask people who have been there</small></span></a>
                <a class="mega__link" href="#"><span class="mega__icon" style="--tint: #ffe4ef; --mark: #c8336f"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M3 12h4l3-7 4 14 3-7h4"/></svg></span><span class="mega__text"><b>Status</b><small>Uptime and incidents, live</small></span></a>
              </div>
              <a class="mega__card" href="#" style="--card: #2a1d10; --glow: #ffc35a">
                <svg class="mega__art" viewBox="0 0 240 120" aria-hidden="true">
                  <path d="M120 30C100 18 66 16 40 22V102C66 96 100 98 120 110Z" fill="#f5e9d2"/>
                  <path d="M120 30C140 18 174 16 200 22V102C174 96 140 98 120 110Z" fill="#fff6e6"/>
                  <path d="M120 30V110" stroke="#d9c4a0" stroke-width="2"/>
                  <g stroke="#cdb48a" stroke-width="3" stroke-linecap="round"><path d="M56 42c18-3 34-2 50 4M56 56c18-3 34-2 50 4M56 70c14-2 26-1 38 3"/><path d="M134 46c16-6 32-7 50-4M134 60c16-6 32-7 50-4M134 74c12-4 24-5 36-4"/></g>
                  <path d="M176 18V48L184 42L192 48V18Z" fill="#ffc35a"/>
                </svg>
                <span class="mega__tag">Field guide</span>
                <b>The small team playbook</b>
                <small>Forty pages on doing more with less, free to download.</small>
              </a>
            </section>
          </div>
        </div>
      </nav>
      <p class="sr" aria-live="polite" data-live></p>
    </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.

110 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 tile mega menu” from Animorio and fit it into my site

What it does

On a black page an ivory, pill shaped bar with three menus, Products, Solutions and Resources, each with its little arrow. With the pointer on Products a big ivory panel is born under the bar, and it does not arrive all at once: it is a grid of tiles turning in from edge on one after the other, starting from the one right under the menu, like a giant flip board; as they turn they lift towards you, lean away from the wave and darken when edge on. When the last one stops the mega menu is there: two columns of links with a heading, Platform and Add-ons, each with a coloured icon, a name and a line of description, and on the right a dark card with a small drawing of a dashboard, the label New and Live dashboards. Moving to Solutions the light pill behind the menu slides and the wave starts again from Solutions: every tile flips again and on its back is the new menu, By team and By size with the card Twice the bookings; Resources shows Learn, Help and The small team playbook with an open book. Leaving the bar the tiles turn away edge on, in a wave from the open menu, and the panel is gone. The links can be pointed at, clicked and reached with Tab and the arrows.

How it works

A grid of two sided cards

The panel is cut into a grid fitted to its size (TILE is the size a tile aims for), with round corners only on the corner tiles. Each tile has two faces in CSS 3D, one turned by 180 degrees, with backface-visibility: hidden, and the grid has its own perspective.

The menu cut into pieces

Each face holds a copy of the menu as big as the panel, moved so that only its own rectangle shows through the face: side by side, the tiles put the menu back together. The copies cannot be reached with Tab or read twice; the real menu, with the links, sits over the tiles, transparent while they turn, and shows as soon as they are still.

An angle that only grows

A tile’s turn is an angle that only goes forward: the next multiple of 180 degrees whose face is not the one in view. The new menu goes on the hidden face before it comes round, even while the wave before is still turning; a still tile that already shows the right menu does not turn. Closing goes to the next edge-on angle, 90 plus a multiple of 180.

The wave from the menu

The wave starts at the point right under the middle of the menu pointed at: each tile’s delay is its distance from that point divided by WAVE. While it turns, the tile lifts towards you by the sine of the angle, leans away from the wave and darkens as it goes edge on; it stops with a small bounce.

Intent, keys and aria

The pointer has an intent made of GSAP delayed calls: a moment before opening, one before switching menus (crossing a menu on the way somewhere else does not open it) and one before closing. The menus are buttons with aria-expanded and aria-controls; the down arrow or Tab go into the menu, the side arrows run along the bar, Esc closes and gives the focus back, and a tap outside or the focus leaving close it too.

Values to change

NameFileDefaultWhat it changes
TILEscript.js96The size a tile aims for, in pixels (the grid is fitted to the panel).
FLIPscript.js0.62The seconds a tile takes to turn over.
WAVEscript.js1500How fast the wave spreads from the menu’s button, in pixels a second.
LIFTscript.js22How far a turning tile comes towards you, in pixels.
INTENTscript.js[0.05, 0.12, 0.3]The pointer’s intent, in seconds: before opening, before switching menus, before closing.

Try them in the Playground

When to use it

For the main navigation of a product site with many pages: a SaaS, a studio, a shop with categories. The flip gives it character without slowing it down: the tiles near the menu arrive at once, and the real links are there as soon as the panel is still.

On phones

At 390 pixels the bar fits on one line and the panel is as wide as the screen, with the links in two columns and the card underneath; the grid becomes four columns. A tap on a menu opens it (the wave starts there), a tap on the same one closes it, and so does a tap outside.

With reduced motion

No tiles: the panel fades in, switching menus the new one fades over the old, and closing it fades away. The pill behind the menu moves without sliding.

Pitfalls

  • Turning from 0 to 180 degrees every time, a tile caught halfway by a new wave jumped back: the angle now only grows, to the next multiple of 180 with the other face, and the new menu always goes on the hidden face.
  • A thin black line of the page showed between two still tiles, from the antialiasing of 3D faces: each face is half a pixel bigger than its tile and the positions are rounded to the pixel.
  • Giving the real links their place only once the turn was over, a click during the wave was lost: the real menu always sits over the tiles, transparent while they turn, and takes the clicks and the focus at every moment.
  • The copies of the menu on the tiles had links reachable with Tab and were read twice: no href, tabindex -1, and the whole grid is aria-hidden.
  • The card’s drawing used a gradient with an id, and in the copies without ids it vanished: the areas are flat colours with an opacity.

Libraries

GSAP 3.15.0, Manrope (font variabile) 5.3.0.

Compressed with gzip, the three demo files weigh 10 KB; the libraries: GSAP 28 KB.

Testing

Tested on 5 October 2026

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

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