Skip to content
animorio
IT
All components

The sidebar

Navigation, Transitions, SVGGSAPAdvancedCompressed weight: Demo 7.3 KBGSAP 28 KBFreeDownload the zip

A dark bar with the pages and their icons: a light pill slides under the page chosen, stretching on the way, and every icon has its own little movement. Projects opens its pages underneath, and the button at the top folds the bar down to its icons: from then on a page's name comes out beside the icon pointed at and slides from one icon to the next.

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 sidebar</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/atkinson-hyperlegible-next@5.3.0/index.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage">
    <!-- the sidebar: a light pill slides to the page chosen and every icon has its own little movement; Projects opens
         its pages underneath; the button at the top folds the bar down to its icons, and then the names come out
         beside the pointer -->
    <div class="sb-wrap" data-wrap>
      <nav class="sb" aria-label="Main" data-nav data-autoclic>
        <button class="sb__toggle" type="button" aria-expanded="true" aria-controls="sb-list" aria-label="Collapse the sidebar" data-toggle>
          <svg viewBox="0 0 24 24" aria-hidden="true"><rect x="3" y="4" width="18" height="16" rx="2.5"/><path d="M9.5 4V20"/><path class="i-chev" d="M15.5 9.5L13 12L15.5 14.5"/></svg>
        </button>
        <div class="sb__body" data-body>
          <span class="sb__pill" data-pill aria-hidden="true"></span>
          <ul class="sb__list" id="sb-list">
            <li><a class="sb__item" href="#" aria-current="page" data-item data-icon="home">
              <svg viewBox="0 0 24 24" aria-hidden="true"><path class="i-roof" d="M3.5 10.5L12 3.5L20.5 10.5"/><path d="M5.5 9V20H18.5V9"/><path class="i-door" d="M10 20V14.5H14V20"/></svg>
              <span class="sb__label">Home</span></a></li>
            <li><a class="sb__item" href="#" data-item data-icon="inbox">
              <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M3 13.5H8L9.5 16.5H14.5L16 13.5H21V20H3Z"/><g class="i-arrow"><path d="M12 3V11M8.5 7.5L12 11L15.5 7.5"/></g></svg>
              <span class="sb__label">Inbox</span></a></li>
            <li class="sb__group">
              <button class="sb__item sb__item--parent" type="button" aria-expanded="true" aria-controls="sb-projects" data-projects data-icon="projects">
                <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M3 19V5.5H9L11 7.5H19.5V11"/><path class="i-flap" d="M3 19L5.5 11H22L19.5 19Z"/></svg>
                <span class="sb__label">Projects</span>
                <svg class="sb__chev" viewBox="0 0 24 24" aria-hidden="true"><path d="M9.5 6.5L15 12L9.5 17.5"/></svg>
              </button>
              <div class="sb__sub" id="sb-projects" data-sub>
                <span class="sb__guide" data-guide aria-hidden="true"></span>
                <ul>
                  <li><a class="sb__item sb__item--child" href="#" data-item><span class="sb__label">Website</span></a></li>
                  <li><a class="sb__item sb__item--child" href="#" data-item><span class="sb__label">Mobile app</span></a></li>
                  <li><a class="sb__item sb__item--child" href="#" data-item><span class="sb__label">Brand book</span></a></li>
                </ul>
              </div>
            </li>
            <li><a class="sb__item" href="#" data-item data-icon="calendar">
              <svg viewBox="0 0 24 24" aria-hidden="true"><rect x="3" y="5" width="18" height="16" rx="2.5"/><path d="M8 3V7M16 3V7M3 10H21"/><rect class="i-day" x="13" y="13.5" width="4.5" height="4.5" rx="1"/></svg>
              <span class="sb__label">Calendar</span></a></li>
            <li><a class="sb__item" href="#" data-item data-icon="reports">
              <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M3 20.5H21"/><path class="i-bar" d="M6.5 18V12.5"/><path class="i-bar" d="M12 18V8"/><path class="i-bar" d="M17.5 18V4.5"/></svg>
              <span class="sb__label">Reports</span></a></li>
            <li><a class="sb__item" href="#" data-item data-icon="settings">
              <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 6.5H7M11 6.5H20M4 12H13M17 12H20M4 17.5H6M10 17.5H20"/><path class="i-knob" d="M9 4V9"/><path class="i-knob" d="M15 9.5V14.5"/><path class="i-knob" d="M8 15V20"/></svg>
              <span class="sb__label">Settings</span></a></li>
          </ul>
        </div>
      </nav>
      <!-- the name of an icon, beside it, while the bar is folded -->
      <span class="sb__tip" aria-hidden="true" data-tip><span data-tip-text></span></span>
    </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.

21 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 sidebar” from Animorio and fit it into my site

What it does

On a black background an almost black bar with rounded corners: at the top a button with a sidebar drawing, then Home, Inbox, Projects with its pages Website, Mobile app and Brand book, Calendar, Reports and Settings, each with its icon. Under Home there is a grey pill with a small purple bar on its left edge. On arrival the rows come in from the left one after the other, the pill widens under Home and the little house opens its door. Choosing another page the pill goes there with its leading edge leaving at once and the trailing one following, stretching on the way and gathering itself on arrival, and the icon does its movement: the house's door opens, the arrow drops into the tray, the folder opens, the day comes up on the calendar, the bars grow, the sliders slide; pointing at a page with the mouse plays the same movement. With one of Projects' pages chosen, Projects stays bright too. Projects opens and closes its pages: the arrow turns, the guide line comes down from its icon and the pages come in from the left, while the rows below move down and the pill follows them. The button at the top folds the bar: its little arrow turns, the names fade, the bar narrows to the icons alone and Projects' pages close; from then on, pointing at an icon, its name comes out beside it in a label, which slides from one icon to the next changing its word. Tapping Projects while folded, the bar opens again with its pages; open again, the names come back one after the other.

How it works

The pill on two springs

The pill is a box under the list, whose top and height the script sets. Its two edges are two springs with no bounce that chase the row of the page chosen every frame (offsetTop added up going through the offsetParents up to the list): the edge on the side the pill is going has the stiffer spring, which takes about SLIDE seconds, the other TRAIL times it, so the pill stretches and gathers itself.

Following the row

When the rows move (Projects opening or closing, the bar folding) the pill moves with its row by the same amount in the same frame, and the springs only do the journey from one page to another. While Projects' pages are hidden, the pill sits on Projects.

The icons

Each icon has a paused timeline on the parts of its SVG (the roof and the door, the arrow, the folder's front, the day, the bars, the sliders' ticks), which starts again from the beginning (restart) when the page is chosen or pointed at; at rest each one shows how its movement ends (progress(1)).

Projects and the folded bar

The box of Projects' pages animates its height between zero and its list's height (power3.inOut, 0.45 seconds), the arrow turns 90 degrees, the guide comes down (scaleY) and the pages come in one after the other; hidden pages are inert, out of Tab's way. Folding, the names fade moving 6 pixels one after the other, the width goes from 248 to 66 pixels in FOLD seconds and Projects' pages close; opening does the opposite, and at the end the pages open again if they were open.

The label, the keys and the preview

With the bar folded, pointing at an icon or reaching it with Tab shows its name in a label beside it: it comes in from 8 pixels to the left with back.out, slides to the next icon in 0.28 seconds and its word rolls; Escape or leaving the bar hides it, and after a tap it stays for 1.2 seconds. The button at the top has aria-expanded and its label says Collapse or Expand the sidebar. A click from a script on the bar (the library's preview) plays the next step of a little tour: Inbox, Website, fold, Calendar with its name, open, close Projects, Home, open Projects again.

Values to change

NameFileDefaultWhat it changes
SLIDEscript.js0.42Roughly the seconds the pill's leading edge takes to arrive.
TRAILscript.js0.55How stiff the trailing edge is next to the leading one: less stretches the pill more.
FOLDscript.js0.5The seconds the bar takes to fold or open.

Try them in the Playground

When to use it

For the bar of an app or an admin panel: an email client, a management tool, a project app, a site's dashboard, a booking tool, a control panel.

On phones

The bar is 248 pixels wide open and 66 folded, and fits a phone screen; the rows are 40 pixels tall (34 for Projects' pages). With no hover, on the folded bar a tap on an icon chooses the page and shows its name for 1.2 seconds.

With reduced motion

The pill jumps to the page chosen without stretching, the icons stay still, the bar folds and opens at once, Projects' pages appear and disappear without sliding and the name label appears and disappears without moving; on arrival the rows are already in place.

Pitfalls

  • While Projects' pages open, the rows below move: a pill sent with a tween towards a place taken at the start would land in the wrong place. That is why it follows its row every frame.
  • The pill moves with top and height, not with a transform: the transform stays free for its entrance, which GSAP animates with scaleX, and the two do not overwrite each other.
  • Names hidden with opacity stay readable for screen readers, which is what the folded bar needs; Projects' closed pages instead become inert, or Tab would land on invisible rows.
  • The bar has overflow hidden to cut the names while it narrows: that is why the name label sits outside the bar, beside it, or it would be cut too.
  • The icons start from the end of their movement (progress(1)): a paused fromTo writes its starting state at once, and Reports' bars, for example, would stay at zero until the page is chosen.

Libraries

GSAP 3.15.0, Atkinson Hyperlegible Next (font variabile) 5.3.0.

Compressed with gzip, the three demo files weigh 7.3 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