Skip to content
animorio
IT
All components

The dropdown that changes shape

Menus, Hover, TransitionsGSAPAdvancedCompressed weight: Demo 6.3 KBGSAP 28 KBFreeDownload the zip

A bar with three menus sharing a single panel: going from one menu to the next the panel changes width, height and place, its little arrow slides under the new button and the lists slide in from the side they come from, always cut inside the panel.

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 dropdown that changes shape</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/plus-jakarta-sans@5.3.0/index.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage">
    <div class="zone" data-zone>
      <!-- one panel for every menu: moving from one to the next it changes size and place, and the contents slide in
           from the side they come from. Each list sits in the page right after its button, for the Tab key -->
      <nav class="nav" aria-label="Main" data-nav>
        <ul class="nav__list">
          <li>
            <button class="nav__trigger" type="button" aria-expanded="false" aria-controls="drop-products" data-trigger data-autoclic>Products<svg class="nav__caret" viewBox="0 0 14 14" aria-hidden="true"><path d="M3.5 5.5 7 9l3.5-3.5"/></svg></button>
            <div class="drop drop--products" id="drop-products" data-section>
              <ul class="drop__list">
                <li><a class="drop__link" href="#"><span class="drop__icon" style="--tint: #ece8ff; --mark: #6246ea"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5 19V11M12 19V5M19 19v-5"/></svg></span><span class="drop__text"><b>Analytics</b><small>See what happens on your site</small></span></a></li>
                <li><a class="drop__link" href="#"><span class="drop__icon" style="--tint: #dff6ec; --mark: #138a5a"><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="drop__text"><b>Automations</b><small>Let the routine run by itself</small></span></a></li>
                <li><a class="drop__link" href="#"><span class="drop__icon" style="--tint: #ffeedd; --mark: #d0611b"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M7 3h7l4 4v14H7zM14 3v4h4M10 12h5M10 16h5"/></svg></span><span class="drop__text"><b>Reports</b><small>Numbers your team can read</small></span></a></li>
              </ul>
            </div>
          </li>
          <li>
            <button class="nav__trigger" type="button" aria-expanded="false" aria-controls="drop-solutions" data-trigger data-autoclic>Solutions<svg class="nav__caret" viewBox="0 0 14 14" aria-hidden="true"><path d="M3.5 5.5 7 9l3.5-3.5"/></svg></button>
            <div class="drop drop--solutions" id="drop-solutions" data-section>
              <div class="drop__column">
                <p class="drop__heading">By size</p>
                <a class="drop__plain" href="#">Startups</a>
                <a class="drop__plain" href="#">Agencies</a>
                <a class="drop__plain" href="#">Enterprise</a>
              </div>
              <div class="drop__column">
                <p class="drop__heading">By field</p>
                <a class="drop__plain" href="#">Retail</a>
                <a class="drop__plain" href="#">Education</a>
                <a class="drop__plain" href="#">Nonprofits</a>
              </div>
            </div>
          </li>
          <li>
            <button class="nav__trigger" type="button" aria-expanded="false" aria-controls="drop-developers" data-trigger data-autoclic>Developers<svg class="nav__caret" viewBox="0 0 14 14" aria-hidden="true"><path d="M3.5 5.5 7 9l3.5-3.5"/></svg></button>
            <div class="drop drop--developers" id="drop-developers" data-section>
              <a class="drop__card" href="#">
                <svg class="drop__glyph" 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>
                <b>Read the docs</b>
                <small>Everything you need to build with us, from the first call to the last.</small>
              </a>
              <ul class="drop__list drop__list--small">
                <li><a class="drop__link drop__link--small" href="#"><span class="drop__mini"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 6h16M4 12h16M4 18h10"/></svg></span><b>API reference</b></a></li>
                <li><a class="drop__link drop__link--small" href="#"><span class="drop__mini"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5 4h10l4 4v12H5zM9 12h6M9 16h4"/></svg></span><b>Guides</b></a></li>
                <li><a class="drop__link drop__link--small" href="#"><span class="drop__mini"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 7v5l3 2M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0z"/></svg></span><b>Changelog</b></a></li>
                <li><a class="drop__link drop__link--small" href="#"><span class="drop__mini"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M3 12h4l3-7 4 14 3-7h4"/></svg></span><b>Status</b></a></li>
              </ul>
            </div>
          </li>
          <li><a class="nav__trigger nav__trigger--link" href="#">Pricing</a></li>
        </ul>
        <!-- the white panel behind the lists, and its little arrow: they move, the lists take turns on top -->
        <span class="panel" data-panel aria-hidden="true"></span>
        <span class="arrow" data-arrow aria-hidden="true"></span>
      </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.

40 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 dropdown that changes shape” from Animorio and fit it into my site

What it does

A bar on a white background with three menus, Products, Solutions and Developers, and a link, Pricing. The three menus share a white panel: pointing at a button opens its list below, rising 10 pixels; moving to the next button the panel changes width, height and place in half a second, its little arrow slides under the new button, the old list leaves towards the side it is leaving and the new one comes in from the side it comes from, always cut inside the panel. Products has three entries with icons on coloured squares, Solutions two columns of links, Developers a dark card and a list. When the pointer leaves, the panel waits a fifth of a second and closes, and an invisible bridge between the bar and the panel lets you go down into the list without closing it. On a phone and with the keyboard a button opens and closes its list, the down arrow goes to the first entry, Esc closes and gives the focus back to the button, and a tap outside closes.

How it works

One panel for all

The panel is a single element behind the lists. GSAP takes its x, width and height to those of the list to show, measured each time with offsetWidth and offsetHeight, in MORPH seconds with expo.out. The list goes under its button, kept inside the window, and the little arrow under the button but at least 26 pixels from the panel’s corners.

The lists that slide

The direction comes from comparing the new menu with the old one: the old list moves SLIDE pixels towards the opposite side and vanishes in 0.18 seconds, the new one arrives from SLIDE pixels on the other side in 0.45 seconds with power3.out.

Cut to the panel

On every update each visible list gets a clip-path inset worked out from where it and the panel are at that moment: while the panel changes shape, nothing sticks out of its edges.

The lists in the page

In the code each list sits right after its button: so the Tab key goes from the button into its list and then to the next button. The hidden lists have visibility: hidden and stay out of the way.

Mouse, finger and keyboard

The pointer opens by going over a button, and with the mouse a click only opens; a finger, the keyboard or a script open and close. Once the pointer leaves, closing waits GRACE seconds; the bridge is an ::after of the bar as tall as the space between bar and panel (GAP).

Values to change

NameFileDefaultWhat it changes
MORPHscript.js0.5The seconds of the panel changing size and place.
SLIDEscript.js36The pixels the lists slide when going from one menu to another.
LIFTscript.js10The pixels the panel rises from when it opens.
GRACEscript.js0.2The seconds the panel waits, after the pointer leaves, before closing.
GAPscript.js12The pixels between the bar and the panel.

Try them in the Playground

When to use it

For the navigation of a site with rich menus (products, solutions, documentation) where each menu holds something different. For a single menu a plain dropdown is enough.

On phones

The buttons tighten to fit a 390 pixel screen; a tap opens and closes, the panel stays inside the screen, and the columns of Solutions and the card of Developers stack one under the other.

With reduced motion

The panel does not travel and the lists do not slide: changing menu is a quick fade, and so are opening and closing.

Pitfalls

  • The lists must be cut to the panel while it changes shape: without it, the leaving one stays visible outside its edges for a moment.
  • There is a gap between the bar and the panel: without a bridge (an ::after of the bar) the pointer crosses it, the bar loses it and the panel closes before you get there.
  • With the mouse a click on the button must not close: the pointer has just opened it by going over it, and a click that closes would look like a fault.
  • The bar’s container must not stretch: in a grid the items stretch in height (align-items: stretch), and the panel, hung from the bottom of the bar, would end up hundreds of pixels lower.

Libraries

GSAP 3.15.0, Plus Jakarta Sans (font variabile) 5.3.0.

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

Testing

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