Vai al contenuto
animorio
EN
Torna alla libreria

Le cartelle con le linguette

ScrollSolo CSS, GSAPFacilePeso compresso: Demo 6,3 KBGSAP + ScrollTrigger 45 KBGratisScarica lo zip

Scorri dentro l’anteprima

Cinque cartelle che scorrendo si impilano: ognuna si ferma alla stessa altezza, e le linguette, una più in là dell’altra, restano in fila come i divisori di uno schedario. Cliccando una linguetta si torna alla sua cartella.

Il codice

Una cartella che funziona da sola: index.html, style.css, script.js e le librerie da CDN. Nelle schede React e Vue la stessa demo è già un componente, che spegne le sue animazioni quando esce dalla pagina.

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>The tabbed folders</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/anybody@5.3.0/wdth.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/geist-mono@5.3.0/index.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <header class="top">
    <p class="brand">Offcut</p>
    <p class="top__line">Selected jobs</p>
  </header>

  <main>
    <section class="intro">
      <h1 class="intro__title">Five jobs,<br>filed.</h1>
      <p class="intro__text">Every job at Offcut lives in a folder. Scroll, and each one slides over the one before; the tabs stay in a row, so you can always pull a folder back out.</p>
    </section>

    <!-- every folder sticks at the same height; its tab sits one place further along, so the row fills up -->
    <ul class="drawer" data-drawer style="--count: 5">
      <li class="folder" data-folder style="--n: 0; --tint: #f7f5ee; --on: #0e1440">
        <div class="folder__row"><button class="folder__tab" type="button" data-tab aria-label="Go to Ferry timetable"><span>Ferry</span></button></div>
        <article class="folder__body">
          <div class="folder__inside">
            <div class="folder__text">
              <p class="folder__date">March 2026</p>
              <h2 class="folder__title">Ferry timetable</h2>
              <p class="folder__line">Forty crossings a day, set so you can read them from the pier.</p>
              <ul class="folder__tags"><li>Timetable</li><li>Wayfinding</li></ul>
            </div>
            <figure class="folder__art" aria-hidden="true">
              <svg viewBox="0 0 400 300" preserveAspectRatio="xMidYMid slice">
                <rect width="400" height="300" fill="#2343e6"/>
                <circle cx="306" cy="80" r="38" fill="#ffd21f"/>
                <rect x="212" y="112" width="24" height="36" fill="#0e1440"/>
                <rect x="212" y="120" width="24" height="8" fill="#ffd21f"/>
                <rect x="132" y="146" width="126" height="34" fill="#ff5b4a"/>
                <g fill="#f7f5ee"><rect x="146" y="156" width="16" height="13"/><rect x="172" y="156" width="16" height="13"/><rect x="198" y="156" width="16" height="13"/><rect x="224" y="156" width="16" height="13"/></g>
                <path d="M84 180 H312 L284 222 H112 Z" fill="#f7f5ee"/>
                <path d="M0 228 q25 -16 50 0 t50 0 t50 0 t50 0 t50 0 t50 0 t50 0 t50 0 V300 H0 Z" fill="#1a31b4"/>
                <path d="M-25 256 q25 -16 50 0 t50 0 t50 0 t50 0 t50 0 t50 0 t50 0 t50 0 t50 0 V300 H-25 Z" fill="#0e1440"/>
              </svg>
              <i></i><i></i><i></i><i></i>
            </figure>
          </div>
        </article>
      </li>

      <li class="folder" data-folder style="--n: 1; --tint: #7df0c4; --on: #0e1440">
        <div class="folder__row"><button class="folder__tab" type="button" data-tab aria-label="Go to Seed packets"><span>Seeds</span></button></div>
        <article class="folder__body">
          <div class="folder__inside">
            <div class="folder__text">
              <p class="folder__date">January 2026</p>
              <h2 class="folder__title">Seed packets</h2>
              <p class="folder__line">A packet for every seed in the neighbourhood library, printed in two inks.</p>
              <ul class="folder__tags"><li>Packaging</li><li>Riso</li></ul>
            </div>
            <figure class="folder__art" aria-hidden="true">
              <svg viewBox="0 0 400 300" preserveAspectRatio="xMidYMid slice">
                <rect width="400" height="300" fill="#f7f5ee"/>
                <g transform="rotate(-11 122 156)">
                  <rect x="70" y="66" width="104" height="172" rx="6" fill="#0e1440"/>
                  <g transform="translate(122 150)" fill="#7df0c4"><rect x="-3" y="-6" width="6" height="52"/><path d="M0 6 C-30 2 -36 -30 -36 -30 C-8 -32 0 -14 0 6 Z"/><path d="M0 -6 C26 -10 32 -40 32 -40 C8 -40 0 -24 0 -6 Z"/></g>
                  <rect x="86" y="212" width="72" height="8" fill="#f7f5ee"/>
                </g>
                <g transform="rotate(3 200 150)">
                  <rect x="148" y="52" width="104" height="184" rx="6" fill="#ff5b4a"/>
                  <g transform="translate(200 146)" fill="#0e1440"><rect x="-3" y="-6" width="6" height="56"/><path d="M0 8 C-30 4 -36 -28 -36 -28 C-8 -30 0 -12 0 8 Z"/><path d="M0 -4 C26 -8 32 -38 32 -38 C8 -38 0 -22 0 -4 Z"/></g>
                  <rect x="164" y="210" width="72" height="8" fill="#f7f5ee"/>
                </g>
                <g transform="rotate(15 282 160)">
                  <rect x="230" y="70" width="104" height="172" rx="6" fill="#ffd21f"/>
                  <g transform="translate(282 154)" fill="#0e1440"><rect x="-3" y="-6" width="6" height="52"/><path d="M0 6 C-30 2 -36 -30 -36 -30 C-8 -32 0 -14 0 6 Z"/><path d="M0 -6 C26 -10 32 -40 32 -40 C8 -40 0 -24 0 -6 Z"/></g>
                  <rect x="246" y="216" width="72" height="8" fill="#0e1440"/>
                </g>
              </svg>
              <i></i><i></i><i></i><i></i>
            </figure>
          </div>
        </article>
      </li>

      <li class="folder" data-folder style="--n: 2; --tint: #0e1440; --on: #f7f5ee">
        <div class="folder__row"><button class="folder__tab" type="button" data-tab aria-label="Go to Night market"><span>Market</span></button></div>
        <article class="folder__body">
          <div class="folder__inside">
            <div class="folder__text">
              <p class="folder__date">October 2025</p>
              <h2 class="folder__title">Night market</h2>
              <p class="folder__line">Posters that glow under the lamps: one for each of the six nights.</p>
              <ul class="folder__tags"><li>Posters</li><li>Screen print</li></ul>
            </div>
            <figure class="folder__art" aria-hidden="true">
              <svg viewBox="0 0 400 300" preserveAspectRatio="xMidYMid slice">
                <rect width="400" height="300" fill="#1a31b4"/>
                <path d="M318 34 a46 46 0 1 0 34 78 a36 36 0 1 1 -34 -78 Z" fill="#ffd21f"/>
                <path d="M0 92 Q200 150 400 80" fill="none" stroke="#f7f5ee" stroke-width="2.5"/>
                <g fill="#ffd21f"><rect x="52" y="106" width="16" height="24" rx="5"/><rect x="122" y="120" width="16" height="24" rx="5"/><rect x="192" y="125" width="16" height="24" rx="5"/><rect x="262" y="118" width="16" height="24" rx="5"/></g>
                <g>
                  <rect x="46" y="200" width="7" height="72" fill="#f7f5ee"/><rect x="166" y="200" width="7" height="72" fill="#f7f5ee"/>
                  <path d="M30 168 H190 L176 204 H44 Z" fill="#ff5b4a"/>
                  <g fill="#f7f5ee"><path d="M62 168 H94 L90 204 H64 Z"/><path d="M126 168 H158 L156 204 H130 Z"/></g>
                  <rect x="40" y="244" width="140" height="30" fill="#f7f5ee"/>
                </g>
                <g>
                  <rect x="232" y="212" width="7" height="60" fill="#f7f5ee"/><rect x="348" y="212" width="7" height="60" fill="#f7f5ee"/>
                  <path d="M216 182 H372 L358 216 H230 Z" fill="#7df0c4"/>
                  <g fill="#0e1440"><path d="M248 182 H278 L274 216 H250 Z"/><path d="M310 182 H340 L338 216 H314 Z"/></g>
                  <rect x="226" y="250" width="136" height="24" fill="#f7f5ee"/>
                </g>
              </svg>
              <i></i><i></i><i></i><i></i>
            </figure>
          </div>
        </article>
      </li>

      <li class="folder" data-folder style="--n: 3; --tint: #ffd21f; --on: #0e1440">
        <div class="folder__row"><button class="folder__tab" type="button" data-tab aria-label="Go to Paper zoo"><span>Zoo</span></button></div>
        <article class="folder__body">
          <div class="folder__inside">
            <div class="folder__text">
              <p class="folder__date">June 2025</p>
              <h2 class="folder__title">Paper zoo</h2>
              <p class="folder__line">A book to cut up: eleven animals, and not a drop of glue.</p>
              <ul class="folder__tags"><li>Book</li><li>Die cut</li></ul>
            </div>
            <figure class="folder__art" aria-hidden="true">
              <svg viewBox="0 0 400 300" preserveAspectRatio="xMidYMid slice">
                <rect width="400" height="300" fill="#f7f5ee"/>
                <path d="M70 44 H330 V262 H70 Z" fill="none" stroke="#0e1440" stroke-width="2.5" stroke-dasharray="9 8"/>
                <path d="M112 74 L200 158 L112 196 Z" fill="#ff5b4a"/>
                <path d="M288 74 L200 158 L288 196 Z" fill="#ff5b4a"/>
                <path d="M112 196 L200 158 L288 196 L200 248 Z" fill="#e8402f"/>
                <path d="M150 204 L200 182 L250 204 L200 248 Z" fill="#f7f5ee" stroke="#0e1440" stroke-width="0"/>
                <path d="M186 232 H214 L200 248 Z" fill="#0e1440"/>
                <path d="M150 150 l18 10 l-18 8 Z" fill="#0e1440"/>
                <path d="M250 150 l-18 10 l18 8 Z" fill="#0e1440"/>
              </svg>
              <i></i><i></i><i></i><i></i>
            </figure>
          </div>
        </article>
      </li>

      <li class="folder" data-folder style="--n: 4; --tint: #ff5b4a; --on: #0e1440">
        <div class="folder__row"><button class="folder__tab" type="button" data-tab aria-label="Go to Open Tuesday"><span>Tuesday</span></button></div>
        <article class="folder__body">
          <div class="folder__inside">
            <div class="folder__text">
              <p class="folder__date">April 2025</p>
              <h2 class="folder__title">Open Tuesday</h2>
              <p class="folder__line">Signs for a repair café that opens one evening a week.</p>
              <ul class="folder__tags"><li>Signage</li><li>Stencil</li></ul>
            </div>
            <figure class="folder__art" aria-hidden="true">
              <svg viewBox="0 0 400 300" preserveAspectRatio="xMidYMid slice">
                <rect width="400" height="300" fill="#0e1440"/>
                <path d="M136 92 q-14 -16 0 -32 M170 92 q-14 -16 0 -32 M204 92 q-14 -16 0 -32" fill="none" stroke="#f7f5ee" stroke-width="7" stroke-linecap="round"/>
                <path d="M226 144 h20 a28 28 0 0 1 0 56 h-20" fill="none" stroke="#f7f5ee" stroke-width="15"/>
                <path d="M104 116 H236 V206 a30 30 0 0 1 -30 30 H134 a30 30 0 0 1 -30 -30 Z" fill="#f7f5ee"/>
                <g transform="rotate(34 300 176)">
                  <rect x="289" y="120" width="22" height="132" rx="7" fill="#ffd21f"/>
                  <circle cx="300" cy="118" r="28" fill="#ffd21f"/>
                  <rect x="290" y="82" width="20" height="38" fill="#0e1440"/>
                </g>
              </svg>
              <i></i><i></i><i></i><i></i>
            </figure>
          </div>
        </article>
      </li>
    </ul>

    <section class="outro">
      <p class="outro__text">Next job: yours.</p>
    </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

Cambia valori, colori e testi con le manopole e guarda l’animazione rispondere dal vivo. Oppure scrivi tu nel codice. Poi scarica la cartella con le tue modifiche.

62 valori da cambiare

Oppure chiedila a Claude Code

Col connettore di Animorio, Claude Code la prende da qui con tutta la sua guida e la adatta al tuo progetto. Incolla questa richiesta:

Prendi «Le cartelle con le linguette» da Animorio e adattala al mio sito

Cosa fa

I lavori di uno studio inventato, ognuno nella sua cartella: carta, menta, blu notte, giallo, corallo. Scorrendo, la prima cartella si ferma in alto; la seconda le sale sopra e la copre, ma la sua linguetta sta un posto più a destra, e quella della prima resta in vista. Così fino alla quinta: alla fine le cinque linguette sono in fila, a scalini, coi bordi delle cartelle vecchie che spuntano dietro la nuova. Mentre viene coperta, una cartella perde il suo contenuto; quando una cartella arriva, la sua linguetta fa un saltello e compaiono le maniglie del riquadro intorno al disegno. Ogni linguetta è un bottone: riporta la pagina alla sua cartella.

Come funziona

Tutte alla stessa altezza

Le cartelle sono le voci di una lista, una dopo l’altra, ognuna con position: sticky e lo stesso top. Quando una arriva alla riga si ferma; la successiva, che nel codice viene dopo e quindi sta sopra, continua a salire e la copre. Fra una e l’altra c’è un margine di 14vh: è lo scorrimento in cui la cartella si vede intera.

Le linguette in fila

Ogni cartella è una riga con la linguetta e, sotto, il corpo. La linguetta è larga un quinto della cartella e ha un margine sinistro di tanti quinti quanti ne dice --n: 0 per la prima, 4 per l’ultima. Il resto della riga è vuoto, e da lì si vedono le linguette delle cartelle sotto. Il lato inclinato è un triangolo ritagliato con clip-path.

La pila che tiene

Un elemento sticky deve restare dentro il contenuto di chi lo contiene, margine compreso. Per questo la lista finisce con un tratto vuoto, un ::after alto 36vh: finché quel tratto non è passato tutte le cartelle restano ferme, poi se ne vanno insieme.

Cosa aggiunge lo script

Un solo numero legato allo scorrimento con ScrollTrigger: quante cartelle sono arrivate sulla prima, frazioni comprese, da 0 a 4. Da lì, per ogni cartella, quante ne ha sopra: si alza di 7 px per ognuna (la variabile --rise), e mentre la prima di queste le sale sopra il suo contenuto scende al 15% di opacità (--fade). Lo scrub ha 0,4 secondi di ritardo.

L’arrivo e le linguette

Quando il numero passa un intero, la cartella arrivata è quella davanti: la sua linguetta salta di 9 px e ricade con un elastico, le quattro maniglie del disegno crescono una dopo l’altra, e la scritta della linguetta si accende. Il clic su una linguetta calcola dove la sua cartella si ferma (il punto della prima, più tante distanze quante cartelle) e ci porta la pagina, con lo scorrimento morbido del browser.

Valori da cambiare

NomeFileDi partenzaCosa cambia
--count, --nindex.html5, 0-4Quante cartelle ci sono e il posto di ognuna nella fila: da cambiare insieme quando ne aggiungi o ne togli una.
--tint, --onindex.html#f7f5ee, #0e1440Il colore di ogni cartella e quello dei suoi testi.
--top, --tab, --slantstyle.css58px, 46px, 22pxDove si fermano le cartelle, quanto sono alte le linguette e quanto è largo il loro lato inclinato.
LIFTscript.js7I pixel di cui una cartella si alza per ogni cartella che le arriva sopra.
FADEscript.js0.85Quanto contenuto perde una cartella coperta: 1 lo fa sparire del tutto.
SCRUBscript.js0.4I secondi che la pila impiega a raggiungere lo scorrimento.
.folder margin, .drawer::afterstyle.css14vh, 36vhLo scorrimento fra una cartella e l’altra, e quanto la pila resta ferma alla fine.

Provali nel Playground

Quando usarla

Per una lista corta di lavori, casi studio, servizi o capitoli, da tre a sei, dove conta vedere subito quanti sono e poterci tornare: il portfolio di uno studio, l’indice di un rapporto, le tappe di un percorso.

Sul telefono

Uguale, con le linguette più strette e le scritte a 11 px; dentro la cartella il disegno va sotto il testo e si prende l’altezza che resta.

Con il movimento ridotto

Le cartelle si impilano lo stesso, perché è la pagina che scorre, ma non si alzano, il contenuto non sfuma e le linguette non saltano; il clic su una linguetta porta alla cartella senza scorrimento animato.

Trappole

  • Il padding in fondo alla lista non basta a tenere ferma la pila: una scatola sticky deve restare dentro il contenuto del contenitore, e il padding non è contenuto. Serve un tratto vuoto vero, come un ::after.
  • Se l’ultima cartella non ha lo stesso margine delle altre, le altre sono più alte di lei di un margine e vengono spinte su proprio mentre lei arriva.
  • La riga della linguetta è larga quanto la cartella anche dove è vuota: senza pointer-events: none sulla cartella (e auto su linguetta e corpo) i clic non arrivano alle linguette delle cartelle sotto.
  • Un transform sulla cartella non sposta il punto in cui si ferma: per alzarla si può usare translateY. Cambiare top, invece, cambierebbe la pila.
  • offsetTop di una scatola sticky è la posizione di adesso, non quella di partenza: la distanza fra due cartelle va calcolata da altezza più margine.
  • La cartella deve stare tutta nello schermo, linguetta compresa: l’altezza del corpo è il minimo fra 540 px e lo schermo meno riga, linguetta e un margine.

Librerie

GSAP 3.15.0, ScrollTrigger (GSAP) 3.15.0, Anybody (font variabile) 5.3.0, Geist Mono (font variabile) 5.3.0.

Compressi con gzip, i tre file della demo pesano 6,3 KB; le librerie: GSAP + ScrollTrigger 45 KB.

Collaudo

Provata il 30 settembre 2026

  • Computer: Fatto
  • Telefono: Fatto
  • Movimento ridotto: Fatto
  • Safari su iPhone: Fatto

Licenza d’uso

Puoi usarla nei tuoi progetti e in quelli dei tuoi clienti. Non puoi rivenderla né ridistribuirla così com’è. GSAP resta sotto la sua licenza Standard. Licenza completa.

Tutte le animazioni