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
-
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 elementbuttonconaria-label. -
Timeline di apertura. Sul pulsante di apertura, un’interazione click GSAP con tre action: set wrapper
display: flexa 0s; animate opacity del wrapper from0%(0.8s, ease in-out power1); animate move-Y del popup from2rem(stesso timing). -
Chiusura con reverse. Sul pulsante di chiusura, aggiungi un trigger click con control: reverse.
-
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 attributodialog-animation="close", poi punta il trigger di chiusura a quell’attributo ed elimina il duplicato. -
Scroll lock. Aggiungi una set action a 0s che colpisce
body(custom selector), impostandooverflow: hidden. Pubblica e prova sul link live — l’apertura blocca lo scroll, la chiusura lo ripristina finito il reverse.