Capitolo 05

Lo scroll che racconta

Lo scroll è il gesto più trasparente che abbiamo. Non chiede permesso, non interrompe, non si insegna: già lo sappiamo fare. Una pagina che lo asseconda — invece di rincorrerlo — è una pagina che si lascia leggere fino in fondo.

9 min di lettura

C’è un gesto, nel modo in cui usiamo il web, che non abbiamo mai dovuto imparare: scorrere. Non c’è nessun tutorial per lo scroll, nessuna voce che dice “puoi continuare a leggere se vai giù”. Eppure è il gesto più comune che facciamo in una giornata davanti a uno schermo — il pollice che sale, la rotella che gira, la barra spaziatrice premuta a metà di un paragrafo. Una pagina che vale è una pagina che riconosce questo gesto come l’azione principale dell’utente, e che decide cosa farne.

Lo scroll è una variabile narrativa: può essere il tempo di una storia, l’asse di un argomento, la cadenza di una rivelazione. Ma può anche essere il rumore di un’interfaccia che insegue il pollice — rivela, nasconde, anima, sussulta. La differenza fra le due cose non è quanto si “muove” la pagina, è cosa si muove e quando. Le pagine che raccontano qualcosa con lo scroll lo fanno con una mano leggera: la mappa del progresso, un dettaglio che entra, un titolo che resta. Mai un’orchestra.

In questo capitolo apriamo sei modi diversi di “scorrere bene”. Dal più discreto — una barra che dice solo dove sei — al più strutturato — un’illustrazione che cambia man mano che la storia avanza. Sei modi, una stessa filosofia: lo scroll è il gesto dell’utente, e l’interfaccia che lo rispetta è quella che si fa scorrere senza scorrerlo dietro.

La barra che sa dove sei

In ogni lettura un po’ lunga c’è un momento in cui l’occhio cerca un appiglio: quanto manca? La risposta migliore non è una percentuale gigante, e neanche un “scrolla fino alla fine per scoprire”. È una barretta sottile, in alto, che si riempie con lo scroll — niente di più. Una mappa di posizione, non un’animazione.

05.a

Una piccola finestra

Le interfacce che chiedono concentrazione devono restituire fiducia. Una barra di lettura sottile, ferma in alto, è un patto silenzioso: ti dico dove sei e quanto manca, senza distrarti.

Il movimento di questa barra è guidato dallo scroll della finestra qui sotto, non dal tempo: se ti fermi, lei si ferma; se torni indietro, lei torna indietro. È un indicatore di posizione, non un'animazione.

Il valore percentuale a destra usa cifre tabulari — così quando passa da 9 a 10 non sussulta. La precisione tipografica non è vanità: è la condizione per cui un numero "vivo" non diventa fastidio.

Cosa la barra non fa

Non lampeggia. Non si gonfia all'arrivo. Non celebra il 100% con un fuoco d'artificio. Arrivare in fondo a una lettura è un gesto privato: la barra si conclude e basta.

Su prefers-reduced-motion: reduce non cambia nulla — non c'è motion qui, c'è solo mappatura. La differenza è importante: ridurre il movimento non significa togliere informazione.

Il fallback gentile

Nei browser che non supportano animation-timeline: scroll() la barra resta a zero. Un piccolo script di sicurezza la spinge col scroll event passivo, così il valore arriva comunque — ma la versione nativa, quando c'è, è di gran lunga preferita: gira sul compositor, mai sul main thread.

Continua a scorrere. Quasi finito.

L'ultimo paragrafo è il momento più delicato di una lettura lunga: chi è arrivato qui ha deciso di starci. Tutto quello che si guadagna in fondo, si guadagna con la pazienza dei primi due terzi.

Barra di lettura · scroll-driven, niente teatro

La barra è guidata dallo scroll dello scroller via animation-timeline: scroll(self) — il CSS legge la posizione direttamente dal compositor, senza passare dal main thread. Non c’è scroll event, non c’è requestAnimationFrame: zero JS per il rendering. Sui browser senza supporto, un fallback con listener passivo dà lo stesso risultato — più tradizionale, più costoso, ma onesto.

Il valore percentuale a destra usa font-variant-numeric: tabular-nums: cifre a larghezza fissa, così quando passa da 9 a 10 non sussulta. È un dettaglio invisibile finché non lo togli — è esattamente il tipo di cura che fa la differenza fra “barra che funziona” e “barra che si lascia guardare”. Reduced motion non cambia niente, perché non c’è motion: c’è solo mappatura. Una distinzione importante che torna anche nelle altre demo.

Gli elementi che si svelano una volta sola

Il pattern del “fade-up on scroll” è uno dei più abusati del web. Quando funziona, sembra che la pagina si svegli mentre scendi; quando viene applicato senza criterio, sembra che insegua il pollice. La regola è semplice: una sola volta. Se rifai scroll sopra e sotto, gli elementi restano dove sono. Le pagine che ri-animano a ogni passaggio sembrano nervose; quelle che animano una volta sembrano abitate.

05.b

Sei carte sotto. Ognuna entra una volta sola, alla sua prima apparizione in viewport. Se torni indietro, restano ferme.

  • 01

    Il primo gesto

    Una scheda nuova entra dal basso, salendo di pochi pixel. L'occhio nota il movimento, non lo subisce.

  • 02

    Il ritardo onesto

    Ogni scheda ritarda la sua entrata di 40 ms rispetto alla precedente. Stagger, ma corto: niente coda di pavone.

  • 03

    Una sola volta

    Se scrollo sopra e sotto, queste schede non rifanno la scena. La memoria visiva si costruisce, non si interrompe.

  • 04

    La soglia

    Il trigger scatta a 18% dell'altezza visibile. Più basso sarebbe nervoso, più alto farebbe "saltare" gli ultimi.

  • 05

    Reduced motion

    Chi ha chiesto meno movimento riceve subito tutto al posto giusto. L'informazione non si conquista: appare.

  • 06

    Lo spazio è già lì

    L'opacità parte da 0, ma lo spazio della scheda è già riservato. Nessun salto del layout, mai.

Reveal al primo ingresso · niente rincorsa

Un IntersectionObserver con soglia al 18% e unobserve dopo il primo trigger. Ogni card riceve un data-revealed=“true” permanente, le transizioni CSS partono da lì. Lo stagger fra una card e l’altra è di 40 ms — corto, niente coda di pavone. Più lungo sarebbe celebrazione, più corto un’unica cosa che entra.

Lo spazio della card è già riservato in layout: l’opacità parte da 0 ma la geometria no. È la differenza fra “reveal” e “layout shift”. Su prefers-reduced-motion: reduce tutte le card appaiono subito al loro posto e l’observer non viene neanche allestito — chi ha chiesto meno movimento non riceve “meno movimento”, riceve l’informazione. È un punto importante: ridurre il motion non significa rallentare, significa dare lo stesso stato finale senza il tragitto.

Il titolo che resta, e cambia in silenzio

Quando un articolo è diviso in sezioni, ognuna con il suo titolo, c’è un momento sottile: il lettore arriva a metà di una sezione e dimentica dove si trova. Le pagine migliori lo aiutano senza chiederglielo — un piccolo sticky in alto, con il titolo della sezione corrente, che cambia con un cross-fade morbido quando ne entra una nuova. Non un menu, non una breadcrumb: solo un’etichetta tipografica.

05.c

Il primo respiro

Una sezione esiste perché l'occhio sa dove sta. Quando il titolo lavora per chi legge, la lettura cambia ritmo da sola — non serve dire "torna su per ricordartelo".

Lo sticky della testata fa un solo lavoro: dirti, nella periferia della vista, dove sei. Non fa ombra, non si gonfia, non chiede attenzione. È un dettaglio tipografico, non un componente.

Il cross-fade del titolo dura 260 ms. Basta. Più lungo si trasformerebbe in un'animazione celebrativa; più corto sembrerebbe un salto. La distanza fra "movimento" e "rumore" è di poche decine di millisecondi.

Continuiamo. La prossima sezione arriva da sola.

La pausa

Una sezione esiste perché l'occhio sa dove sta. Quando il titolo lavora per chi legge, la lettura cambia ritmo da sola — non serve dire "torna su per ricordartelo".

Lo sticky della testata fa un solo lavoro: dirti, nella periferia della vista, dove sei. Non fa ombra, non si gonfia, non chiede attenzione. È un dettaglio tipografico, non un componente.

Il cross-fade del titolo dura 260 ms. Basta. Più lungo si trasformerebbe in un'animazione celebrativa; più corto sembrerebbe un salto. La distanza fra "movimento" e "rumore" è di poche decine di millisecondi.

Continuiamo. La prossima sezione arriva da sola.

La ripresa

Una sezione esiste perché l'occhio sa dove sta. Quando il titolo lavora per chi legge, la lettura cambia ritmo da sola — non serve dire "torna su per ricordartelo".

Lo sticky della testata fa un solo lavoro: dirti, nella periferia della vista, dove sei. Non fa ombra, non si gonfia, non chiede attenzione. È un dettaglio tipografico, non un componente.

Il cross-fade del titolo dura 260 ms. Basta. Più lungo si trasformerebbe in un'animazione celebrativa; più corto sembrerebbe un salto. La distanza fra "movimento" e "rumore" è di poche decine di millisecondi.

Continuiamo. La prossima sezione arriva da sola.

Il commiato

Una sezione esiste perché l'occhio sa dove sta. Quando il titolo lavora per chi legge, la lettura cambia ritmo da sola — non serve dire "torna su per ricordartelo".

Lo sticky della testata fa un solo lavoro: dirti, nella periferia della vista, dove sei. Non fa ombra, non si gonfia, non chiede attenzione. È un dettaglio tipografico, non un componente.

Il cross-fade del titolo dura 260 ms. Basta. Più lungo si trasformerebbe in un'animazione celebrativa; più corto sembrerebbe un salto. La distanza fra "movimento" e "rumore" è di poche decine di millisecondi.

Continuiamo. La prossima sezione arriva da sola.

Sticky heading · cross-fade fra sezioni

Un solo elemento sticky, non uno per sezione. Quando una nuova sezione attraversa la “linea di lettura” (rootMargin -15% 0px -55% 0px sull’IntersectionObserver), il testo dentro lo slot fa cross-fade — il vecchio sale e svanisce, il nuovo sale ed entra. Durata 260 ms. Più lungo si trasformerebbe in animazione celebrativa; più corto sembrerebbe un salto.

Il pattern evita un classico inciampo: il “doppio sticky” che si scavalca. Avere un titolo per sezione che diventa sticky a turno crea momenti in cui due titoli sono visibili contemporaneamente, uno che esce e uno che entra. Tenere un solo slot tipografico, e farne ruotare il contenuto, è più semplice e più onesto. Funziona anche meglio con i lettori vocali: l’elemento è marcato aria-hidden=“true” perché è un duplicato visivo dei titoli reali sotto.

La profondità che non dà nausea

La parallasse è un trucco antico — lo usavano già i cartoni Disney, anni prima dei browser. Sul web ha vissuto un decennio di abusi: layer che si rincorrono, sfondi che galoppano, scene che si “rivelano” come al circo. La versione onesta è quasi invisibile: i layer si muovono di pochi pixel, le proporzioni sono coerenti (la luna più lenta delle creste, le creste più lente dei sassolini), e su prefers-reduced-motion tutto si spegne. Atmosfera, non spettacolo.

05.d
Capitolo zero

Il fondale si muove appena

Tre layer dietro a queste parole. Scorri e nota: la luna in cima si sposta di pochi pixel, le creste a metà altezza un po' di più, i sassolini in basso quasi quanto il testo.

La parallasse onesta non si vede a colpo d'occhio. Si vede quando la togli: la pagina sembra piatta, mentre prima sembrava semplicemente "buona". Quello scarto sottile è il regalo del trucco.

La regola dei millimetri vale anche qui: meno di 20 px di translazione su tutto il viaggio dello scroll. Oltre, la stomaco se ne accorge prima dell'occhio.

Le tre quote di profondità sono coerenti: più un layer è "lontano", più lentamente si muove. È la fisica: la luna è più stabile delle nuvole, le nuvole sono più stabili degli alberi.

Hai chiesto meno motion? Tutta la parallasse è spenta — niente trucchetti morbidi, niente versione "soft". Il rispetto è binario.

Parallasse onesto · meno di 20px su tutto lo scroll

Tre layer sticky in cima allo scroller, animati con animation-timeline: scroll() sul singolo asse Y. Ampiezze: −18px sul layer “lontano” (luna e stelle), −8px sul medio (creste), −3px sul vicino (sassolini). Niente oltre. Una parallasse che si vede a colpo d’occhio è una parallasse di troppo: l’occhio l’apprezza, lo stomaco no.

La scelta di rispettare la fisica — più lontano è un layer, più lentamente si muove — non è feticismo: è la chiave per cui la profondità sembra reale invece di “effetto”. Su prefers-reduced-motion: reduce non offriamo una versione “soft” con meno ampiezza: spegniamo tutto. Il rispetto del setting è binario; chi ha chiesto meno motion non vuole vedere il trucco in versione attenuata, vuole non vederlo per niente.

Due fili sincronizzati, senza scrub

Lo scrollytelling — testo a sinistra che scorre, immagine a destra che cambia — è una delle forme narrative più belle del web, ed è anche una delle più fragili. La versione “millimetrica” (ogni pixel di scroll sposta un pixel dell’illustrazione) sembra magica per dieci secondi e poi diventa nervosa: a bassa banda, con tastiera, con uno screen reader, smette di significare. La versione a passi è più onesta: tre stati discreti, tre soglie chiare, una transizione corta fra l’uno e l’altro.

05.e

Un seme, un albero, un bosco

Tre passi, tre stati. Scorri per cambiarli.

01

Il seme

Tutto comincia in piccolo. Una proprietà CSS, un evento, una transizione di 200 ms. Nessuna di queste cose, da sola, fa una buona interfaccia.

Ma è da queste briciole che la qualità si compone: la cura del singolo dettaglio, ripetuta cento volte, è quello che il lettore "sente" senza poterlo nominare.

02

L'albero

I componenti si combinano in pattern. Una conferma diventa un'azione, un'azione una rotta, una rotta una sezione. La pagina prende forma — diventa abitabile.

A questo livello la decisione più importante non è "cosa fa", è "come si comporta quando l'utente sbaglia". L'errore gentile è la firma del progettista.

03

Il bosco

Quando i pattern si stratificano, nasce una voce. Non è scelta a tavolino: emerge dalle ripetizioni piccole. Le interfacce che hanno una voce sono quelle che chi le usa riconosce a colpo d'occhio.

Il bosco non è la somma degli alberi. È quello che resta quando i singoli alberi spariscono — il ritmo, il silenzio, l'aria fra una cosa e l'altra.

(Fine. Torna su per ricominciare.)

Scrollytelling a passi · seme, albero, bosco

Il testo scorre normalmente; il pannello laterale è in position: sticky e cambia stato quando una nuova sezione del testo attraversa la zona centrale del viewport (IntersectionObserver con rootMargin -20% 0px -20% 0px). Le tre illustrazioni — seme, albero, bosco — vivono nello stesso SVG sovrapposte; quella attiva diventa opaca con una piccola scala-up, le altre svaniscono. Cross-fade, non morph.

Perché a passi e non scrub continuo? Perché su tastiera, screen reader e zoom 200% lo scrub continuo è essenzialmente invisibile: non c’è “metà strada” semantica fra “seme” e “albero”. A passi, ogni stato è un’unità di significato, raggiungibile e annunciabile. Su mobile (sotto 32rem) il pannello laterale diventa una card in cima — stesso markup, geometria diversa, semantica intatta.

L’indice che segue, mai si mette davanti

Un articolo lungo che si divide in sezioni vuole un indice — non un menu, non una breadcrumb: una piccola colonnina che dice “stai leggendo questa”. La voce attiva si sposta da sola mentre scorri, e cliccarci sopra ti porta lì con un scroll dolce. La regola della discrezione: l’indice esiste finché serve. Su uno schermo stretto sparisce — non si trasforma in un hamburger, non chiede attenzione: cede lo spazio al testo, che è il vero protagonista.

05.f

Premessa

Un indice è onesto solo se sa stare al suo posto. Troppo grande, distrae; troppo piccolo, non serve. La giusta misura è una colonna sottile, sempre visibile, mai prepotente.

La voce attiva qui non è "quella più in alto". È quella che stai davvero leggendo — la sezione con la maggior parte di sé sopra la riga della lettura.

Il metodo

Un IntersectionObserver guarda ogni sezione e segnala quando la sua intersezione col viewport supera una soglia. Il rootMargin alza la linea di "lettura" al 30% dal bordo superiore — così l'evidenziazione cambia quando l'occhio è davvero sulla nuova sezione, non un attimo prima.

Niente listener di scroll, niente getBoundingClientRect ad ogni frame. L'osservatore lavora sul compositor, e il main thread resta libero per quello che conta.

Le voci

Il marker laterale si sposta con una transizione corta — 220 ms — perché l'occhio non perda il filo del cambio. Più lungo sarebbe nostalgico, più corto sarebbe brusco.

Tab funziona come da specifica: ogni voce è un link onesto, con focus visibile. Il marker tiene conto anche del focus da tastiera: se "punto" l'ultima voce con Tab, lui mi segue. È un indice che si lascia usare in due modi.

I limiti

Su viewport stretti l'indice scompare. Niente "hamburger toc": è un dettaglio editoriale, non navigazione critica. Su schermo piccolo lo spazio va al contenuto.

Su un articolo molto lungo, oltre 12 voci, l'indice diventa rumore. La regola: se non sta in una colonna senza scroll, è il segno che l'articolo va diviso in capitoli — non l'indice da ingrandire.

Il congedo

L'indice che hai usato per orientarti svanisce quando l'articolo finisce. Niente sticky che resta a fluttuare nel vuoto. Il dettaglio si toglie quando non serve più.

Questo è il modo migliore per misurare se un componente è onesto: se ci pensi solo quando lo cerchi.

Indice flottante · scroll-spy con marker laterale

Cinque sezioni, cinque voci nell’indice. La voce attiva è scelta da un IntersectionObserver che misura la maggior intersezione visibile, con un rootMargin: ‘-30% 0px -50% 0px’ per alzare la “linea di lettura” — l’evidenziazione cambia quando l’occhio è davvero sulla nuova sezione, non quando il suo bordo è entrato per primo. Il marker laterale (l’accento sul bordo sinistro) scivola con una transizione di 220 ms, abbastanza per non perdere il filo, abbastanza poco per non sembrare un trucchetto.

I link funzionano da tastiera: focus visibile, scroll-behavior: smooth per il navigatore vocale, scroll-margin-block-start sulle sezioni perché il titolo non finisca incollato al bordo. Su prefers-reduced-motion lo smooth scroll diventa istantaneo e la transizione del marker sparisce: chi ha chiesto meno motion riceve la stessa informazione, nello stesso posto, senza il tragitto. È lo stesso principio della prima demo: ridurre il motion non significa nascondere lo stato.

Cosa portiamo via

Lo scroll è il gesto più trasparente che abbiamo. Una pagina che lo asseconda — invece di rincorrerlo — è una pagina che si lascia leggere fino in fondo. Le sei dimostrazioni qui sopra mostrano sei modi di “raccontare con lo scroll” senza prendersi troppo sul serio:

  1. La barra di lettura che mappa la posizione, scroll-driven, mai animata.
  2. Il reveal una sola volta, perché una pagina che ri-anima a ogni passaggio è una pagina nervosa.
  3. Il titolo sticky che cambia in silenzio, un solo slot per evitare i doppi-sticky.
  4. La parallasse onesta, sotto i 20 px, completa di spegnimento totale con reduced-motion.
  5. Lo scrollytelling a passi — tre stati discreti, semantica intatta su tastiera.
  6. L’indice flottante che evidenzia, mai si mette davanti, e cede lo spazio a chi legge.

Sei modi, una sola regola: lo scroll è il gesto dell’utente, e l’interfaccia che lo rispetta è quella che si fa scorrere senza scorrerlo dietro. Il prossimo capitolo cambia registro — numeri che hanno peso — perché dopo aver imparato a far raccontare lo scroll, è tempo di imparare anche a far parlare le quantità: cifre tabulari, count-up onesti, delta con segno, e formati che riconoscono chi sta guardando.