Skip to content
animorio
IT
Back to the library

The line of years

Scroll, TextGSAP, CSS onlyMediumCompressed weight: Demo 8.3 KBGSAP + ScrollTrigger 45 KBFreeDownload the zip

Scroll inside the preview

A coffee roaster’s story on a single thin vertical line: as you scroll, the ink runs down a ruler of ticks, the big year counts every year it passes and, at every year that matters, the line makes a vermilion notch and a sentence unrolls beside it, rolling back in when the next year comes. Minimal style.

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 line of years</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>
  <!-- the story of a made-up coffee roaster on a single thin vertical line. Scrolling down, the ink runs down the line
       like the hand of a ruler: the big year counts every year it passes, and at every year that matters the line
       makes a notch and a sentence unrolls beside it, then rolls back in when the next year comes -->
  <main>
    <section class="apertura">
      <p class="piccolo">Ashby Coffee Roasters</p>
      <h1 class="titolo">Sixty-seven years,<br>one line.</h1>
      <p class="piccolo piccolo--tenue">Scroll down</p>
    </section>

    <section class="racconto" data-racconto>
      <div class="palco">
        <header class="testa">
          <p class="marchio">Ashby</p>
          <p class="piccolo piccolo--tenue">Roasting since 1958</p>
        </header>

        <div class="scena" data-scena>
          <div class="guida" data-guida aria-hidden="true"></div>
          <div class="traccia" data-traccia aria-hidden="true"></div>
          <div class="righello" data-righello aria-hidden="true"></div>

          <ol class="tappe">
            <li class="tappa" data-tappa data-anno="1958">
              <i class="tappa__segno" aria-hidden="true"></i>
              <i class="tappa__filo" aria-hidden="true"></i>
              <span class="tappa__anno" aria-hidden="true">1958</span>
              <p class="tappa__frase"><span class="nascosto">1958. </span>A second-hand drum roaster in a garage on Mill Lane.</p>
            </li>
            <li class="tappa" data-tappa data-anno="1971">
              <i class="tappa__segno" aria-hidden="true"></i>
              <i class="tappa__filo" aria-hidden="true"></i>
              <span class="tappa__anno" aria-hidden="true">1971</span>
              <p class="tappa__frase"><span class="nascosto">1971. </span>The corner shop opens. Twelve seats, one grinder.</p>
            </li>
            <li class="tappa" data-tappa data-anno="1986">
              <i class="tappa__segno" aria-hidden="true"></i>
              <i class="tappa__filo" aria-hidden="true"></i>
              <span class="tappa__anno" aria-hidden="true">1986</span>
              <p class="tappa__frase"><span class="nascosto">1986. </span>Nora takes the drum over from her father.</p>
            </li>
            <li class="tappa" data-tappa data-anno="2004">
              <i class="tappa__segno" aria-hidden="true"></i>
              <i class="tappa__filo" aria-hidden="true"></i>
              <span class="tappa__anno" aria-hidden="true">2004</span>
              <p class="tappa__frase"><span class="nascosto">2004. </span>The first beans bought straight from a farm.</p>
            </li>
            <li class="tappa" data-tappa data-anno="2025">
              <i class="tappa__segno" aria-hidden="true"></i>
              <i class="tappa__filo" aria-hidden="true"></i>
              <span class="tappa__anno" aria-hidden="true">2025</span>
              <p class="tappa__frase"><span class="nascosto">2025. </span>A new roastery by the river. The old drum came too.</p>
            </li>
          </ol>

          <i class="ago" data-ago aria-hidden="true"></i>
          <p class="anno" data-grande aria-hidden="true">1958</p>
        </div>
      </div>
    </section>

    <section class="apertura apertura--fine">
      <h2 class="titolo titolo--medio">The drum is<br>still turning.</h2>
    </section>
  </main>

  <script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/gsap.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/ScrollTrigger.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.

37 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 line of years” from Animorio and fit it into my site

What it does

The about page of a made-up coffee roaster, Ashby, on a warm white with black ink and a single vermilion. The screen stops and a hairline draws itself from top to bottom; beside it drops a ruler of small ticks, one per year, a longer one every ten. A vermilion needle opens on the line, the ink runs down to 1958 and the big year comes in digit after digit. Then, as you scroll, the ink runs down like the hand of a gauge: the big year follows it and counts every year it passes, narrow and thin while it runs (Archivo’s width and weight axes), with each digit clicking in when it changes; the ticks near the tip lean out in a small wave. At every year that matters the ink stops, the ticks around it feel the knock and shiver, the year lands wide and heavy on an elastic, a vermilion notch springs out of the line, a hairline carries it on and a sentence unrolls beside it, word after word. When the next year comes, the sentence rolls back into the notch, the notch shrinks and turns black, and the year stays written beside it, small and grey. At the end a last wave runs down the whole ruler. On a phone the line sits on the left, the year hangs under the notch and the sentence goes under the year. Everything follows the scroll, forwards and backwards.

How it works

The years from the page

Every year that matters is an item of a list with data-anno and its sentence: the script reads them, sorts them and gets the first and the last year. That gives the scale: a function turns a year into a height on the line, and everything (ticks, tip, sentences) is placed with it. The ruler has one tick per year, from four years before the first to four after the last.

Numbers instead of properties

The timeline, tied to the scroll with scrub SCRUB, mostly moves numbers: the year the ink has reached, the needle, the knock on the ticks, and for every year the notch, the hairline, the sentence, its way back and the small year. On every update of the timeline a single function redraws everything from those numbers: the height of the ink with scaleY, the length of the ticks with scaleX, the cut of the sentence with clip-path.

The counting year

The big figure is the year reached, rounded: when a digit changes, its inner box makes a small jump with a timed tween (back.out), in the direction of the count. Two CSS variables, --wd and --wg, are Archivo’s wdth and wght axes: at the start of every run they drop to NARROW and THIN, on arrival they go back to WIDE and HEAVY along the LAND curve. Tabular digits keep the figure from dancing while it counts.

The wave on the ticks

Each tick of the ruler grows with its distance from the tip, along a bell (Math.exp of the squared distance) WAVE high. On arrival at a year the knock variable jumps up and goes back to zero on an elastic: the bell swells and shivers. Ticks already passed turn black, the ones to come stay light. The final wave is another bell running from top to bottom.

The sentence that unrolls

The sentence sits beside the notch, centred on its height (under the year on a phone), and is cut with clip-path: an inset from the right at 100% that opens to 0 with power3.out while the words, one per box, slide in with a stagger. To roll it back a second number closes the cut again, the words leave from the last one, and the notch goes short again, from vermilion to black with gsap.utils.interpolate.

Values to change

NameFileDefaultWhat it changes
NARROWscript.js64The width of the big year while it counts (62 is the narrowest).
WIDEscript.js100The width it lands on (125 is the widest).
THINscript.js260The weight of the year while it counts.
HEAVYscript.js780The weight it lands on.
NOTCHscript.js30How far the vermilion notch springs out of the line, in pixels.
WAVEscript.js1.6How much the ticks near the tip lean out.
LANDscript.js'elastic.out(1, 0.55)'The curve of the year landing and of the notch springing out: a smaller second number means more shiver.
SCRUBscript.js0.8How many seconds the drawing takes to catch up with the scroll.

Try them in the Playground

When to use it

For the story of a company, a shop, a studio or a family on an about page: four to six years that matter, one short sentence each. The years are changed in the HTML (data-anno and the sentence): the scale rebuilds itself. With more than six years the section must be made longer.

On phones

The same animations: the line moves to the left, the big year hangs under the notch instead of sitting left of the line, and the sentence goes under the year, on two lines if needed. This way the year going down never runs over the small years already written.

With reduced motion

No movement: line and ruler are already drawn. Scrolling steps from one year to the next: the big year and the sentence fade out and back in at their new place, the years already told stay written small, the ones to come are not there yet.

Pitfalls

  • The big figure had data-anno like the list items: querySelector('[data-anno]') took the first item, emptied it, and the real figure stayed frozen at the top. The elements the script looks for get attributes that do not repeat (here data-grande).
  • On a phone, with the year above the notch, the big year going down ran over the small years already written beside the ticks. Placed under the notch, the year always sits on the empty part of the line.
  • Words coming in from 14 pixels to the left overlapped the word before while arriving: the cut of the sentence already opens the space, so 6 pixels are enough.
  • With many fromTo on the same variables (--wd, --wg, the knock, the hairline) the timeline uses immediateRender: false on every tween and the starting values are set by hand first: otherwise the last tween built imposes its start from the very first frame.
  • Reading the sentence’s offsetHeight inside the redraw function forces the browser to lay out the page on every frame: heights are measured once, on ScrollTrigger’s refresh.

Libraries

GSAP 3.15.0, ScrollTrigger (GSAP) 3.15.0, Archivo (font variabile, asse wdth) 5.3.0.

Compressed with gzip, the three demo files weigh 8.3 KB; the libraries: GSAP + ScrollTrigger 45 KB.

Testing

Tested on 14 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 animations