
Il caricamento che diventa marchio
Transizioni, SVG, Testo
NuovaGratis
Quattro colonne di larghezze diverse cadono a scalinata, la pagina cambia sotto e poi si scopre. Un gesto solo per caricamento e passaggi.
Una cartella che funziona da sola: index.html, style.css, script.js e le librerie da CDN.
<!doctype html>
<html lang="it">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Otturatore a colonne</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/mona-sans@5.3.0/standard.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<header class="testata">
<span class="marchio">Studio</span>
<nav class="voci" aria-label="Pagine">
<button type="button" class="voce" data-vai="lavori" aria-current="page">Lavori</button>
<button type="button" class="voce" data-vai="studio" data-autoclic>Chi siamo</button>
</nav>
</header>
<main>
<section class="pagina pagina--lavori" data-pagina="lavori">
<p class="sopra">Pagina uno</p>
<h1 class="titolo">Lavori scelti</h1>
<p class="testo">Clicca «Chi siamo»: l'otturatore cade a colonne, la pagina cambia sotto, poi le colonne risalgono a scalinata.</p>
</section>
<section class="pagina pagina--studio" data-pagina="studio" hidden>
<p class="sopra">Pagina due</p>
<h1 class="titolo">Chi siamo</h1>
<p class="testo">La stessa mossa serve per il caricamento iniziale e per ogni passaggio fra pagine: chi guarda la riconosce e il sito sembra uno solo.</p>
</section>
</main>
<!-- l'otturatore: quattro colonne di larghezze diverse -->
<div class="otturatore" aria-hidden="true">
<span style="--f: 22"></span><span style="--f: 18"></span><span style="--f: 38"></span><span style="--f: 22"></span>
</div>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/gsap.min.js"></script>
<script src="script.js"></script>
</body>
</html>/* Otturatore a colonne */
:root {
--colonna: #0b3b39; /* colore delle colonne */
--pagina-uno: #eef3ef;
--pagina-due: #ff8a3d;
--testo: #07201f;
}
* { box-sizing: border-box; }
html,
body {
margin: 0;
height: 100%;
}
body {
font-family: 'Mona Sans Variable', system-ui, sans-serif;
color: var(--testo);
background: var(--pagina-uno);
overflow: hidden;
}
.testata {
position: fixed;
inset: 0 0 auto 0;
z-index: 5;
display: flex;
justify-content: space-between;
align-items: center;
padding: 22px 5vw;
}
.marchio {
font-weight: 760;
font-stretch: 116%;
font-size: 20px;
letter-spacing: -0.04em;
}
.voci {
display: flex;
gap: 8px;
}
.voce {
font: inherit;
font-weight: 600;
font-stretch: 106%;
font-size: 15px;
color: inherit;
background: none;
border: 0;
padding: 10px 16px;
border-radius: 999px;
cursor: pointer;
box-shadow: inset 0 0 0 1.5px rgba(7, 32, 31, 0.25);
transition: background-color 0.3s, color 0.3s, transform 0.16s;
}
.voce:active {
transform: scale(0.96);
}
.voce[aria-current='page'] {
background: var(--testo);
color: #f6f7f3;
box-shadow: none;
}
.pagina {
position: fixed;
inset: 0;
display: grid;
align-content: end;
gap: 18px;
padding: 0 5vw 9vh;
}
.pagina[hidden] {
display: none;
}
.pagina--studio {
background: var(--pagina-due);
}
.sopra {
margin: 0;
font-size: 15px;
font-weight: 600;
opacity: 0.65;
}
.titolo {
margin: 0;
font-size: clamp(56px, 12vw, 190px);
font-weight: 700;
font-stretch: 114%;
letter-spacing: -0.05em;
line-height: 0.88;
}
.titolo .riga {
display: inline-block;
overflow: hidden;
vertical-align: top;
padding-bottom: 0.06em;
}
.titolo .riga span {
display: block;
}
.testo {
margin: 0;
max-width: 46ch;
font-size: clamp(16px, 1.3vw, 20px);
line-height: 1.45;
}
.otturatore {
position: fixed;
inset: 0;
z-index: 10;
display: flex;
pointer-events: none;
}
.otturatore span {
flex: var(--f, 1);
background: var(--colonna);
transform: scaleY(1); /* chiuso all'inizio: la pagina si apre al caricamento */
box-shadow: inset 1px 0 0 rgba(246, 247, 243, 0.08);
}/* Otturatore a colonne
Un solo gesto per il caricamento e per i passaggi fra pagine: quattro colonne di larghezze
diverse cadono dall'alto in un ordine sparso (la più larga per prima), la pagina cambia sotto,
poi le colonne continuano la corsa verso il basso e scoprono la pagina nuova a scalinata.
In un sito vero: chiudi prima di cambiare pagina (location.href) e apri al caricamento. */
/* ---- valori da cambiare ---- */
const ORDINE = [2, 0, 3, 1]; /* in che ordine si muovono le colonne (indici da sinistra) */
const DURATA = 0.85; /* secondi di ogni colonna */
const SFASATURA = 0.06; /* secondi fra una colonna e la successiva */
const CURVA = 'expo.inOut';
const otturatore = document.querySelector('.otturatore');
const colonne = ORDINE.map((i) => otturatore.querySelectorAll('span')[i]);
const RIDOTTO = matchMedia('(prefers-reduced-motion: reduce)').matches;
let occupato = false;
/* a otturatore aperto il contenitore è nascosto: Safari su iPhone altrimenti tinge le sue barre
con il colore delle colonne, anche se sono ridotte a zero */
function chiudi() {
otturatore.style.visibility = 'visible';
return gsap.fromTo(colonne, { scaleY: 0, transformOrigin: '50% 0%' }, { scaleY: 1, duration: DURATA, ease: CURVA, stagger: SFASATURA });
}
function apri() {
return gsap.to(colonne, {
scaleY: 0, transformOrigin: '50% 100%', duration: DURATA * 1.1, ease: CURVA, stagger: SFASATURA,
onComplete: () => { otturatore.style.visibility = 'hidden'; },
});
}
/* le righe del titolo salgono dalla loro maschera mentre l'otturatore si apre */
function entrata(pagina) {
const titolo = pagina.querySelector('.titolo');
if (!titolo.dataset.pronto) {
titolo.innerHTML = titolo.textContent.split(' ').map((p) => `<span class="riga"><span>${p}</span></span>`).join(' ');
titolo.dataset.pronto = '1';
}
const tl = gsap.timeline();
tl.fromTo(titolo.querySelectorAll('.riga span'), { yPercent: 110 }, { yPercent: 0, duration: 1.2, ease: 'expo.out', stagger: 0.08 }, 0.25)
.fromTo(pagina.querySelectorAll('.sopra, .testo'), { y: 20, opacity: 0 }, { y: 0, opacity: 1, duration: 0.9, ease: 'expo.out', stagger: 0.08 }, 0.45);
return tl;
}
async function vai(nome) {
const dove = document.querySelector(`[data-pagina="${nome}"]`);
if (occupato || !dove || !dove.hidden) return;
occupato = true;
const cambia = () => {
document.querySelectorAll('.pagina').forEach((p) => { p.hidden = p !== dove; });
document.querySelectorAll('[data-vai]').forEach((b) => {
const qui = b.dataset.vai === nome;
b.toggleAttribute('aria-current', qui);
if (qui) b.setAttribute('aria-current', 'page');
/* l'anteprima automatica clicca sempre l'altra voce */
b.toggleAttribute('data-autoclic', !qui);
});
};
if (RIDOTTO) {
cambia();
occupato = false;
return;
}
await chiudi();
cambia();
entrata(dove);
await apri();
occupato = false;
}
document.querySelectorAll('[data-vai]').forEach((b) => b.addEventListener('click', () => vai(b.dataset.vai)));
/* al caricamento l'otturatore è chiuso (style.css) e si apre */
if (RIDOTTO) { gsap.set(colonne, { scaleY: 0 }); otturatore.style.visibility = 'hidden'; }
else {
gsap.delayedCall(0.3, () => { apri(); entrata(document.querySelector('.pagina:not([hidden])')); });
}Un contenitore fisso a tutto schermo in flex, con quattro span e flex-grow 22, 18, 38, 22. Le larghezze diverse sono metà dell'effetto: con colonne uguali sembra una tenda.
L'array ORDINE decide chi parte per prima: [2, 0, 3, 1] fa partire la colonna larga, poi la prima, poi l'ultima. Con uno stagger piccolo il bordo diventa una scalinata.
Per chiudere, scaleY da 0 a 1 con l'origine in alto. Per aprire, scaleY da 1 a 0 con l'origine in basso: la colonna sembra proseguire la corsa invece di tornare indietro.
La funzione vai() aspetta la chiusura (le timeline di GSAP si possono attendere con await), scambia il contenuto, fa partire l'entrata del titolo e poi apre.
| Nome | File | Di partenza | Cosa cambia |
|---|---|---|---|
ORDINE | script.js | [2, 0, 3, 1] | L'ordine delle colonne. Prova [0, 1, 2, 3] per una tenda che scorre, [1, 3, 0, 2] per un ritmo più nervoso. |
DURATA / SFASATURA | script.js | 0.85 / 0.06 | Durata di ogni colonna e ritardo fra l'una e l'altra. |
--f | index.html | 22, 18, 38, 22 | La larghezza relativa di ogni colonna. |
--colonna | style.css | #0b3b39 | Il colore dell'otturatore. |
Per i passaggi fra pagine e per il caricamento, quando vuoi che tutto il sito abbia una firma riconoscibile. Non per aprire menù o finestre: lì è troppo lento.
Identica. Le colonne sono in proporzione, quindi funzionano anche in verticale.
Nessun otturatore: la pagina cambia subito.
GSAP 3.15.0, Mona Sans (font variabile) 5.3.0.
Peso: 7,0 KB dei nostri file.
Provata il 26 settembre 2026
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.
Con l’email e la password del tuo account.
Hai fatto l’accesso come .
Il tuo piano è gratis. Quando arriverà, l’accesso a vita si attiverà su questo stesso account.