Popup GSAP accessibile in Webflow (con scroll lock)

Avanzato 23:50 webflowgsapaccessibilitàpopupscroll-lock

Costruisci un popup modale accessibile in Webflow con GSAP — ruoli ARIA corretti, una timeline apri/chiudi reversibile, chiusura al click esterno e scroll lock mobile-safe.

Punti chiave

  • L'accessibilità di un modale sta in tre attributi ARIA: role="dialog", aria-modal="true" e aria-labelledby che punta all'id del titolo così gli screen reader lo annunciano per nome.
  • Usa un vero button (un custom element con type=button), non un link stilizzato — i button sono focusabili, usabili da tastiera e annunciati come azioni; aggiungi un aria-label ai button con la sola icona.
  • Una timeline reversibile gestisce sia apertura SIA chiusura: costruisci l'animazione di apertura, poi imposta il control del trigger di chiusura su reverse — niente animazione duplicata.
  • La chiusura al click esterno richiede un layer overlay dietro il popup (così i click dentro il popup non lo chiudono), e unire i trigger di chiusura è più pulito con un attributo custom condiviso.
  • Lo scroll lock è un cambio di stato istantaneo, quindi usa la set action di GSAP per portare l'overflow del body a hidden; su mobile dimensiona il wrapper fixed con 100dvh (non vh) così i cambi dell'UI del browser non lo rompono.

Capitoli del video

  1. 00:00 Intro
  2. 01:16 Analisi della struttura del popup e accessibilità
  3. 08:07 Costruire l'interazione apri/chiudi con GSAP
  4. 13:39 Chiudere il popup cliccando fuori
  5. 16:41 Usare gli attributi HTML per unire più trigger
  6. 18:45 Bloccare lo scroll della pagina quando il popup è aperto
  7. 23:03 Outro

La maggior parte dei tutorial sui popup ti dà un popup. Questo ti dà un modale pronto per la produzione: ARIA corretto così gli screen reader lo capiscono, un vero button focusabile per chiuderlo, una singola timeline GSAP reversibile per apri e chiudi, chiusura al click esterno e uno scroll lock vero che si comporta bene su mobile. È tanto una lezione di accessibilità quanto di animazione.

Il tema di fondo è architettura invece di hack. Ogni pezzo — i ruoli, la timeline reversibile, i trigger basati su attributi, lo scroll lock — è scelto perché il tutto resti pulito e riutilizzabile.

Come funziona

L’accessibilità viene prima. L’elemento dialog porta tre attributi ARIA: role="dialog" dice alle tecnologie assistive che questa è una finestra di dialogo e non un contenitore generico; aria-modal="true" segnala che lo sfondo non è interattivo mentre è aperto; e aria-labelledby="modal-title" collega il dialogo all’id del suo titolo, così uno screen reader annuncia qualcosa come “dialogo, Iscriviti alla newsletter” invece di una scatola senza nome. Sia il controllo di apertura sia quello di chiusura sono veri button — custom element con type="button", non il button nativo di Webflow (che viene reso come <a>, cioè navigazione). Un button è focusabile, funziona con Invio/Spazio ed è annunciato come azione. Dato che hanno la sola icona, ciascuno riceve un aria-label (“chiudi dialogo”, “apri dialogo iscrizione newsletter”).

Una timeline, invertita. Il wrapper è position: fixed, a tutto viewport, z-index alto, display: none di default. L’interazione di apertura è tre action: una set che porta il display del wrapper da none a flex a 0s, una animate che fa il fade in dell’opacity del wrapper (un from 0%), e una animate che fa scivolare su il popup (un from su move-Y, es. 2rem). Per chiudere non costruisci nulla di nuovo — aggiungi un trigger click al pulsante di chiusura e imposta il suo control su reverse. GSAP riproduce l’intera timeline all’indietro.

Click esterno per chiudere, fatto bene. Attaccare il trigger di chiusura direttamente al wrapper scatterebbe a ogni click, anche dentro il form del popup. Aggiungi invece un div overlay che riempie il wrapper ma sta dietro il popup — dato che entrambi hanno z-index: auto, lo stacking segue l’ordine nel DOM, quindi trascina l’overlay sopra il popup nel navigator per spingerlo visivamente dietro. Ora i click dentro il popup non raggiungono mai l’overlay; solo quelli fuori sì. Invece di mantenere due trigger di chiusura (button + overlay), dai a entrambi lo stesso attributo custom (dialog-animation="close") e colpisci l’attributo — un solo trigger, struttura più pulita.

Lo scroll lock è un cambio di stato, non un’animazione. Bloccare lo scroll significa portare l’overflow del body a hidden; sbloccarlo, a visible. È istantaneo, quindi è una set action, aggiunta a 0s della timeline di apertura e ripristinata in automatico al reverse. Un tranello mobile: cambiare l’overflow del body può spostare i calcoli del viewport e rompere il wrapper fixed, quindi dai al wrapper height: 100dvh. A differenza di vh (basato sul viewport iniziale e che non segue l’UI del browser mobile che si espande/collassa), dvh — dynamic viewport height — corrisponde sempre all’area realmente visibile.

Come si usa

  1. Struttura + ARIA. Wrapper: position: fixed, full, z-index alto, display: none, height: 100dvh, flex centrato. Sul dialog: role="dialog", aria-modal="true", aria-labelledby="modal-title" (l’id del titolo). Rendi i controlli apri/chiudi custom element button con aria-label.

  2. Timeline di apertura. Sul pulsante di apertura, un’interazione click GSAP con tre action: set wrapper display: flex a 0s; animate opacity del wrapper from 0% (0.8s, ease in-out power1); animate move-Y del popup from 2rem (stesso timing).

  3. Chiusura con reverse. Sul pulsante di chiusura, aggiungi un trigger click con control: reverse.

  4. Overlay per il click esterno. Aggiungi un div popup-closing-overlay (position: absolute, full) e trascinalo sopra il popup nel navigator. Dai all’overlay e al pulsante di chiusura lo stesso attributo dialog-animation="close", poi punta il trigger di chiusura a quell’attributo ed elimina il duplicato.

  5. Scroll lock. Aggiungi una set action a 0s che colpisce body (custom selector), impostando overflow: hidden. Pubblica e prova sul link live — l’apertura blocca lo scroll, la chiusura lo ripristina finito il reverse.

Risorse

Domande frequenti

Quali attributi ARIA servono a un popup modale accessibile?
Tre sull'elemento dialog: role="dialog" (così gli screen reader lo trattano come un dialogo, non un contenitore generico), aria-modal="true" (così lo sfondo è annunciato come non interattivo) e aria-labelledby impostato sull'id del titolo del popup, che dà al dialogo un nome accessibile annunciato all'apertura.
Il pulsante di chiusura di un popup deve essere un link o un button in Webflow?
Un button. Usa un custom element con type button — è focusabile, funziona con Invio e Spazio ed è annunciato come azione. Il button nativo di Webflow viene reso come link (un <a>), che rappresenta navigazione, non un'azione. Per un button con la sola icona, aggiungi un aria-label tipo "chiudi dialogo" così non viene annunciato solo come "button".
Come apro e chiudo un popup con una sola animazione GSAP?
Costruisci l'animazione di apertura una volta (es. set del display del wrapper a flex, fade in, slide su del popup). Poi sul trigger di chiusura imposta il control su reverse — GSAP riproduce tutta la timeline all'indietro, così non costruisci mai un'animazione di chiusura separata.
Come chiudo un popup quando si clicca fuori?
Aggiungi un div overlay che riempie il wrapper ma sta dietro il popup nel DOM (trascinalo sopra il popup nel navigator così il popup resta in cima). Dagli il trigger di chiusura. Dato che sta dietro il popup, i click dentro il popup non lo raggiungono — solo i click fuori chiudono il modale. Uniscilo al pulsante di chiusura dando a entrambi lo stesso attributo custom.
Come blocco lo scroll quando un popup è aperto, e perché usare dvh?
Bloccare lo scroll è solo impostare l'overflow del body a hidden — un cambio istantaneo, quindi usa la set action di GSAP (non animate) all'inizio della timeline di apertura, e viene ripristinato in automatico quando la timeline si inverte. Dai al wrapper fixed height: 100dvh così su mobile, dove l'UI del browser si espande e collassa, l'overlay corrisponde sempre al viewport realmente visibile.

← Torna al corso