Skip to content
animorio
IT
All components

The tape pagination

Navigation, Transitions, TextGSAP, CSS onlyAdvancedCompressed weight: Demo 12 KBGSAP 28 KBFreeDownload the zip

The pages of a list of results sit on a tape that runs through the bar, and the dots are its reels: changing page the tape runs, the numbers pass through the dark carriage of the current page and light up white, cut at its edge, the ones at the ends are wound narrow into the turning dots, and underneath the figures of the results roll.

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 tape pagination</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/archivo@5.3.0/wdth.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage">
    <!-- the pagination of a list of 472 results, ten to a page. The pages in between are numbers on a tape that runs
         through the bar, and the two sets of dots are its reels: going to another page the tape runs, the numbers
         slide through the dark carriage of the current page (lit white inside it), the ones at the ends are wound
         into the dots and new ones come out of the other side. Under the bar the range of results rolls, digit by
         digit. Without the script, the links below work as they are -->
    <div class="pager" data-pager>
      <nav class="bar" aria-label="Pagination" data-bar>
        <a class="key key--prev" href="?page=6" rel="prev" aria-label="Previous page" data-prev>
          <svg class="key__arrow" viewBox="0 0 24 24" aria-hidden="true"><path d="M14.25 6.5 8.75 12l5.5 5.5"/></svg>
        </a>

        <div class="track" data-track>
          <ol class="slots" data-slots>
            <li class="slot"><a class="slot__page" href="?page=1" aria-label="Page 1">1</a></li>
            <li class="slot"><span class="slot__gap" aria-hidden="true">&hellip;</span></li>
            <li class="slot"><a class="slot__page" href="?page=6" aria-label="Page 6">6</a></li>
            <li class="slot"><a class="slot__page" href="?page=7" aria-label="Page 7" aria-current="page">7</a></li>
            <li class="slot"><a class="slot__page" href="?page=8" aria-label="Page 8">8</a></li>
            <li class="slot"><span class="slot__gap" aria-hidden="true">&hellip;</span></li>
            <li class="slot"><a class="slot__page" href="?page=48" aria-label="Page 48">48</a></li>
          </ol>
          <!-- what the script draws: the carriage, the reels and the tape twice, the second one lit inside the carriage -->
          <span class="carriage" aria-hidden="true" data-carriage></span>
          <span class="reel" aria-hidden="true" data-reel><i></i><i></i><i></i></span>
          <span class="reel" aria-hidden="true" data-reel><i></i><i></i><i></i></span>
          <div class="tape" aria-hidden="true" data-tape></div>
          <div class="tape tape--lit" aria-hidden="true" data-lit></div>
        </div>

        <a class="key key--next" href="?page=8" rel="next" aria-label="Next page" data-next data-autoclic>
          <svg class="key__arrow" viewBox="0 0 24 24" aria-hidden="true"><path d="M9.75 6.5 15.25 12l-5.5 5.5"/></svg>
        </a>
      </nav>

      <p class="range">
        <span class="range__said" aria-live="polite" data-said>Results 61 to 70 of 472</span>
        <span class="range__shown" aria-hidden="true"><span class="odo" data-odo>61</span><span class="range__dash">&ndash;</span><span class="odo" data-odo>70</span> of <span data-total>472</span> results</span>
      </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.

24 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 tape pagination” from Animorio and fit it into my site

What it does

On a white background, alone in the middle, a pale grey tray 60 pixels tall with two round white keys at its ends, Previous and Next with drawn arrows, and between them the pages of 472 results, ten to a page: 1, the dots, 6, 7 and 8, the dots, 48. Page 7 sits inside a dark carriage with rounded corners, its number in white; under the bar a line reads 61–70 of 472 results. On arrival the bar rises gently and the tape runs from page 1 to page 7: the carriage slides to the middle, then stops and the numbers go by. Choosing another page the tape runs: the numbers pass through the carriage, and the part inside it is white and a little bolder, cut exactly at its edge; the ones that reach the ends narrow on the font's width axis, squash and vanish into the dots, while new ones come out of the other side and widen. The dots are two little reels: they turn half a turn for every page that goes into them and always come back level, and when the tape runs fast they spread a little. The carriage rides on a spring and the tape drags it a few pixels before it settles back; near the first and the last page the tape stops and the carriage travels instead, stretching with its speed. A far jump, from 11 to 1 or from 7 to 48, runs every page in between through, with a soft trail behind the numbers. Underneath, the figures of the results roll like those of a mileage counter, upwards when the number grows and downwards when it shrinks, and the extra digits open or close. With the mouse over an arrow the reels wind up a little that way and the arrow leans out; over a page a white plate appears and the carriage reaches towards it; over the dots the reels turn further, because a click there skips five pages. On the first or the last page the arrow that leads nowhere sinks into the tray, and clicking it knocks the carriage against the end.

How it works

One number

The tape is not a series of states but a number: the page as a fraction, which GSAP takes from the old page to the new one. From it each frame works out u, the page in the middle slot, held back near the ends (between the fourth and the fourth from last with one neighbour each side), and the carriage's slot, m + page − u: in the middle it stays put and the numbers run, near the ends u stops and the carriage travels. A jump of forty pages runs them all through, and a click halfway starts again from where the tape has got to.

The dots are the reels

Each page has its place on the tape, r = m + p − u, held between the slots of the two sets of dots. How much of it shows is the smaller of r − eL and the last slot − r − eR, where eL and eR say how open the dots are (0 when the slot holds the second or the second to last page). So a page going into the dots narrows on the font's wdth axis down to 62, squashes and fades in the very slot where the dots are forming, and the classic rules of pagination (dots only when they hide at least two pages) come out on their own.

The carriage lights the tape

The tape is drawn twice, the same: the dark numbers and over them the white ones, clipped on every frame with a clip-path inset taken from the carriage's size, rounded corners included. So a number halfway is white inside and dark outside, cut at the edge. Both layers share the same glyphs: the numbers' weight grows with their closeness to the carriage in both, otherwise the two halves would not match. The carriage follows its slot on a spring, the tape drags it with its speed and it stretches when it runs.

The rolling figures

Under the bar each number is a row of little windows, one per digit, each holding a column of figures written twice, so a window rolls on from 9 to 0 without going back. Each window goes to its new figure with the same duration and curve as the tape, and the leading digits that are not needed close up in width. A hidden aria-live region tells screen readers Results 71 to 80 of 472.

Real links, a drawn tape

Under the tape are the real slots: links with href ?page=N and aria-label Page N, aria-current on the current page, the dots as buttons Ahead 5 pages, to page 12. Their text is transparent and the numbers you see are on the tape, aria-hidden; without the script the links show and work. The slots change at once, the tape catches up; the keyboard focus moves to the new current page. When nothing moves the loop stops drawing. The library's preview clicks page 1, the page after the current one, the dots and Next in turn: they have data-autoclic.

Values to change

NameFileDefaultWhat it changes
RESULTSscript.js472How many results the list has.
PER_PAGEscript.js10The results on each page.
STARTscript.js7The page open when the component loads.
NEIGHBOURSscript.js1The pages shown each side of the current one (fewer on a narrow screen, if they do not fit).
SKIPscript.js5How many pages a click on the dots skips.
STEPscript.js0.5The seconds the tape takes to move by one page (a far jump a little longer).
FARscript.js1.1The longest a jump can take, in seconds, however far it goes.
TURNscript.js0.5How far the dots turn for each page wound into them, in turns (0.5: half a turn, and they are level again).
SWAYscript.js1How far the tape drags the carriage before it springs back (0 not at all).
SMEARscript.js1How much the numbers smear when the tape runs fast (0 no trail).
WINDscript.js0.16How far the reels wind up with the pointer on an arrow, in pages (0.16: a small turn).

Try them in the Playground

When to use it

Under a long list split into pages: the results of a search, the orders of a shop, a blog's archive, a table in a dashboard. The tape shows where you are going and how far, even on a jump of forty pages, and the line underneath says at once which results are there.

On phones

The bar narrows to the screen with a 16 pixel margin each side (358 pixels on a 390 phone) and drops to 56 tall, with 40 pixel keys; each page is a target of about 34 by 44 pixels. If the chosen neighbours do not fit with slots of at least 34 pixels fewer are shown, and a long number that does not fit its slot narrows on the width axis. A finger on an arrow winds the reels up like the mouse, and a page held down shrinks on the tape.

With reduced motion

The tape does not run: the numbers and the carriage fade out and back in already on the new page, the figures of the results swap with a fade, the dots do not turn. On arrival the component only fades in; no spring, no trail, no reels winding up.

Pitfalls

  • In Hubot Sans the tabular figures have a slashed zero and a one with a foot, like a code editor: 70 looked like 7Ø. It became Archivo, which has clean tabular figures and a width axis down to 62, more than the travel needed to squeeze a number into the dots.
  • The speed trail, a text-shadow on the tape layer, also smudged the first and the last page, which never move. Now those two have a class that takes it off.
  • The first trail was three sharp copies of the number: in the stills it looked like double vision. Now it is four soft copies fading away, and the trail never goes past three tenths of a slot: on a phone the trail of a two digit number reached over the next one.
  • With the mouse on Next the tape leaned out by a seventh of a page: at rest the dots stayed tilted and the next number peeped out squashed against them, and it looked like a fault. Now the tape stays still and only the reels wind up.
  • The carriage shifting four pixels towards the page under the mouse left its number off centre, like a misalignment. Now it reaches: only the edge on the side of the page grows, the other one stays put.
  • After a click on a page with the mouse still on it, the carriage kept reaching towards the slot it was in before, because pointerover does not fire again. Now after each change of page what is under the pointer is worked out again.
  • The slots are fixed and change number: pressing Enter on 8 the focus stayed in the same slot, which now held 9. Now the focus moves to the new current page; on the arrows it stays, until they sink.
  • The width of the digit windows was measured from the first figure, which, once the window had a width, measured the window itself: with the real font loaded they stayed as wide as the fallback font's. Now it measures a separate hidden figure.
  • With inline-flex on the results line the spaces round of disappeared (70of): the line went back to inline and only the numbers are inline-flex.
  • On a phone three digit numbers (249 250 251) did not fit in 36 pixel slots and 250 spilled out of the carriage: a number too long for its slot now narrows on the width axis, the same gesture as the dots.

Libraries

GSAP 3.15.0, Archivo (font variabile) 5.3.0.

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

Testing

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