Skip to content
animorio
IT
All components

The rotary card file menu

Menus, Hover, 3DGSAP, CSS onlyAdvancedCompressed weight: Demo 12 KBGSAP 28 KBFreeDownload the zip

In the rich style: the Menu button of a letterpress studio opens a rotary desk card file, with a walnut base, chrome legs, knurled knobs and twenty-one cards hanging on the rails. Every coloured divider with its tab is a menu item: turning the drum the cards flip over the rails one after another, each on a spring of its own, and the chosen item rises out of the file with a brass paper clip.

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 rotary card file menu</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/mona-sans@5.3.0/wdth.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource/courier-prime@5.3.0/400.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage">
    <!-- the menu of a letterpress studio: the Menu button opens a rotary card file under it. The drum turns, the
         cards flip over the rails one after another, and every coloured divider with its tab is a menu item. Drag the
         cards, roll the wheel, use the arrows or point at a tab; choosing a divider lifts it out of the file. -->
    <nav class="rolo-menu" aria-label="Main" data-rolo-menu>
      <button class="rolo-btn" type="button" aria-expanded="false" aria-controls="rolo-file" data-toggle>
        <svg class="rolo-btn__icon" viewBox="0 0 44 44" aria-hidden="true">
          <path class="rolo-btn__base" d="M6 37.5 H 38 L 36 41 H 8 Z"/>
          <path class="rolo-btn__leg" d="M10 37.5 L 13 27 M34 37.5 L 31 27"/>
          <g class="rolo-btn__cards">
            <rect class="rolo-btn__card rolo-btn__card--back" data-icon-card x="14" y="11" width="16" height="16" rx="1.6"/>
            <rect class="rolo-btn__card rolo-btn__card--mid" data-icon-card x="14" y="11" width="16" height="16" rx="1.6"/>
            <rect class="rolo-btn__card rolo-btn__card--front" data-icon-card x="14" y="11" width="16" height="16" rx="1.6"/>
          </g>
          <rect class="rolo-btn__tab" x="16.5" y="8.4" width="6" height="3.2" rx="0.8"/>
          <path class="rolo-btn__rod" d="M9 27 H 35"/>
          <circle class="rolo-btn__knob" cx="8.4" cy="27" r="2.6"/>
          <circle class="rolo-btn__knob" cx="35.6" cy="27" r="2.6"/>
        </svg>
        <span class="rolo-btn__word" data-word>Menu</span>
      </button>

      <div class="rolo" id="rolo-file" inert data-rolo>
        <div class="rolo__shadow" aria-hidden="true"></div>

        <!-- the frame: walnut base, chrome legs, the hubs and the knurled knobs -->
        <div class="rolo__base" aria-hidden="true">
          <span class="rolo__base-top"></span>
          <span class="rolo__base-front"></span>
        </div>
        <span class="rolo__leg rolo__leg--l" aria-hidden="true"></span>
        <span class="rolo__leg rolo__leg--r" aria-hidden="true"></span>

        <!-- the drum: the cards are made by the script around the dividers below -->
        <div class="rolo__drum" data-drum>
          <ul class="rolo__cards" data-cards role="list">
            <li class="rolo__rail" aria-hidden="true" data-rail></li>
            <li><a class="card card--divider" href="#work" data-item style="--tone: var(--coral)"><span class="card__front"><span class="card__tab"><span class="card__tab-word">Work</span></span><span class="card__word">Work</span><span class="card__note">Posters, books, invitations</span></span><span class="card__back"><span class="card__tab"></span></span></a></li>
            <li><a class="card card--divider" href="#printing" data-item style="--tone: var(--mustard)"><span class="card__front"><span class="card__tab"><span class="card__tab-word">Printing</span></span><span class="card__word">Printing</span><span class="card__note">Letterpress and foil</span></span><span class="card__back"><span class="card__tab"></span></span></a></li>
            <li><a class="card card--divider" href="#papers" data-item style="--tone: var(--teal)"><span class="card__front"><span class="card__tab"><span class="card__tab-word">Papers</span></span><span class="card__word">Papers</span><span class="card__note">Cotton, kraft, deckle edges</span></span><span class="card__back"><span class="card__tab"></span></span></a></li>
            <li><a class="card card--divider" href="#workshops" data-item style="--tone: var(--sky)"><span class="card__front"><span class="card__tab"><span class="card__tab-word">Workshops</span></span><span class="card__word">Workshops</span><span class="card__note">Saturday classes at the press</span></span><span class="card__back"><span class="card__tab"></span></span></a></li>
            <li><a class="card card--divider" href="#journal" data-item style="--tone: var(--olive)"><span class="card__front"><span class="card__tab"><span class="card__tab-word">Journal</span></span><span class="card__word">Journal</span><span class="card__note">Notes from the press room</span></span><span class="card__back"><span class="card__tab"></span></span></a></li>
            <li><a class="card card--divider" href="#studio" data-item style="--tone: var(--plum)"><span class="card__front"><span class="card__tab"><span class="card__tab-word">Studio</span></span><span class="card__word">Studio</span><span class="card__note">Who sets the type</span></span><span class="card__back"><span class="card__tab"></span></span></a></li>
            <li><a class="card card--divider" href="#contact" data-item style="--tone: var(--rose)"><span class="card__front"><span class="card__tab"><span class="card__tab-word">Contact</span></span><span class="card__word">Contact</span><span class="card__note">Say hello, order a proof</span></span><span class="card__back"><span class="card__tab"></span></span></a></li>
          </ul>
        </div>

        <!-- the hubs on the ends of the rails (the rails sit among the cards: in front of the cards behind them,
             behind the cards in front) -->
        <span class="rolo__hub rolo__hub--l" aria-hidden="true"></span>
        <span class="rolo__hub rolo__hub--r" aria-hidden="true"></span>
        <span class="rolo__knob rolo__knob--l" aria-hidden="true" data-knob><span class="rolo__knob-mark" data-knob-mark></span></span>
        <span class="rolo__knob rolo__knob--r" aria-hidden="true" data-knob><span class="rolo__knob-mark" data-knob-mark></span></span>
        <span class="rolo__pawl" aria-hidden="true" data-pawl></span>

        <!-- the brass plate on the base: it says which divider is at the front -->
        <span class="rolo__plate">
          <span class="rolo__brand" aria-hidden="true">Kardwell</span>
          <span class="rolo__now" aria-live="polite"><span class="rolo__now-word" data-now>Work</span></span>
        </span>
      </div>
    </nav>
  </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.

42 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 rotary card file menu” from Animorio and fit it into my site

What it does

On a white background an ink coloured pill button with a rotary card file drawn small inside it, its wooden base, its legs and three cards. Pointing at it the front card falls forward, the others straighten up and then go back to their places; the word narrows and bounces. Pressing it, the word turns into Close and the card file of Kardwell, a letterpress studio, opens under it: the walnut base with its grain and a brass plate, two chrome legs, two ball hubs, two black knurled knobs and the rails twenty-one cards hang on. The file arrives turning and the cards flip over the rails in a cascade before they stop. At the front there is a coloured divider with its tab, the name of the item set big, a rule and a note; behind it the cards lean back in a fan and the tabs of the next dividers show; in front of the rails the cards already past have fallen forward and show their backs, with the Filed stamp and the coloured tabs. Behind every divider there are two cream cards with blue ruled lines, a red head line and three typewritten lines. The items are Work, Printing, Papers, Workshops, Journal, Studio and Contact. It turns by dragging the cards up or down, with the wheel, with the arrows or by pointing at a tab, which rises a little: every card falls on a spring of its own and a small delay, the knurls on the knobs run past, the white mark goes round the knob, the ratchet pawl ticks at every card and the plate rolls the name of the item at the front. Choosing a divider, the card rises out of the file, drops back with an elastic and stays a little higher than the others with a brass paper clip on top, and a gleam runs over the plate.

How it works

A drum made of one number

The whole card file moves from a single number, the angle of the drum. Every card has its place on the circle, twenty-one places 360 divided by 21 degrees apart; on every frame the script works out how far the card is from the front, between minus 180 and 180 degrees, and from that how far it should lean. The drum follows the chosen item on a spring that overshoots a little; dragging, the angle follows the finger and on release it runs on with its speed and stops on the nearest divider.

The fan and the fall

The wanted lean comes from two hyperbolic tangents: the cards behind open slowly up to 56 degrees, the ones that went past the rails fall fast up to 72. No card goes straight there though: each one has a spring of its own, with a slightly different stiffness and little damping, so every card arrives a moment late, overshoots and settles. That is what makes the cascade when the drum turns.

Real cards in CSS 3D

Every card is a list item turned with rotateX around its bottom edge, which sits on the rails; inside it has two faces, the front and the back turned by 180 degrees, with backface-visibility hidden, so the cards that fell forward show their backs. The perspective sits on the list with the point of view high above the file, and the order of drawing is decided by the script with z-index: behind, the card nearest the front is on top; in front, the most upright covers the one lying flatter, and the rails sit in between.

The knobs, the pawl, the plate

The knobs are cylinders seen from the side: the knurling is a repeating gradient that runs vertically with the angle of the drum, and the white mark goes round the cylinder with a sine, squashed by the cosine and hidden when it goes round the back. Every time a card goes past the rails the pawl ticks and comes back with an elastic; when the divider at the front changes the brass plate sends the old name up and bounces the new one in from below.

Real links and the keyboard

The dividers are the links of the menu, in the right order and reachable with the Tab key: when one takes the focus the drum brings it to the front, the arrows go to the previous and the next, Home and End go to the ends, Esc closes and gives the focus back to the button. The chosen item has aria-current, the plate speaks in an aria-live region, the typewritten cards are hidden from screen readers and the closed file is inert.

Values to change

NameFileDefaultWhat it changes
SPRINGscript.js1How lively the cards are when they flip over the rails (1 is normal).
FANscript.js56The degrees the cards behind lean back.
FLOPscript.js72The degrees the cards in front fall forward.
DRAGscript.js0.5The degrees the drum turns for every pixel dragged.
LIFTscript.js18How far the chosen divider rises out of the file.
SPINscript.js1How fast the drum turns to an item (1 is normal).

Try them in the Playground

When to use it

For the menu of the site of a print studio, a graphic design studio, a stationery shop, an archive, a professional office or a contact book: the items are the dividers, from five to eight, with short names like on real tabs.

On phones

The card file is as wide as the screen and at most as tall as the room left under the button; the cards, the type and the plate are measured on the box with container units. It turns by dragging the cards up or down with a finger, and a tap on a tab brings its item to the front and chooses it.

With reduced motion

The card file fades in already still on the item at the front; turning, the cards go straight to their places with no springs or cascade, the chosen item stays a little higher with no elastic and the paper clip appears. No ticking pawl, no gleam on the plate.

Pitfalls

  • An effect with opacity or a filter on the faces inside a 3D context (a pseudo element with a variable opacity, then a filter: brightness) made Chrome paint some cards solid black while they turned: the shading of the cards is now one more layer in the background, a gradient whose colour has a variable alpha.
  • Opacity on the list item flattens its preserve-3d and the back of the cards stops hiding: the front shows the wrong way round. The fade goes on the faces, not on the card.
  • With the point of view high up, a card fallen to 50 degrees is almost in the plane of the eye and shows edge-on like a line: the cards in front have to fall past the eye (72 degrees) to show their backs.
  • On the first tick gsap.ticker.deltaRatio() is not a number yet: the time step came out NaN and every card stayed NaN for ever, with its transform thrown away. The step must be checked with Number.isFinite.
  • The cards behind must not be stacked like the ones in front: behind, the card nearest the front is on top; in front, the most upright covers the one lying flatter, because it is seen from above.

Libraries

GSAP 3.15.0, Mona Sans (font variabile) 5.3.0, Courier Prime 5.3.0.

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

Testing

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