Per apprezzare l’importanza di questa API, è utile ripercorrere l’evoluzione delle architetture web.
Agli albori del web, il modello MPA era l’unico esistente. I browser erano client relativamente semplici, con motori JavaScript poco potenti. Il loro compito principale era renderizzare HTML e CSS inviati dal server.
Di conseguenza, l’intero stato dell’applicazione (chi sei, cosa hai nel carrello, etc.) doveva risiedere e essere gestito quasi esclusivamente sul server. Ad ogni interazione, il browser richiedeva una pagina completamente nuova.
Con l’evoluzione dei browser e la crescente potenza di JavaScript, è emerso il modello SPA. In questa architettura, una porzione significativa dello stato applicativo viene delegata al client. La pagina HTML viene caricata una sola volta e le interazioni successive aggiornano dinamicamente la vista tramite JavaScript, creando un’esperienza utente fluida e reattiva.
Un’architettura SPA, dove la pagina di base è persistente, permette di manipolare liberamente le risorse allocate dal browser per quel tab. Animare un elemento da uno stato A a uno stato B è quindi un’operazione nativa e relativamente semplice.
In una MPA, ogni volta che si naviga verso una nuova pagina, il contesto della pagina precedente viene completamente distrutto. Tutte le variabili, gli elementi del DOM e gli stati in memoria vengono eliminati per far posto al nuovo ambiente.
Questo rende impossibile usare JavaScript per animare un elemento tra le due pagine.
Anche se meccanismi come localStorage
, sessionStorage
e i cookie
permettono di mantenere dati tra le navigazioni, non risolvono il problema dell’animazione. Essi persistono dati, non elementi del DOM durante il brevissimo istante della transizione tra il rendering di una pagina e l’altra. Questo è un limite intrinseco del modello di navigazione dei browser, una sandbox le cui regole non possiamo aggirare.
La View Transitions API nasce per superare esattamente questo limite. Introdotta in Chrome a partire dalla versione 111 (Marzo 2023) e decentemente supportata nella maggior parte dei browsers, permette di orchestrare transizioni animate anche tra documenti diversi in una MPA.
Con poche righe di CSS, è possibile istruire il browser a gestire la transizione di elementi specifici.
Il primo passo è abilitare la funzionalità per le navigazioni cross-documento. Questo si fa aggiungendo una semplice regola nel CSS di entrambe le pagine (quella di partenza e quella di arrivo).
/* style.css */
@view-transition {
navigation: auto;
}
Questa regola indica al browser di intercettare le navigazioni tra pagine della stessa origine e di applicare una transizione di default (una dissolvenza incrociata, un fade-in/fade-out).
Il passaggio chiave è dire al browser quale elemento nella pagina A corrisponde a quale elemento nella pagina B. Questo si ottiene assegnando lo stesso, unico valore alla proprietà CSS view-transition-name
a entrambi gli elementi.
Pagina di Listing (/blog
):
<a href="/blog/mio-post">
<img
src="/path/to/thumbnail.jpg"
style="view-transition-name: hero-image-post-123;"
/>
</a>
Pagina del Post (/blog/mio-post
):
<img
src="/path/to/hero-image.jpg"
style="view-transition-name: hero-image-post-123;"
class="hero-image"
/>
Assegnando lo stesso view-transition-name
(hero-image-post-123
), il browser capisce che questi due elementi sono concettualmente lo stesso e animerà la transizione tra le loro diverse dimensioni e posizioni, creando un effetto fluido e professionale che prima era un’esclusiva delle SPA.
Nota come il nome hero-image-post-123
sia specifico. In un’applicazione reale, questo valore non sarebbe statico ma generato dinamicamente, ad esempio utilizzando l’ID univoco del prodotto o dell’articolo (es. hero-image-post-${post.id}
). Questo assicura che ogni elemento della lista punti correttamente e senza ambiguità alla sua controparte nella pagina di destinazione.
Attenzione: La proprietà
view-transition-name
deve essere unica nel DOM in un dato momento. Se due elementi visibili hanno lo stesso nome, il browser non saprà come gestire la transizione e l’animazione non avverrà.La generazione dinamica basata su ID risolve proprio questo problema. Puoi infatti creare il valore di
view-transition-name
in modo dinamico:<div style="view-transition-name: hero-image-post-${post.id};"> />
Di default, il browser applica una dissolvenza incrociata (cross-fade). Tuttavia, hai il pieno controllo sull’animazione tramite CSS. Utilizzando degli speciali pseudo-elementi, puoi definire animazioni complesse e su misura.
Ad esempio, per modificare l’animazione di default della pagina e farla scorrere, potresti usare:
::view-transition-old(root) {
animation: slide-from-right 0.5s ease-out;
}
::view-transition-new(root) {
animation: slide-to-left 0.5s ease-in;
}
/* E puoi animare in modo specifico l'elemento condiviso! */
::view-transition-new(hero-image-post-123) {
/* L'animazione di transform (scala, posizione)
è gestita dal browser. Qui puoi aggiungere altro,
come una transizione sul `border-radius`. */
transition: border-radius 0.5s;
}
Questo apre infinite possibilità creative che vanno ben oltre l’effetto standard.