
Standing letters with a long shadow
Text, Scroll, 3D
NewFree
Scroll inside the preview
A studio’s year in three huge figures, one at a time: each figure arrives as a thin red bar that grows to its measure, breaks into one piece per digit, and each piece rises up its digit like a blind, drawing it. Minimal style.
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>Bars that fold into figures</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/inter-tight@5.3.0/index.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<!-- the year of a made-up architecture studio in three figures, one at a time. Each figure arrives as a thin bar that
grows along a line to its measure, breaks into one piece per digit, and every piece rises up its digit like a
blind, drawing it; then the digits gain weight and the label slides out beside them -->
<main>
<section class="apertura">
<p class="piccolo">Norrow, annual report</p>
<h1 class="titolo">Our year,<br>measured.</h1>
<p class="piccolo piccolo--tenue">Three figures. Scroll.</p>
</section>
<section class="racconto" data-racconto>
<div class="palco">
<header class="testa">
<p class="marchio">Norrow</p>
<p class="piccolo piccolo--tenue">Annual report 2025</p>
</header>
<div class="scena" data-scena>
<div class="cifre">
<figure class="cifra" data-cifra>
<p class="cifra__numero" data-numero>148</p>
<figcaption class="cifra__dida">
<span class="cifra__riga"><span class="cifra__nome" data-scivola>Projects delivered</span></span>
<span class="cifra__riga"><span class="cifra__nota" data-scivola>Up from 112 the year before</span></span>
</figcaption>
</figure>
<figure class="cifra" data-cifra>
<p class="cifra__numero" data-numero>23</p>
<figcaption class="cifra__dida">
<span class="cifra__riga"><span class="cifra__nome" data-scivola>Countries we built in</span></span>
<span class="cifra__riga"><span class="cifra__nota" data-scivola>Six of them for the first time</span></span>
</figcaption>
</figure>
<figure class="cifra" data-cifra>
<p class="cifra__numero" data-numero>96%</p>
<figcaption class="cifra__dida">
<span class="cifra__riga"><span class="cifra__nome" data-scivola>Clients who came back</span></span>
<span class="cifra__riga"><span class="cifra__nota" data-scivola>And most of them brought a friend</span></span>
</figcaption>
</figure>
</div>
<div class="linea" data-linea aria-hidden="true">
<i class="tacca" data-tacca></i><i class="tacca" data-tacca></i><i class="tacca" data-tacca></i><i class="tacca" data-tacca></i><i class="tacca" data-tacca></i>
</div>
<div class="pezzi" data-pezzi aria-hidden="true"></div>
</div>
<nav class="voci" aria-label="The three figures">
<span class="voce" data-voce>Projects</span>
<span class="voce" data-voce>Countries</span>
<span class="voce" data-voce>Clients</span>
</nav>
</div>
</section>
<section class="apertura apertura--fine">
<h2 class="titolo titolo--medio">Thank you for<br>building with us.</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>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.
34 values to change
With the Animorio connector, Claude Code takes it from here, guide included, and fits it into your project. Paste this request:
Take “Bars that fold into figures” from Animorio and fit it into my siteThe script splits each figure into one element per digit and makes an invisible copy of it, always thin (weight THIN), in the same place. Measures are taken on the copy, so they do not change while the real digits gain weight. At the end of the copy there is a zero-height element sitting on the baseline: its edge gives the height of the hairline.
The box of a digit is wider than its drawing. With measureText on a canvas, set in the same font, you read where the ink of each digit starts and ends and how high it rises above the baseline: each piece of bar shrinks exactly to the width of its digit and stops exactly at its top.
The timeline, tied to the scroll with scrub SCRUB, does not move elements: it moves numbers from 0 to 1 (the bar growing, breaking, and for each digit the flight, the rise, the closing, the drop), all with fromTo. On every change a function redraws the figure from those numbers: position and scaleX of the pieces, the text of the count, the cut of the digits.
Each digit has a polygon clip-path whose top edge follows the top edge of its piece of bar: below the piece the digit shows, above it does not. Before starting, the cut sits under the lowest ink, so the bellies of the 3 and the 8 do not peek out; the curve of the rise is RISE, a back.out that takes the piece a little past the top before it settles.
When the pieces have closed, a CSS variable --w goes from THIN to BOLD with elastic.out and is the font-weight of the figure (Inter Tight is a variable font). The label sits beside the figure with left: 100%, so it follows it as it widens, and comes out of an overflow hidden window with xPercent from -104 to 0. On a phone it goes under the hairline.
| Name | File | Default | What it changes |
|---|---|---|---|
MEASURES | script.js | [0.74, 0.46, 0.96] | How far the bar of each figure grows, from 0 to 1 of the hairline (one per figure). |
THIN | script.js | 200 | The weight of the digits while the bar draws them. |
BOLD | script.js | 640 | The weight they settle on in the end. |
LINE | script.js | 2 | How thick the bar is, in pixels. |
RISE | script.js | 'back.out(1.3)' | The curve of each piece rising up its digit: the number in brackets is how far it goes past the top. |
STAGGER | script.js | 0.3 | The delay between one digit and the next, in steps of the story. |
SCRUB | script.js | 0.6 | How many seconds the drawing takes to catch up with the scroll. |
For the figures of an annual report, a yearly review, the about page of a studio or a company: a few important numbers, told one at a time. It works with short figures (up to four or five characters); with more than three figures the section must be made longer.
The same, with the figure larger in proportion to the screen and the label under the hairline instead of beside it.
No bars and no movement: the figures are already full with their label and, as you scroll, they take turns with a cross-fade; the names at the bottom light up in turn.
GSAP 3.15.0, ScrollTrigger (GSAP) 3.15.0, Inter Tight (font variabile) 5.3.0.
Compressed with gzip, the three demo files weigh 7.3 KB; the libraries: GSAP + ScrollTrigger 45 KB.
Tested on 14 October 2026
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.
With your account’s email and password.
or with your email
You’re signed in as .
With your account, you can download the zip of every animation and every pack, for free.