
Standing letters with a long shadow
Text, Scroll, 3D
NewFree
As you scroll, each title arrives blurred and comes into focus in the first quarter of its run; then it rises slowly while the subtitle overtakes it faster.
A folder that runs on its own: index.html, style.css, script.js and the libraries from a CDN.
<!doctype html>
<html lang="it">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Titoli che vengono a fuoco</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>
<main>
<section class="apertura">
<p class="sopra">Scorri piano</p>
<p class="grande">I titoli arrivano sfocati e vengono a fuoco mentre scorri; poi salgono, e il sottotitolo li supera.</p>
</section>
<section class="capitolo" data-fuoco>
<h2 class="titolo">Tutto parte<br>da una curva</h2>
<p class="sottotitolo">Prima si decide come parte e come frena. Il resto viene dopo.</p>
</section>
<section class="citazione" data-citazione>
<blockquote>«Un’animazione che arriva dritta sembra spinta. Una che frena sembra viva.»</blockquote>
</section>
<section class="capitolo" data-fuoco>
<h2 class="titolo">Il tempo è<br>un materiale</h2>
<p class="sottotitolo">Si taglia, si allunga, si piega: come il legno, ha una venatura.</p>
</section>
<section class="capitolo" data-fuoco>
<h2 class="titolo">Il resto<br>è ritmo</h2>
<p class="sottotitolo">Fine. Torna su e rifallo.</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>/* Titoli che vengono a fuoco */
:root {
--fondo: #f6f7f3;
--inchiostro: #07201f;
--tenue: #5b706d;
}
* { box-sizing: border-box; }
html,
body {
margin: 0;
}
body {
font-family: 'Mona Sans Variable', system-ui, sans-serif;
color: var(--inchiostro);
background: var(--fondo);
overflow-x: clip;
}
.sopra {
margin: 0 0 12px;
font-size: 14px;
font-weight: 620;
color: var(--tenue);
}
.grande {
margin: 0;
max-width: 26ch;
font-size: clamp(24px, 3vw, 44px);
font-weight: 620;
line-height: 1.06;
letter-spacing: -0.03em;
}
.apertura {
min-height: 80vh;
padding: 12vh clamp(18px, 5vw, 72px);
display: flex;
flex-direction: column;
justify-content: flex-end;
}
/* ogni capitolo è alto uno schermo e mezzo: il titolo ha il tempo di mettere a fuoco e salire */
.capitolo {
min-height: 150vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 28px;
padding: 0 clamp(18px, 5vw, 72px);
text-align: center;
}
.titolo {
margin: 0;
font-size: clamp(56px, 11vw, 190px);
font-weight: 820;
font-stretch: 75%;
line-height: 0.86;
letter-spacing: -0.02em;
text-transform: uppercase;
will-change: transform, filter, opacity;
}
.sottotitolo {
max-width: 32ch;
margin: 0;
font-size: clamp(15px, 1.3vw, 19px);
line-height: 1.45;
color: var(--tenue);
will-change: transform;
}
.citazione {
min-height: 110vh;
display: grid;
place-items: center;
padding: 0 clamp(18px, 8vw, 140px);
}
blockquote {
margin: 0;
max-width: 24ch;
font-size: clamp(28px, 4vw, 60px);
font-weight: 300;
font-stretch: 118%;
line-height: 1.15;
letter-spacing: -0.02em;
text-align: center;
will-change: filter, opacity;
}/* Titoli che vengono a fuoco
Ogni titolo è legato allo scorrimento del suo capitolo. All'ingresso è sfocato, semitrasparente e più
in basso; entro il primo quarto della corsa arriva a fuoco. Poi sale piano mentre il sottotitolo sale
molto più in fretta e lo supera: due velocità diverse danno profondità senza nessun 3D. Le citazioni
fanno solo la messa a fuoco, un po' più lenta. */
gsap.registerPlugin(ScrollTrigger);
/* ---- valori da cambiare ---- */
const SFOCATO = 14; /* pixel di sfocatura all'ingresso del titolo */
const A_FUOCO = 0.24; /* a che punto della corsa il titolo è nitido */
const SU_TITOLO = -30; /* di quanto sale il titolo alla fine, in percentuale della sua altezza */
const SU_SOTTO = -150; /* di quanto sale il sottotitolo: di più, quindi più veloce */
const RIDOTTO = matchMedia('(prefers-reduced-motion: reduce)').matches;
if (!RIDOTTO) {
document.querySelectorAll('[data-fuoco]').forEach((cap) => {
const titolo = cap.querySelector('.titolo');
const sotto = cap.querySelector('.sottotitolo');
gsap.timeline({
defaults: { ease: 'none' },
scrollTrigger: { trigger: cap, start: 'top bottom', end: 'bottom top', scrub: 0.8 },
})
.fromTo(titolo, { filter: `blur(${SFOCATO}px)`, opacity: 0.35, yPercent: 50 },
{ filter: 'blur(0px)', opacity: 1, yPercent: 0, duration: A_FUOCO, ease: 'power2.out' }, 0)
.fromTo(sotto, { yPercent: 60, opacity: 0 }, { yPercent: 0, opacity: 1, duration: A_FUOCO, ease: 'power2.out' }, 0.05)
.to(titolo, { yPercent: SU_TITOLO, duration: 1 - A_FUOCO }, A_FUOCO)
.to(sotto, { yPercent: SU_SOTTO, duration: 1 - A_FUOCO }, A_FUOCO);
});
document.querySelectorAll('[data-citazione] blockquote').forEach((q) => {
gsap.fromTo(q, { filter: 'blur(8px)', opacity: 0.35 }, {
filter: 'blur(0px)', opacity: 1, ease: 'power1.out',
scrollTrigger: { trigger: q, start: 'top bottom', end: 'top 45%', scrub: 0.8 },
});
});
}Each chapter has its own scrubbed timeline, from entering at the bottom to leaving at the top. Inside, durations are fractions of the run: focusing lasts 0.24, a quarter.
The title goes from blur(14px), opacity 0.35 and yPercent 50 to sharp, solid and in place, with power2.out: it arrives decisively and then settles.
For the rest of the run the title rises by 30% of its height, the subtitle by 150%: same duration, different distances, so the subtitle goes faster and overtakes it. It is parallax without images.
Quotes only focus, from blur(8px) to sharp, between entering and a little past the middle of the screen: they do not move, so they can be read.
| Name | File | Default | What it changes |
|---|---|---|---|
SFOCATO | script.js | 14 | Pixels of blur on entry. |
A_FUOCO | script.js | 0.24 | At which point of the run the title is sharp. |
SU_TITOLO / SU_SOTTO | script.js | -30 / -150 | How far title and subtitle rise after focusing. |
For chapter openers on a long page: reports, stories, manifestos. Not for text to be read continuously: a blurred paragraph is a paragraph nobody reads.
Identical. The blur filter on huge text is heavy: keep it for titles only.
Titles and quotes are sharp and still.
GSAP 3.15.0, ScrollTrigger (GSAP) 3.15.0, Mona Sans (font variabile) 5.3.0.
Weight: 5.2 KB of our own files.
Tested on 27 September 2026
Use it in your own projects and in your clients’ projects. Do not resell it or redistribute it as is. GSAP stays under its own Standard license. Full license.
With your account’s email and password.
You’re signed in as .
Your plan is free. When lifetime access arrives, it will switch on for this same account.