Skip to content
animorio
IT
All components

The filing drawer menu

Menus, 3D, PhysicsCSS only, GSAPAdvancedCompressed weight: Demo 9.6 KBGSAP 28 KBFreeDownload the zip

A site's menu as a steel filing cabinet in 3D: the items are drawers that come out on a spring as you pass; the chosen one runs out to its stop and the folders of the sub-items rise one after the other, their tabs in a staircase. Choosing another drawer, the open one slams shut and the cabinet jolts.

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 filing drawer menu</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/familjen-grotesk@5.3.0/index.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource/courier-prime@5.3.0/index.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource/courier-prime@5.3.0/700.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage">
    <!-- the menu as a filing cabinet: each item is a drawer in 3D. Under the pointer a drawer comes out a little on a
         spring; pressed, it runs out on its rails and its folders rise one after the other, their tabs in a staircase.
         The cabinet's shell, the dark holes and the inside of the drawers are drawn by the script -->
    <nav class="cab" aria-label="Archive" data-cab>
      <div class="cab__scene" data-scene>
        <div class="cab__body" data-body>
          <ul class="cab__drawers">
            <li class="drawer" style="--tint: #e0603f" data-drawer>
              <button class="drawer__front" type="button" aria-expanded="false" aria-controls="drawer-projects" data-front data-autoclic>
                <span class="drawer__card"><span class="drawer__name">Projects</span><span class="drawer__range">A&ndash;F</span></span>
                <span class="drawer__pull" aria-hidden="true"></span>
              </button>
              <ul class="drawer__folders" id="drawer-projects" aria-label="Projects" data-folders>
                <li><a class="folder" href="#" data-folder aria-current="page"><span class="folder__tab">Residential</span><span class="folder__note">32 houses, 1998&ndash;2026</span></a></li>
                <li><a class="folder" href="#" data-folder><span class="folder__tab">Workplace</span><span class="folder__note">Offices and studios</span></a></li>
                <li><a class="folder" href="#" data-folder><span class="folder__tab">Hospitality</span><span class="folder__note">Hotels, two harbour baths</span></a></li>
                <li><a class="folder" href="#" data-folder><span class="folder__tab">Unbuilt</span><span class="folder__note">Competitions we lost</span></a></li>
              </ul>
            </li>
            <li class="drawer" style="--tint: #3f6fb0" data-drawer>
              <button class="drawer__front" type="button" aria-expanded="false" aria-controls="drawer-studio" data-front data-autoclic>
                <span class="drawer__card"><span class="drawer__name">Studio</span><span class="drawer__range">G&ndash;L</span></span>
                <span class="drawer__pull" aria-hidden="true"></span>
              </button>
              <ul class="drawer__folders" id="drawer-studio" aria-label="Studio" data-folders>
                <li><a class="folder" href="#" data-folder><span class="folder__tab">About us</span><span class="folder__note">Twelve people, one table</span></a></li>
                <li><a class="folder" href="#" data-folder><span class="folder__tab">People</span><span class="folder__note">Partners and team</span></a></li>
                <li><a class="folder" href="#" data-folder><span class="folder__tab">Awards</span><span class="folder__note">From Aalto to Mies</span></a></li>
                <li><a class="folder" href="#" data-folder><span class="folder__tab">Careers</span><span class="folder__note">Two places open</span></a></li>
              </ul>
            </li>
            <li class="drawer" style="--tint: #d49b25" data-drawer>
              <button class="drawer__front" type="button" aria-expanded="false" aria-controls="drawer-journal" data-front data-autoclic>
                <span class="drawer__card"><span class="drawer__name">Journal</span><span class="drawer__range">M&ndash;R</span></span>
                <span class="drawer__pull" aria-hidden="true"></span>
              </button>
              <ul class="drawer__folders" id="drawer-journal" aria-label="Journal" data-folders>
                <li><a class="folder" href="#" data-folder><span class="folder__tab">Essays</span><span class="folder__note">On brick, light and time</span></a></li>
                <li><a class="folder" href="#" data-folder><span class="folder__tab">On site</span><span class="folder__note">Notes from the build</span></a></li>
                <li><a class="folder" href="#" data-folder><span class="folder__tab">Press</span><span class="folder__note">What others wrote</span></a></li>
              </ul>
            </li>
            <li class="drawer" style="--tint: #8a5a9e" data-drawer>
              <button class="drawer__front" type="button" aria-expanded="false" aria-controls="drawer-contact" data-front data-autoclic>
                <span class="drawer__card"><span class="drawer__name">Contact</span><span class="drawer__range">S&ndash;Z</span></span>
                <span class="drawer__pull" aria-hidden="true"></span>
              </button>
              <ul class="drawer__folders" id="drawer-contact" aria-label="Contact" data-folders>
                <li><a class="folder" href="#" data-folder><span class="folder__tab">Visit</span><span class="folder__note">By appointment</span></a></li>
                <li><a class="folder" href="#" data-folder><span class="folder__tab">Write to us</span><span class="folder__note">Replies within a day</span></a></li>
                <li><a class="folder" href="#" data-folder><span class="folder__tab">Newsletter</span><span class="folder__note">Four letters a year</span></a></li>
              </ul>
            </li>
          </ul>
        </div>
      </div>
    </nav>
    <p class="sr" aria-live="polite" data-live></p>
  </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.

59 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 filing drawer menu” from Animorio and fit it into my site

What it does

On a white page stands a green steel filing cabinet seen a little from above and three-quarters on, with its top, its side in shadow, its plinth and its shadow on the floor. It has four drawers, each with a typed card in a brass holder (Projects A–F, Studio G–L, Journal M–R, Contact S–Z, the letters in the drawer's colour) and a cup handle. Passing with the mouse, a drawer comes out a few centimetres on a spring and goes back when you leave it; the cabinet follows the pointer slightly. Pressing a drawer, it runs out on its rails and hits the stop, the view tilts down to look inside, the drawer below takes its shadow, and out of the box the manila folders rise one after the other, each higher than the one in front, with their tabs set out in a staircase, edged at the top in the drawer's colour, and a typed line (Residential, 32 houses, 1998–2026); at the knock of the stop they swing like hanging files. Pointing at a folder it lifts a little; pressing it, it becomes the current page and its tab turns white. Choosing another drawer the folders sink back, the open drawer slams shut, the cabinet jolts and the other drawers rattle, then the new one opens.

How it works

The cabinet in CSS 3D

Everything is CSS 3D with transform-style: preserve-3d. The script builds the flat faces it needs (top, side, the steel strips round the drawers, the plinth, the dark behind each slot with its floor and wall, each drawer's box) and puts them in place from their middle with a translate3d, turned 90 degrees when they lie flat or stand sideways. The drawer fronts are real buttons, the folders real links: everything stays clickable and reachable with the keyboard.

Springs and stops

Each drawer has a position and a speed moved by a hand-written spring (SPRING), in 4 millisecond steps on GSAP's ticker, with the frame step capped at 1/30 of a second. The spring pulls past the stop, so the drawer arrives fast: at the two ends of the rails the position is clamped and the speed bounces back, damped. A knock on the closed stop jolts the cabinet (a spring of its own) and gives the other drawers a random push; one on the open stop makes the folders swing.

The folders in a staircase

The folders stand in the box one behind the other. Opening, they rise in turn every STAGGER seconds, each STEP pixels higher than the one in front, on a spring of their own; the tabs move from left to right along the edge, measured once the typeface has arrived, so above the drawer you read a staircase of names. Each folder is also a pendulum around its bottom edge, pushed by the knocks.

The view

The cabinet is turned to show its top and right side and scaled to the width of the screen. The view follows the pointer by a few degrees, and with a drawer open it tilts further down (TILT) and rises the more the lower the drawer is, so you see inside it and it stays in the frame.

Keyboard and screen readers

The drawers are buttons with aria-expanded and aria-controls, the folders links with aria-current; the arrows go from drawer to drawer and from folder to folder, Esc closes and gives the focus back to the drawer, and a press outside the cabinet closes it. When a folder is chosen, an invisible line (aria-live) tells screen readers the new page, once.

Values to change

NameFileDefaultWhat it changes
OUTscript.js150How far a drawer comes out when it opens, in pixels.
PEEKscript.js20How far it comes out under the pointer.
SPRINGscript.js[170, 0.5]The drawers' spring: how stiff, and how much it is held back (1 = no bounce).
STEPscript.js30How much higher each folder rises than the one in front of it.
STAGGERscript.js0.07The seconds between one folder rising and the next.
TILTscript.js[-15, -27]The angle you look down at the cabinet from: at rest, and into an open drawer.
STARTscript.js-1The drawer open when the page loads (-1: none).

Try them in the Playground

When to use it

For the menu of a studio, an archive, a publisher, a museum or a portfolio with many sections and subsections: wherever the idea of an archive truly belongs and the menu can be the first thing you see, in the middle of the page or on an index page.

On phones

The cabinet shrinks to the width of the screen, leaving room for the drawer that comes out towards the left. Without a mouse there is no preview on passing: at a touch the drawer gives a little and opens; the folders are touched on their tab or their body.

With reduced motion

No travel and no springs: a drawer is shut or already all the way out, with the view already tilted, and the folders appear in place with a fade; no jolts, no swinging and no tilting with the pointer.

Pitfalls

  • Between two faces side by side in 3D a one-pixel seam often shows the page: behind all the slots there is a single wider dark wall, and the slots' floors and walls are a little bigger than needed, so the seams show darkness.
  • An opacity on an element holding other elements in 3D flattens them (preserve-3d stops applying): with reduced motion each folder fades, not the list that holds them.
  • With the view tilted down, the lowest drawer, open, ended up out of the frame: the view rises more the lower the open drawer is.
  • The spring pulls the drawer past the stops (60 pixels past the open one, 50 past the closed one): so it reaches the stop still fast and knocks it, instead of slowing down in front of it.
  • The tabs move along the edge according to their width: they must be measured again once the typeface has arrived, or with the fallback one the last tab runs off the folder's edge.

Libraries

GSAP 3.15.0, Familjen Grotesk (font variabile) 5.3.0, Courier Prime 5.3.0.

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

Testing

Tested on 4 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