Vai al contenuto
animorio
EN
Tutti i componenti

L’indice della pagina

Navigazione, Scroll, SVG, FisicaGSAPMediaPeso compresso: Demo 10 KBGSAP 28 KBGratisScarica lo zip

Scorri dentro l’anteprima

L’indice di un articolo come nei migliori siti di documentazione: accanto alle voci corre un filo che segue la struttura dei titoli e rientra con una curva morbida per ogni sottotitolo, e il suo tratto acceso copre esattamente la parte di pagina sullo schermo, con le due estremità su molle. Una voce premuta fa scivolare la pagina fin lì; sul telefono l’indice diventa una barra con la sezione in corso.

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 table of contents rail</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/schibsted-grotesk@5.3.0/index.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <div class="layout" data-layout>
    <!-- the table of contents: on a wide screen a column on the right that stays put, on a phone a bar at the top
         that opens it. The rail beside the entries is an SVG path drawn from where the entries are; its lit stretch
         covers the sections on screen -->
    <aside class="toc" data-toc>
      <button class="toc__bar" type="button" aria-expanded="false" aria-controls="toc-panel" data-bar>
        <svg class="toc__ring" viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="9"/><circle cx="12" cy="12" r="9" pathLength="1" data-ring/></svg>
        <span class="toc__bar-words"><span class="toc__bar-label">On this page</span><span class="toc__bar-now" data-now>Overview</span></span>
        <svg class="toc__chevron" viewBox="0 0 16 16" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg>
      </button>
      <div class="toc__panel" id="toc-panel" data-panel>
        <p class="toc__title" id="toc-title">On this page</p>
        <nav class="toc__nav" aria-labelledby="toc-title">
          <svg class="rail" aria-hidden="true" data-rail>
            <path class="rail__track" data-track/>
            <path class="rail__read" data-read/>
            <path class="rail__ghost" data-ghost/>
            <path class="rail__halo" data-halo/>
            <path class="rail__lit" data-lit/>
            <circle class="rail__head" r="3.5" data-head/>
          </svg>
          <ol class="toc__list" data-list>
            <li><a href="#overview" data-depth="2">Overview</a></li>
            <li><a href="#how-signing-works" data-depth="2">How signing works</a></li>
            <li><a href="#the-signature-header" data-depth="3">The signature header</a></li>
            <li><a href="#rotating-the-secret" data-depth="3">Rotating the secret</a></li>
            <li><a href="#two-secrets-at-once" data-depth="4">Two secrets at once</a></li>
            <li><a href="#verify-a-request" data-depth="2">Verify a request</a></li>
            <li><a href="#with-the-sdk" data-depth="3">With the SDK</a></li>
            <li><a href="#by-hand" data-depth="3">By hand</a></li>
            <li><a href="#retries-and-replays" data-depth="2">Retries and replays</a></li>
            <li><a href="#the-retry-schedule" data-depth="3">The retry schedule</a></li>
            <li><a href="#rejecting-old-events" data-depth="3">Rejecting old events</a></li>
            <li><a href="#test-locally" data-depth="2">Test locally</a></li>
            <li><a href="#going-live" data-depth="2">Going live</a></li>
          </ol>
        </nav>
        <div class="toc__foot">
          <span class="toc__left" data-left>3 min read</span>
          <button class="toc__up" type="button" data-up>
            <svg viewBox="0 0 16 16" aria-hidden="true"><path d="M8 13V3M4 7l4-4 4 4"/></svg>
            Back to top
          </button>
        </div>
      </div>
    </aside>

    <!-- the article it indexes: plain headings and short paragraphs, only there to be read and scrolled -->
    <main class="doc" data-doc>
      <h2 id="overview" tabindex="-1">Overview</h2>
      <p>A webhook endpoint is a public URL. Anyone who finds it can send it a request that looks exactly like a real event, and if your code acts on it, a stranger can mark orders as paid or cancel bookings.</p>
      <p>A signature on every event closes that door: your code repeats the sender’s computation and acts only when the two results match.</p>

      <h2 id="how-signing-works" tabindex="-1">How signing works</h2>
      <p>Each endpoint has its own signing secret, shown once when the endpoint is created. The secret never travels with the events: only what it produces does.</p>
      <p>A signature covers the moment the event was sent and its body, so a request captured on the way cannot be replayed hours later.</p>

      <h3 id="the-signature-header" tabindex="-1">The signature header</h3>
      <p>Every event carries a header with a timestamp and one or more signatures, separated by commas. Each signature is an HMAC-SHA256 of the timestamp, a full stop and the raw body.</p>

      <h3 id="rotating-the-secret" tabindex="-1">Rotating the secret</h3>
      <p>Rotate a secret when someone who knew it leaves the team, when it may have been logged by accident, or simply once a year, and choose how long the old one keeps working.</p>

      <h4 id="two-secrets-at-once" tabindex="-1">Two secrets at once</h4>
      <p>While both secrets are valid, every event is signed with each of them. Code that accepts any of the signatures keeps working through the change, without a single rejected event.</p>

      <h2 id="verify-a-request" tabindex="-1">Verify a request</h2>
      <p>Verification comes before anything else in your handler: before parsing, before logging the body, before touching the database. Anything that fails gets an error and no further work.</p>

      <h3 id="with-the-sdk" tabindex="-1">With the SDK</h3>
      <p>The official libraries do the whole check in one call, including the tolerance on the timestamp and the secrets in rotation, and throw an error when it fails.</p>

      <h3 id="by-hand" tabindex="-1">By hand</h3>
      <p>Without a library the check is short enough to write yourself. Compare the signatures in constant time, and check every one in the header, not just the first.</p>

      <h2 id="retries-and-replays" tabindex="-1">Retries and replays</h2>
      <p>An event counts as delivered when your endpoint answers with a success status within ten seconds. Anything else, a timeout included, is a failure, and the event is sent again later.</p>

      <h3 id="the-retry-schedule" tabindex="-1">The retry schedule</h3>
      <p>Failed events are tried again seven times over about a day and a half, waiting longer each time. Each new attempt is signed again with a fresh timestamp.</p>

      <h3 id="rejecting-old-events" tabindex="-1">Rejecting old events</h3>
      <p>Reject events whose timestamp is more than five minutes away from your server’s clock: a request recorded on the way is then useless almost as soon as it is captured.</p>

      <h2 id="test-locally" tabindex="-1">Test locally</h2>
      <p>The command line tool forwards real, signed events to a URL on your own machine, with a temporary secret for the session, so a failed check shows up at once.</p>

      <h2 id="going-live" tabindex="-1">Going live</h2>
      <p>Before switching to live mode, make sure the raw body is verified first, the live secret sits in your secret manager, old events are rejected and slow work goes to a queue.</p>
      <p>From then on your endpoint acts only on events that are genuine.</p>
    </main>
  </div>

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

61 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 «L’indice della pagina» da Animorio e adattala al mio sito

Cosa fa

Su un fondo bianco, a destra, l’indice di un articolo, On this page, che resta fermo mentre si scorre; a sinistra l’articolo che indicizza, solo titoli e paragrafi brevi su come verificare le firme dei webhook. Accanto alle tredici voci dell’indice corre un filo grigio sottile che segue i livelli dei titoli: rientra con una piccola curva a esse per ogni sottotitolo e torna fuori dopo. Il tratto del filo accanto alle sezioni sullo schermo è verde, con un alone tenue e un cerchietto vuoto in fondo, e copre esattamente quanto se ne vede: metà di una sezione sullo schermo, metà della sua voce accesa. Il filo già letto, sopra, è di un verde pallido. Le voci delle sezioni sullo schermo diventano scure, quella che si sta leggendo verde. Scorrendo, il tratto acceso scivola giù seguendo le curve; premendo una voce lontana la pagina scivola fin lì, l’estremità davanti del tratto corre e quella dietro la segue più morbida, così il tratto si allunga e poi si raccoglie, e il titolo d’arrivo si accende di verde per un attimo. Puntando una voce, il pezzo di filo che accenderebbe si colora appena. Sotto l’indice i minuti di lettura che restano e Back to top. Sul telefono l’indice è una barra in cima allo schermo con un anello che si riempie man mano che si legge e il nome della sezione in corso, che rotola a ogni cambio; premuta, si apre l’elenco col suo filo.

Come funziona

Il filo dalle voci

Ogni voce ha un livello (data-depth) e da quello una x: INDENT pixel in più per ogni livello. Le posizioni vere delle voci (offsetTop e offsetHeight) danno, per ogni voce, dove comincia e dove finisce. Il filo è una funzione dell’altezza: a ogni y c’è una sola x, quella della voce, e fra due voci di livello diverso la x passa dall’una all’altra con una curva smoothstep alta BEND pixel, centrata sulla riga che le divide. Il tracciato SVG è fatto di punti presi da questa funzione: le estremità, e nove punti lungo ogni curva.

Il tratto acceso

Ogni sezione dell’articolo va dal suo titolo al titolo dopo, e l’ultima fino al suo ultimo paragrafo. La finestra (sotto la barra, sul telefono) si traduce in due altezze sul filo: della prima sezione visibile si prende la frazione già passata sopra il bordo alto, dell’ultima quella arrivata al bordo basso, e le si posa sulle loro voci. Il tratto acceso è un secondo tracciato, preso dalla stessa funzione fra quelle due altezze: segue le curve da solo.

Le due estremità su molle

Le due estremità del tratto non saltano: ognuna è un numero con una velocità, tirato verso la sua meta da una molla (SPRING) e frenato. L’estremità verso cui si va usa la molla piena, l’altra una molla più morbida (TRAIL) con il freno in proporzione, così non oscilla: in un salto lungo il tratto si allunga e poi si raccoglie. Il passo è limitato a 1/30 di secondo e il tracciato si riscrive solo se una delle due altezze è cambiata.

Il salto a una sezione

Una voce premuta porta la pagina al suo titolo (meno lo scroll-margin-top, che sul telefono lascia il posto alla barra) con un tween di GSAP su un numero che a ogni aggiornamento chiama scrollTo: la durata cresce con la distanza fino a GLIDE secondi. La rotella, un dito o i tasti di scorrimento fermano il viaggio. All’arrivo il titolo prende il focus (ha tabindex=-1), l’indirizzo prende il suo #, e il colore verde sfuma via.

Sul telefono

Sotto i 900 pixel l’indice diventa una barra appiccicata in cima allo schermo: un anello SVG riempito con stroke-dashoffset dalla frazione letta, e il nome della sezione in corso che rotola su o giù. Premuta, la lista si apre con un clip-path che scende e le voci che arrivano una dopo l’altra; il filo si rimisura perché ora le voci sono più grandi. Una voce scelta chiude la lista e fa scivolare la pagina.

Valori da cambiare

NomeFileDi partenzaCosa cambia
INDENTscript.js12Di quanti pixel rientra il filo per ogni livello di titolo.
BENDscript.js14L’altezza di ogni curva del filo, in pixel.
SPRINGscript.js[210, 24]Le due estremità del tratto acceso: la forza della loro molla e il freno.
TRAILscript.js0.35Quanto è più morbida la molla dell’estremità che segue: più basso, il tratto si allunga di più nei salti.
GLIDEscript.js1.1Il tempo più lungo che la pagina impiega a scivolare fino a una sezione, in secondi.
READscript.js230Le parole lette in un minuto, per i minuti che restano.
HALOscript.jstrueUn alone tenue attorno al tratto acceso.

Provali nel Playground

Quando usarla

Per documentazione, guide, articoli lunghi, termini di servizio: ogni pagina con titoli e sottotitoli in cui chi legge vuole sapere dove si trova e saltare a un punto. Con meno di quattro titoli non serve.

Sul telefono

Sotto i 900 pixel l’indice diventa una barra appiccicata in cima allo schermo, con un anello di quanto si è letto e il nome della sezione in corso; il tocco apre l’elenco col suo filo, una voce toccata chiude e porta lì, un tocco fuori chiude. L’articolo va su una colonna larga quanto lo schermo, e niente sfora di lato.

Con il movimento ridotto

Il tratto acceso segue lo scorrimento senza molle, una voce porta subito alla sezione senza scivolare, il nome nella barra cambia senza rotolare e l’elenco del telefono si apre in dissolvenza.

Trappole

  • Fra due voci di livello diverso il filo ha una curva: il tratto acceso deve usare la stessa funzione del filo, non un rettangolo o un tratteggio sopra, o ai rientri si stacca dal filo.
  • Le ultime sezioni sono corte e non arrivano mai a un terzo della finestra: in fondo alla pagina la linea che decide la sezione in lettura scivola fino al bordo basso, o Going live non diventerebbe mai quella corrente.
  • Con la stessa molla per tutte e due le estremità il tratto viaggiava rigido; con una molla più morbida dietro si allunga, ma il freno va ridotto con la radice del rapporto fra le molle, o l’estremità dietro arriva lenta come in una melassa.
  • Il salto con scrollTo a ogni aggiornamento va fermato appena chi legge tocca la rotella o lo schermo: altrimenti la pagina gli strappa lo scorrimento di mano.
  • Uno script classico non può chiamare una costante top: è già window.top, e la dichiarazione ferma tutto lo script.

Librerie

GSAP 3.15.0, Schibsted Grotesk (font variabile) 5.3.0.

Compressi con gzip, i tre file della demo pesano 10 KB; le librerie: GSAP 28 KB.

Collaudo

Provata il 4 ottobre 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.

Tutti i componenti