Capitolo 07
Anatomia di un input
Un campo da compilare è il momento in cui un sito chiede qualcosa all'utente — e in quel momento si vede subito che voce ha. Lo apriamo qui, dalla label che non mente alla validazione che ascolta.
C’è un istante, in ogni prodotto, in cui un sito smette di parlare all’utente e gli chiede qualcosa. Un nome, una mail, una password, un indirizzo. È il primo vero scambio bidirezionale — fino a quel momento la pagina si era limitata a raccontarsi. E come in ogni conversazione, la differenza tra una domanda gentile e una domanda goffa si sente subito: lo si capisce dalle prime tre righe.
L’input è dove la UX diventa fisica. Non c’è più nulla da nascondere dietro a una landing curata: si chiede una cifra, ci si aspetta una cifra. Se la label mente, se l’errore sgrida, se la tastiera mobile è quella sbagliata, l’interfaccia comincia a fare attrito. E ogni attrito sui form ha un costo misurabile — abbandono, tassi di errore, supporto. Ma soprattutto ha un costo non misurabile: l’utente smette di sentirsi ospite e comincia a sentirsi interrogato.
Sei dimostrazioni, ognuna su una decisione che cambia la qualità del momento in cui un campo viene compilato. Dalla forma del campo a riposo, alla validazione che aspetta, fino alla textarea che cresce da sola. Niente librerie di form, niente schemi complessi — l’HTML nativo, quando lo si conosce, fa quasi tutto. Il resto è una manciata di righe di JS che si potrebbe quasi non scrivere.
Il campo che si apre
La prima cosa che un input deve fare è farsi notare come un input. Non come un placeholder che galleggia in mezzo al testo, non come un’area cliccabile che potrebbe essere un bottone. Tre elementi rendono un campo riconoscibile a colpo d’occhio: una label sopra, un bordo che lo delimita, un focus state che dice “ti sto ascoltando”.
Come preferisci che ti chiamiamo.
Campo a riposo · label, bordo, halo
La label è una vera <label> legata all’input via for/id — cliccarla mette a fuoco il campo, è una conquista di accessibilità che non costa nulla. Sta sopra, non dentro: un placeholder che si comporta da label scompare proprio quando l’utente comincia a scrivere e perde il contesto della domanda. Il placeholder qui resta un esempio (“es. Anna Bianchi”), non un’istruzione.
Il focus produce due cose insieme: il bordo diventa accent, e attorno appare un box-shadow di 3px con il colore dell’accent al 40% di opacità — l’halo. Una sola di queste due cose non basterebbe: il bordo da solo si confonde con lo stato di hover, l’halo da solo sembra un errore. Insieme, l’attenzione del sistema è esplicita. L’hint sotto è collegato via aria-describedby: lo screen reader lo legge subito dopo il nome del campo, come una nota in voce bassa.
La validazione che ascolta
Il difetto più comune nei form non è la validazione assente: è la validazione impaziente. L’utente clicca dentro al campo, comincia a scrivere “a”, e già appare un errore rosso che dice “email non valida”. Il messaggio è tecnicamente corretto, ma il tempismo è offensivo — l’utente sta scrivendo, di certo non ha ancora finito. CSS ha la pseudo-classe giusta: :user-invalid si attiva solo dopo che l’utente ha interagito col campo. È stata pensata esattamente per questo.
Errore inline · solo dopo il primo blur
Prova a scrivere “ciao” e a uscire dal campo: l’errore scivola sotto, con il bordo che si tinge di rosso e un’icona che lo precede in lettura. Prova invece a digitare senza mai uscire dal campo: nessun errore, perché :user-invalid aspetta che l’interazione sia “conclusa” (blur o submit). È la versione CSS-only di quello che prima si faceva con un sacco di JS che ascoltava ogni keystroke per decidere quando “era il momento” di mostrare l’errore. Il browser lo sa già.
L’errore è connesso all’input via aria-describedby e usa role=“status” con annuncio polite — gli screen reader lo leggono dopo, non interrompono. Sul submit, se ci sono campi invalidi, il focus va al primo problematico e viene fatto scattare un evento di blur sintetico per attivare :user-invalid anche su chi non l’aveva ancora visitato. Il bottone non sgrida — semplicemente non manda. La validazione gentile non è permissiva: è solo a tempo debito.
Il numero come quantità
Un campo “quantità” — il numero di persone a tavola, di pezzi nel carrello, di porzioni in una ricetta — non è un campo di testo qualunque. Ha un dominio finito (un minimo, un massimo, un passo) e tre modi naturali per essere modificato: digitando, cliccando i pulsanti ±, usando le frecce di tastiera. Un buon stepper li offre tutti e tre, senza obbligare a sceglierne uno.
min 0 · max 20 — frecce ↑↓ per regolare da tastiera.
Stepper · digita, clicca, freccia
I bottoni − e + sono <button type=“button”> con aria-label esplicito — non basta l’icona, lo screen reader deve sentire “Aumenta di uno”. Quando il valore tocca il minimo, il bottone ”−” si disabilita; lo stesso per il massimo. La cifra centrale usa tabular-nums per non sussultare al passaggio da “9” a “10” (è la lezione del cap. 06). L’input ha inputmode=“numeric” per richiamare la tastiera numerica su mobile, ma resta type=“text” con pattern=“[0-9]*”: type=“number” ha troppi comportamenti strani fra browser per essere affidabile in produzione.
Da tastiera, ArrowUp e ArrowDown regolano il valore senza spostare il focus. Mentre l’utente digita, vengono filtrati i non-numeri ma non si clamp-a il valore: se sto scrivendo “10”, la tappa intermedia “1” non dev’essere forzata al minimo. Il clamping avviene al blur. È la differenza tra un sistema che ti corregge mentre parli e un sistema che ti lascia finire la frase.
La password e il suo velo
Quando un utente compila un campo password, una sola pressione di tasto sbagliata vuol dire ricominciare. Tutti l’abbiamo fatto: si scrive con cura, si invia, “password errata”, si scopre dopo cinque tentativi che il Caps Lock era attivo. Un occhio piccolo accanto al campo — un toggle reveal — risolve il 90% di quegli errori. È controintuitivo (mostrare la password sembra meno sicuro) ma è proprio il contrario: meno tentativi falliti, meno reset, meno tracce nelle password ricordate per errore.
Forza: —
Password con reveal e forza onesta
Il toggle è un <button type=“button”> con aria-pressed che alterna fra false e true; l’aria-label cambia di conseguenza (“Mostra la password” / “Nascondi la password”). Cambia anche il type dell’input fra “password” e “text”. Quattro barre sotto formano un indicatore di forza con quattro stati: debole (rosso), media (ambra), buona (giallo-verde), forte (verde) — colori in OKLCH per restare percettivamente equilibrati.
L’euristica è semplice e onesta: si combinano lunghezza e classi di caratteri usate (minuscole, maiuscole, cifre, simboli). Non vuole essere zxcvbn — vuole essere leggibile in trenta righe e dare un segnale ragionevole. Il messaggio testuale “Forza: buona” usa aria-live=“polite”, quindi viene annunciato senza interrompere il typing. L’attributo autocomplete=“new-password” dice al password manager: genera una nuova, non riempire con una esistente — è il token corretto per registrazione o cambio password.
L’autofill rispettato
Il password manager moderno (1Password, il keychain del sistema, l’autofill del browser) sa già nome, cognome, indirizzo, email, telefono. Se gli attributi autocomplete sono giusti, un form di spedizione si riempie con una sola pressione. Se sono sbagliati o mancanti, l’utente digita ogni campo a mano. È la stessa interfaccia con due esperienze radicalmente diverse — e tutta la differenza la fanno una decina di parole nei name e negli autocomplete.
Form indirizzo · autocomplete completo
Ogni campo ha il token autocomplete esatto: given-name, family-name, street-address, address-level2 (la città in molti paesi), postal-code, email, tel. Sul telefono e sul CAP, inputmode=“numeric” e “tel” richiamano la tastiera giusta su mobile. Il <fieldset> con <legend> raggruppa l’indirizzo: per uno screen reader la prima cosa che si sente entrando nel primo campo è “Indirizzo di spedizione, Nome”.
Quando un campo viene compilato dall’autofill, la regola :autofill applica un’ombra interna di accent al 8%: una sfumatura tenue che dice questo è stato riempito dal browser, controllalo se vuoi. È la stessa cura che si avrebbe stampando un foglio prima di firmarlo. Se vuoi vedere l’effetto, abilita un autofill nel browser, ricarica la pagina e clicca su Nome.
La textarea che cresce con te
L’ultimo dettaglio è il più giovane. Per anni l’unico modo di far crescere una textarea con il testo era misurare lo scrollHeight in JavaScript, copiarlo sull’altezza, gestire la sincronizzazione su input, sopportare lo sfarfallio al primo carattere. Oggi c’è una riga CSS: field-sizing: content. Il campo si adatta da solo, dentro un range definito da min-height e max-height. È supportato in tutti i browser evergreen — finalmente.
field-sizing: content — il campo si adatta al testo, da min a max.
Textarea autoresize · field-sizing: content
Scrivi due righe, poi venti. Il campo cresce fino a 14rem, poi smette e attiva lo scroll interno — il limite c’è, ma non sembra un muro. Il contatore in alto a destra cambia colore a due soglie: a 192 caratteri (80% di 240) diventa ambra come pre-avviso, a 240 diventa rosso e in grassetto. maxlength blocca a livello browser, quindi non si può sforare; il contatore è solo un feedback visivo, non un guardiano.
Il fallback JS per i pochi browser che ancora non supportano field-sizing è leggero (otto righe) e si attiva solo se CSS.supports(‘field-sizing’, ‘content’) torna falso — niente penalità per chi sta su un browser moderno. aria-live=“polite” sul contatore lo rende leggibile dagli screen reader nelle pause del typing, senza interrompere. È un esempio di come la piattaforma stia recuperando, una proprietà alla volta, i pattern che fino a ieri richiedevano una libreria.
Sei interventi piccoli, ognuno su una decisione che da soli sembrano trascurabili e insieme cambiano il carattere di tutto un prodotto. La label che non mente, l’errore che aspetta, il numero che si lascia regolare in tre modi diversi, la password che si fa rileggere, l’indirizzo che si riempie con un click, la textarea che cresce senza ostacoli. Nessuna di queste cose si nota se è ben fatta. Si nota l’assenza dell’attrito — ed è proprio il principio di Altrove: tutto quello che succede senza che l’utente se ne accorga è la vera potenza della UX, nel frattempo si sta concentrando su altro.
Il prossimo capitolo torna alla tipografia: la micro-tipografia. Perché tutto quello che abbiamo costruito qui — i numeri, i nomi, le password, gli indirizzi — è anche, e prima di tutto, testo. E il testo ha mille piccole decisioni che decidono se la pagina respira o trattiene il fiato.