Capitolo 04
Il dialogo silenzioso
Un dialog è un'interruzione che chiede permesso. Quando deve davvero parlare, lo fa con la tastiera in mano: focus dove serve, Escape che ricuce, animazioni che entrano come un sospiro e mai come un grido.
C’è una cattiveria silenziosa nei dialog mal fatti: rubano il focus a metà di un pensiero, ti fanno cercare il bottone “Annulla” col mouse, chiudono il contesto sotto come una porta sbattuta. Il dialog migliore fa l’opposto — sa di essere un’interruzione e si comporta da ospite: bussa, aspetta che tu sia pronto, e lascia la stanza nello stato in cui l’ha trovata.
Dietro un dialog si nascondono dieci responsabilità. Dove va il focus all’apertura? Dove torna alla chiusura? La tastiera è davvero intrappolata dentro, o un Tab maldestro porta fuori? Si può uscire premendo Escape? Lo sfondo è davvero non-interagibile, anche per uno screen reader? Il click sul backdrop chiude — sempre, mai, o quando? La risposta a ognuna di queste domande è una piccola decisione di rispetto.
Apriamo sei dialog diversi — dal più amichevole (un overlay che spiega il proprio linguaggio) al più severo (la conferma di un’azione irreversibile) — e mostriamo come ognuno fa silenzio in modo diverso.
L’overlay che spiega il proprio linguaggio
Le scorciatoie da tastiera sono un dono che si fa solo a chi le scopre. Ma scoprirle non deve essere un atto di archeologia: una buona interfaccia ha sempre un ? che chiama un overlay con tutte le combinazioni, raggruppate, leggibili, e che chiude con Escape come una guida che ha finito di parlare.
Premi ? ovunque (fuori dai campi di testo) per aprire la legenda delle scorciatoie.
Overlay scorciatoie · `?` chiama, Esc chiude
L’overlay si apre con <dialog>.showModal() — un elemento HTML nativo che ti regala focus trap, gestione di Escape, top-layer rendering e backdrop dismiss. Niente librerie, niente focus-trap manuali. Il listener globale ascolta ? solo se il focus non è in un input — perché chi sta scrivendo “perché” non vuole vedere le scorciatoie.
L’ingresso dura 220 ms, l’uscita 160 ms — regola d’oro del motion: le cose escono più rapidamente di quanto entrano. È coerente con come si comportano gli oggetti fisici (entrano nella vista lentamente, vengono ritirati di scatto) e con come l’utente già si aspetta che funzionino. Reduced-motion riduce tutto a 1 ms, l’overlay è disponibile lo stesso.
La conferma che lavora per dirti no
La conferma di un’azione distruttiva ha un compito strano: deve permettere l’azione, ma il suo vero lavoro è impedire che accada per errore. Un buon dialog di conferma mette il default sicuro sotto la mano dell’utente — focus su Annulla, Enter conferma Annulla, click sul backdrop non chiude. La macchina lavora per dirti no.
-
Pensieri di domenica
-
Lista letture
-
Frammenti per il colofone
Conferma distruttiva · default sicuro su Annulla
Tre note in una lista. Click sul cestino apre il dialog: titolo onesto (“Eliminare questa nota?”), descrizione che ricorda l’irreversibilità (“L’azione non si annulla.”), due bottoni — Annulla (primario visivo, autofocus, accent-ink) e Elimina (testo soltanto, color-ink rosso desaturato, secondario). Enter chiude annullando, Esc chiude annullando, click sul backdrop non chiude. Per confermare l’eliminazione serve un atto deliberato: Tab + Enter o un click esplicito.
Il aria-labelledby punta al titolo, aria-describedby alla descrizione — uno screen reader sa subito di cosa parla il dialog e cosa sta per succedere. Quando l’utente annulla, un aria-live=“polite” sussurra “Annullato” — non per celebrare, ma per chiudere la frase iniziata. È un piccolo dettaglio invisibile, ma quando manca si sente.
Il foglio che sale dal basso
Lo stesso contenuto su un telefono e su un desktop chiede due forme diverse: in alto al centro un dialog si comporta come un cartellone; in basso a tutto schermo si comporta come un cassetto aperto. Un bottom sheet ben fatto su mobile, su desktop diventa un dialog centrato senza cambiare markup — solo la sua geometria si adatta al pollice o al cursore.
Su mobile esce dal basso; su desktop al centro.
Sheet inferiore · viene dal basso su mobile, centrato su desktop
Click su “Condividi” apre uno <dialog> nativo. Su < 640px lo stile lo fa partire dal bordo inferiore con uno slide-up di 320 ms (preset spring gentle); su ≥ 640px diventa un dialog centrato standard. Il drag-handle in cima permette di chiuderlo con uno swipe-down (PointerEvent, soglia 60 px) — un gesto naturale per chi è su mobile, invisibile per chi non lo è.
Tre opzioni: copia link, email, altro. Niente avatar, niente icone superflue — è uno sheet che fa una cosa, la fa bene, e si toglie. Tap fuori chiude (lo stato è recuperabile, non c’è perdita di lavoro). Reduced-motion sostituisce lo slide con un fade istantaneo: lo sheet appare e basta, senza spostarsi nello spazio.
Un dialog dentro un dialog, senza inciampare
Il caso più difficile è anche il più frequente di quanto si pensi: un dialog aperto che ne richiede un altro. Le impostazioni di un account che chiedono una conferma per disattivarsi. La risposta sbagliata è chiudere il primo e aprire il secondo — l’utente perde il contesto. La risposta giusta è impilare i dialog, marcare i precedenti come inert, e ricucire il focus alla chiusura.
Dialog annidato · uno dentro l'altro, senza intrappolarsi
Click su “Impostazioni account” apre il primo dialog. Dentro, “Disattiva account” apre un secondo dialog sopra il primo. Il primo resta visibile sotto ma riceve l’attributo inert: Tab, click e screen reader saltano completamente i suoi elementi. Il backdrop del secondo dialog ha un tono più scuro (somma percettiva): si vede chiaramente che è uno strato in più.
Il pattern di focus restoration è uno stack manuale: salvo l’elemento attivo prima di ogni apertura, lo ripristino alla chiusura corrispondente. Chiudendo il nested, il focus torna sul bottone “Disattiva account” dentro il primo dialog (non sul trigger originale). Chiudendo il primo, torna al trigger nello stage. È una piccola coreografia che, fatta bene, sembra naturale. Fatta male, è il modo più rapido di perdere fiducia.
Quando il dialog è troppo: confermare in linea
L’errore più comune nei dialog è… aprirne troppi. Una conferma per ogni piccola azione è un pedaggio costante: l’utente smette di leggere e clicca su tutto. La conferma migliore, quando l’azione è reversibile o piccola, è in linea — il messaggio si trasforma nel suo “sei sicuro?” senza spostare il contesto, senza intrappolare il focus, senza disturbare lo sfondo.
-
«Ho letto il capitolo nuovo — la voce del vuoto è davvero un'idea felice.»
Eliminare il messaggio? -
«Domani ci vediamo per i bottoni del capitolo successivo?»
Eliminare il messaggio? -
«Ho fatto una piccola lista di skeleton da sperimentare 👇»
Eliminare il messaggio?
Conferma in linea · quando il modale è troppo
Tre messaggi in una conversazione finta. Hover su un messaggio rivela un cestino. Click non apre un dialog: il messaggio si trasforma in un pannello inline — “Eliminare?” + due CTA piccole, Annulla (default, focus, accent) e Sì. Auto-timeout di 5 secondi ripristina lo stato originale. Esc annulla. È il dialog senza il dialog.
La voce in aria-live=“polite” annuncia “Conferma eliminazione” agli screen reader — c’è una micro-interruzione che però non spezza il filo. È l’anti-pattern del dialog: dimostra che non sempre serve aprirne uno. Quando l’azione è piccola, quando il contesto è già visibile, quando l’errore è reversibile — un dialog è uno spreco di attenzione. L’inline conferma fa lo stesso lavoro, in silenzio.
Il popover che resta dove serve
L’ultimo caso è il dialog non-modale: una piccola finestra contestuale che si apre vicino al suo trigger, non blocca lo sfondo, e si chiude da sola quando l’utente clicca altrove. È il giusto pattern per filtri, scelte rapide, micro-form. La regola: se l’informazione è contestuale, non spostarla al centro dello schermo.
Filtra capitoli
Popover ancorato · non-modale, non interrompe
Bottone “Filtri” che apre un popover ancorato sotto. Tre checkbox e una CTA “Applica”. Niente backdrop, niente blocco: lo sfondo resta cliccabile, e cliccando fuori il popover si chiude (commit dei filtri). Esc chiude annullando (i filtri tornano allo stato precedente). Tab dentro circola tra le opzioni — ma è un focus trap morbido: dopo l’ultimo elemento, Tab esce e chiude il popover. Non si intrappola l’utente.
Tecnicamente usiamo la Popover API nativa (popover=“auto” + popovertarget) — un’aggiunta recente del web che dà light-dismiss e gestione di Escape gratis. L’ancoraggio sotto il trigger usa CSS Anchor Positioning (anchor-name / position-anchor) dove supportato, con fallback a position: absolute calcolato in JS per browser più vecchi. aria-expanded sul trigger riflette lo stato, sempre.
Cosa portiamo via
Un dialog è un’interruzione. Quando è necessaria, deve farsi piccola. Le sei dimostrazioni qui sopra mostrano sei modi diversi di “interrompere bene”:
- L’overlay strumento che spiega il proprio linguaggio (e si toglie con la stessa scorciatoia con cui è arrivato).
- La conferma distruttiva che lavora per dirti no — default sicuro, backdrop blindato.
- Lo sheet adattivo che cambia geometria fra mobile e desktop senza cambiare markup.
- Il dialog annidato che impila il contesto senza romperlo.
- L’anti-dialog in linea — che dimostra quando non aprire un modal.
- Il popover ancorato non-modale, che resta dove l’utente sta guardando.
Sei tipi di interruzione, una stessa filosofia: rispettare il filo del pensiero di chi sta usando l’interfaccia. Il prossimo capitolo cambia scala — lo scroll che racconta — perché dopo aver imparato a interrompere bene, è tempo di imparare anche a non interrompere mai, lasciando che sia il gesto dell’utente a far scorrere la storia.