Skip to content
animorio
IT
All components

The browser tabs

Navigation, SVG, PhysicsGSAPAdvancedCompressed weight: Demo 9.3 KBGSAP 28 KBFreeDownload the zip

A strip of tabs like the top of a browser, with no brand: drag them and the others step aside on springs, the one in your hand leans with its speed while its curved feet stay planted on the bar. The plus opens a tab that grows out of the button, the cross closes one that shrinks away, and with many tabs only the icons are left.

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 browser tabs</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/geist@5.3.0/index.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage">
    <!-- a strip of tabs like a browser's: drag them and the others move aside on springs, the dragged one leans with
         its speed; the active tab is one SVG shape with curved feet that melts into the bar underneath -->
    <div class="browser" data-browser>
      <div class="strip" data-strip>
        <svg class="sheet" aria-hidden="true" data-sheet><path class="sheet__shade" data-shade/><path class="sheet__body" data-body/><path class="sheet__edge" data-edge/></svg>
        <div class="tabs" role="tablist" aria-label="Open tabs" aria-describedby="tabs-help" data-tabs>
          <div class="tab" role="tab" data-icon="mail" data-autoclic>Inbox</div>
          <div class="tab" role="tab" data-icon="road" data-active>Q3 roadmap</div>
          <div class="tab" role="tab" data-icon="plane" data-autoclic>Flight to Lisbon</div>
          <div class="tab" role="tab" data-icon="pot">Weeknight recipes</div>
          <div class="tab" role="tab" data-icon="notes" data-autoclic>Design review notes</div>
          <div class="tab" role="tab" data-icon="peak">Trail maps</div>
        </div>
        <button class="plus" type="button" aria-label="New tab" data-plus>
          <svg viewBox="0 0 16 16" aria-hidden="true"><path d="M8 3v10M3 8h10"/></svg>
        </button>
      </div>
      <!-- the bar under the active tab: the same colour, so the tab and the bar are one piece -->
      <div class="bar" role="tabpanel" data-panel>
        <div class="bar__nav" aria-hidden="true">
          <svg viewBox="0 0 16 16"><path d="M10 3.5L5.5 8l4.5 4.5"/></svg>
          <svg viewBox="0 0 16 16"><path d="M6 3.5L10.5 8 6 12.5"/></svg>
          <svg viewBox="0 0 16 16"><path d="M12.6 8.6a4.7 4.7 0 1 1-1.5-4.1"/><path d="M11.6 1.9v2.9H8.7"/></svg>
        </div>
        <div class="bar__address">
          <span class="bar__icon" data-bar-icon></span>
          <span class="bar__title" aria-live="polite" data-bar-title></span>
        </div>
      </div>
      <p class="sr" id="tabs-help">Arrow keys switch tabs, Shift with an arrow moves a tab, Delete closes it.</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.

26 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 browser tabs” from Animorio and fit it into my site

What it does

On a black background, the top of a dark browser window: a strip with six tabs and, below, the bar with the arrows and a rounded field with the page’s title. Every tab has its little icon drawn in SVG, a small rounded square of colour with a white drawing (an envelope for Inbox, bars for Q3 roadmap, a plane for Flight to Lisbon, a pot for Weeknight recipes), the title fading out at its end instead of an ellipsis, and a cross. The active tab is a single grey shape with rounded top corners and two curved feet at the bottom that flare into the bar: tab and bar are one piece. Between the idle tabs runs a thin line, which disappears next to the active tab and next to the one under the pointer. Switching tabs the shape runs to the new one, its front edge leaving first and the back edge following more softly, and the title in the bar comes in from below. Taking a tab and dragging it, the others slide aside on springs to make room; the tab in your hand leans a little with its speed, as if the air held it back, while its feet stay on the bar; let go, it drops into its place with the swing it had. The plus opens a new tab that grows out of the button pushing the others; the cross closes a tab that shrinks and fades, and the others only widen when the pointer leaves the strip, so the next cross stays under the pointer. With many tabs they narrow: first the title goes, then the cross, and the icons are left, while the active tab keeps the width for its title as long as there is room.

How it works

Places, by hand

The room between the two ends, minus the plus, is shared among the open tabs within TAB_WIDTH; when they get narrow, the active tab keeps ACTIVE_MIN and the others share the rest. So every tab has a target x and width, and the plus sits right after the last one. After a close with the mouse the widths may shrink but not grow, until the pointer leaves the strip.

Everything on springs

Every tab’s x and width, the plus’s x and the two edges of the active shape are numbers with a speed, pulled towards their goal by a damped spring (SPRING) in a requestAnimationFrame loop, with the step capped at 1/30 of a second. The loop switches off when everything is still. A new tab starts zero wide at the plus’s x; a closed one has a target width of zero and leaves the page when it gets there.

The shape with feet

The active tab is an SVG path rewritten every frame from its two edges: from the left foot (a quarter circle of radius FOOT curving into the bar) up the side, round the top corners, down the other side and out along the right foot. When the tab changes, the edge on the side it is going uses the full spring and the other a softer one (TRAIL), so the shape stretches and then gathers.

Dragging

With pointer events and setPointerCapture the tab you take follows the hand; its speed, filtered, gives the lean (LEAN degrees per pixel a second, up to MAX_LEAN), on a spring of its own that brings it upright. The content rotates around its base and in the shape only the top corners move: the feet stay on the bar. The tab’s place is the number of other tabs whose middle it has passed; let go, it drops into its place with the hand’s speed.

Keyboard and screen readers

The strip is a tablist with a single element reachable with Tab (roving tabindex): the arrows switch tabs, Home and End go to the ends, Shift and an arrow move the tab one place, Delete closes it. The bar below is the tabpanel, labelled by the active tab, and the order of the elements in the page follows the order on screen after every move.

Values to change

NameFileDefaultWhat it changes
SPRINGscript.js[320, 0.72]The tabs going to their place: the strength of the spring and its damping (1 = no bounce).
TRAILscript.js0.42How much softer the spring of the active shape’s back edge is: lower, it stretches more.
LEANscript.js0.011The degrees a dragged tab leans for every pixel a second of speed.
MAX_LEANscript.js8The most a dragged tab leans, in degrees.
TAB_WIDTHscript.js[38, 224]The narrowest and the widest a tab may be, in pixels.
ACTIVE_MINscript.js118The width the active tab keeps while there is room, so its title shows.
FOOTscript.js9The radius of the active tab’s curved feet, in pixels.
NEW_TITLEscript.js'New tab'The title of a tab opened with the plus.

Try them in the Playground

When to use it

For an app with several documents open at once (an editor, a mail client, a design tool, a terminal in the browser), for the files of an IDE, or for a page that shows a product dressed as a browser. From two to about fifteen tabs.

On phones

At 390 pixels the tabs are narrow: the active tab keeps its title and cross, the others show only their icon. They drag with a finger (touch-action: pan-y, so a vertical finger still scrolls the page), a tap chooses one, and the plus opens a tab while there is room for one more icon. Nothing spills over sideways.

With reduced motion

No springs and no lean: the tabs go straight to their place, the active shape jumps to the new tab, the dragged one follows the hand and settles when let go; a closed tab disappears and a new one appears already wide. The title in the bar changes with a fade.

Pitfalls

  • The active shape sat under all the tabs: dragging it over a neighbour, the neighbour’s title showed through. The shape has a z-index above the idle tabs and below the active tab’s content.
  • Widening the other tabs right after a close moved the next cross away from the pointer, and the second click closed the wrong tab. As in real browsers, the widths wait for the pointer to leave the strip.
  • Leaning the whole shape lifted it off the bar: only the top corners move (by tan(angle) times the height) and the content rotates around its base, so the feet stay where they are.
  • Moving the element in the page during the drag lost the pointer capture: the order is kept in an array and the elements are reordered only at the end.
  • A button inside a role=tab is a control inside a control: the cross is hidden from screen readers, and from the keyboard the tab closes with Delete.

Libraries

GSAP 3.15.0, Geist (font variabile) 5.3.0.

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