Per chi sviluppa, il web design è un insieme di decisioni che il codice rende ripetibili: una gerarchia visiva chiara, una scala di spaziature e una tipografica, una palette con contrasto verificato, un layout che regge su ogni larghezza, un’interfaccia usabile da tastiera e una pagina che appare in fretta senza saltare. Non serve essere designer: serve trasformare queste regole in variabili CSS e controlli che si ripetono a ogni rilascio.
Questa guida segue l’ordine in cui le scelte si presentano quando si costruisce un’interfaccia, con esempi CSS da adattare al proprio progetto e, in fondo, gli errori UX più frequenti e una checklist da usare prima di consegnare.
Indice
- Gerarchia visiva: cosa si guarda per primo
- Spaziatura e griglia
- Tipografia leggibile e scala con clamp()
- Colori, variabili CSS e contrasto WCAG
- Responsive senza perdere pezzi
- Accessibilità che si vede nel CSS
- Performance percepita
- Errori UX comuni lato sviluppo
- Checklist prima di consegnare
Gerarchia visiva: cosa si guarda per primo
La gerarchia visiva è l’ordine in cui l’occhio legge una pagina. La decidono quattro leve: dimensione, peso del font, colore e posizione. Se titolo, sottotitolo e paragrafo hanno la stessa dimensione e lo stesso peso, o se un colore acceso finisce su elementi secondari, tutto sembra ugualmente importante e l’utente deve leggere ogni riga per capire cosa conta.
Il punto di partenza è l’HTML: un solo <h1>, titoli che scendono di livello senza salti, sezioni marcate con gli elementi giusti. La struttura semantica dà l’ordine logico (quello che leggono motori di ricerca e screen reader), il CSS lo rende visibile. Se l’argomento è nuovo, la guida all’HTML semantico copre i tag da usare.
Tre regole pratiche:
- Una sola azione principale per vista. Il pulsante primario ha il colore d’accento e il massimo contrasto; le azioni secondarie sono bordate o testuali. Il testo dice cosa succede dopo il clic (“Scarica il PDF”, “Crea l’account”), non “Clicca qui”.
- Pochi livelli, ben distinti. Tre o quattro dimensioni di testo bastano per quasi ogni pagina. Due livelli troppo simili (18px e 19px) non creano gerarchia, creano rumore.
- Il test dello sguardo sfocato. Allontanati dallo schermo o sfoca la pagina con un filtro (
filter: blur(4px)sulbodynei DevTools): quello che resta riconoscibile è ciò che l’utente vede per primo. Se non è il titolo o l’azione principale, la gerarchia va rivista.
Spaziatura e griglia
Lo spazio vuoto raggruppa gli elementi che stanno insieme e separa quelli che non c’entrano: un’etichetta vicina al suo campo, più lontana dal campo successivo. Il problema tipico nel codice non è la mancanza di spazio, ma l’incoerenza: 15px in un componente, 17px in un altro, 1.2rem in un terzo. Ogni valore inventato al momento è un piccolo disallineamento che l’utente percepisce come trascuratezza, e un punto in più da cambiare quando il design evolve.
La soluzione è una scala di spaziatura definita una volta, con pochi gradini, e usata ovunque al posto dei numeri sparsi:
:root {
--space-1: 0.25rem;
--space-2: 0.5rem;
--space-3: 1rem;
--space-4: 1.5rem;
--space-5: 2rem;
--space-6: 3rem;
}
/* Spazio verticale uniforme tra i figli di un contenitore */
.stack > * + * {
margin-block-start: var(--space-3);
}
.card {
display: grid;
gap: var(--space-2);
padding: var(--space-4);
}
section + section {
margin-block-start: var(--space-6);
}
Dove possibile, preferisci gap ai margini sui figli: con Flexbox e Grid lo spazio appartiene al contenitore e non si somma per sbaglio. La griglia di pagina si costruisce con CSS Grid (struttura su due assi: header, contenuto, sidebar) e i componenti interni con Flexbox (allineamenti su un asse: barre di navigazione, gruppi di pulsanti). Per i dettagli ci sono le guide a CSS Grid e a Flexbox.
Tipografia leggibile e scala con clamp()
La maggior parte di un sito è testo, quindi la tipografia pesa più della palette. I valori di partenza sono noti:
- Corpo del testo da 16px in su, espresso in
remcosì rispetta la dimensione scelta dall’utente nel browser. - Interlinea tra 1.5 e 1.7 per i paragrafi, più stretta (1.1-1.3) per i titoli grandi. Usa valori senza unità, così si scalano con il font.
- Righe tra 45 e 75 caratteri. In CSS si ottiene direttamente con l’unità
ch:max-width: 65chsul contenitore del testo. - Una o due famiglie, pochi pesi. Ogni peso è un file da scaricare. Un font per i titoli e uno per il testo, oppure uno solo con pesi diversi, coprono quasi tutti i casi.
Sulla scelta tra serif e sans-serif circola un’idea datata: che i serif affatichino la lettura a schermo. Valeva per i monitor a bassa risoluzione; sugli schermi attuali un serif ben disegnato funziona anche per il testo lungo. Conta di più che il font abbia una buona altezza della x, spaziatura regolare e i caratteri accentati dell’italiano disegnati bene.
Per le dimensioni, una scala tipografica fluida con clamp(minimo, preferito, massimo) evita di riscrivere i font-size a ogni breakpoint: il valore cresce con la larghezza della finestra ma resta sempre dentro i limiti.
:root {
--step-0: clamp(1rem, 0.95rem + 0.25vw, 1.125rem); /* testo */
--step-1: clamp(1.25rem, 1.15rem + 0.5vw, 1.5rem); /* h3 */
--step-2: clamp(1.5rem, 1.3rem + 1vw, 2rem); /* h2 */
--step-3: clamp(1.875rem, 1.5rem + 1.9vw, 2.75rem); /* h1 */
--font-body: "Source Sans 3", system-ui, sans-serif;
--font-heading: "Merriweather", Georgia, serif;
}
body {
font-family: var(--font-body);
font-size: var(--step-0);
line-height: 1.6;
}
h1, h2, h3 {
font-family: var(--font-heading);
line-height: 1.2;
text-wrap: balance;
}
h1 { font-size: var(--step-3); }
h2 { font-size: var(--step-2); }
h3 { font-size: var(--step-1); }
.prose {
max-width: 65ch;
}
Un dettaglio che si sbaglia spesso: il valore centrale deve combinare rem e vw. Un font-size: 4vw puro non cambia quando l’utente fa zoom, e questo va contro il criterio WCAG sul ridimensionamento del testo. Con 0.95rem + 0.25vw la parte in rem segue lo zoom e quella in vw segue la larghezza. text-wrap: balance distribuisce meglio le parole sulle righe dei titoli; dove non è supportato viene semplicemente ignorato.
Colori, variabili CSS e contrasto WCAG
Per una palette di lavoro bastano un colore di superficie, uno o due colori di testo, un colore d’accento per le azioni e i colori di stato (errore, successo, avviso). La regola 60-30-10, presa dall’interior design, è un’indicazione utile sulle proporzioni: circa 60% di colore dominante (di solito lo sfondo), 30% di secondario, 10% di accento riservato a link e pulsanti. Non è una formula da misurare, serve a ricordare che l’accento funziona solo se è raro.
Nel codice conviene nominare le variabili per ruolo e non per tinta: --color-text invece di --blu-scuro. Così il tema scuro diventa una ridefinizione delle stesse variabili, senza toccare i componenti:
:root {
color-scheme: light dark;
--color-surface: #ffffff;
--color-text: #1f2937; /* 14.7:1 sul bianco */
--color-text-muted: #4b5563; /* 7.6:1 */
--color-accent: #0f766e; /* 5.5:1, anche con testo bianco sopra */
--color-on-accent: #ffffff;
}
@media (prefers-color-scheme: dark) {
:root {
--color-surface: #111827;
--color-text: #e5e7eb; /* 14.3:1 */
--color-text-muted: #9ca3af; /* 7:1 */
--color-accent: #5eead4; /* 12:1 */
--color-on-accent: #111827;
}
}
body {
background-color: var(--color-surface);
color: var(--color-text);
}
.button-primary {
background-color: var(--color-accent);
color: var(--color-on-accent);
}
I numeri nei commenti sono rapporti di contrasto, e sono la parte che non si sceglie a occhio. Le soglie WCAG di livello AA:
- 4.5:1 per il testo normale.
- 3:1 per il testo grande, cioè da 24px in su, o da circa 18.7px in su se in grassetto.
- 3:1 per i componenti dell’interfaccia e per gli indicatori di focus rispetto ai colori adiacenti (bordi dei campi, icone che servono a capire un controllo).
Un esempio di quanto sia facile sbagliare: testo bianco su un verde smeraldo come #10b981 sembra leggibile, ma il rapporto è circa 2.5:1 e non supera nemmeno la soglia per il testo grande. Lo stesso vale per il classico grigio #999 su bianco (2.8:1) usato per date e didascalie. Il pannello Contrast dei Chrome DevTools, nell’ispezione dei colori, mostra il rapporto e propone la tinta più vicina che passa. Infine, il colore non deve essere l’unico segnale: un campo in errore ha anche un messaggio di testo, un link nel corpo del testo resta sottolineato.
Responsive senza perdere pezzi
L’approccio mobile-first parte dagli stili per lo schermo stretto, senza media query, e aggiunge regole con min-width quando c’è spazio per affiancare gli elementi. Il vantaggio non è solo tecnico: obbliga a decidere cosa è essenziale, perché su 360px non c’è posto per tutto.
Molti layout non hanno nemmeno bisogno di breakpoint. Una griglia di card che si adatta da sola:
.cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(100%, 16rem), 1fr));
gap: var(--space-4);
}
img,
video {
max-width: 100%;
height: auto;
}
Il min(100%, 16rem) impedisce che una card larga 16rem esca dallo schermo sui telefoni più stretti. Quando servono media query, i breakpoint vanno messi dove il contenuto si rompe, non sulle dimensioni di un dispositivo specifico. Viewport, media query e un mini progetto completo sono nella guida al layout responsive.
Accessibilità che si vede nel CSS
Gran parte dell’accessibilità si gioca nell’HTML (etichette dei form, testi alternativi, elementi nativi invece di div cliccabili), ma alcune scelte sono puramente di stile e capita spesso che sia proprio il CSS a romperle.
/* Da non fare: chi naviga con Tab non sa più dove si trova */
:focus {
outline: none;
}
/* Focus visibile per tastiera, senza anelli al clic del mouse */
:focus-visible {
outline: 3px solid var(--color-accent);
outline-offset: 2px;
}
/* Link riconoscibili anche senza colore */
.prose a {
text-decoration-line: underline;
text-underline-offset: 0.2em;
}
/* Rispetto per chi ha disattivato le animazioni nel sistema */
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}
:focus-visible mostra l’indicatore quando serve (navigazione da tastiera) e lo nasconde al clic del mouse, che è il motivo per cui in molti progetti si toglieva l’outline. Altri due controlli rapidi: le aree cliccabili devono essere di almeno 24×24px secondo WCAG 2.2 (per i pulsanti principali su mobile conviene stare più larghi, intorno ai 44px), e ogni elemento interattivo deve avere uno stato :hover e :active che faccia capire che risponde.
Performance percepita
Una pagina lenta o che salta mentre carica è un difetto di design quanto un pulsante illeggibile. Google la misura con tre Core Web Vitals:
- LCP (Largest Contentful Paint): quando appare l’elemento principale. Buono entro 2,5 secondi.
- INP (Interaction to Next Paint): quanto la pagina risponde in fretta a clic e tasti. Buono entro 200 millisecondi. Ha sostituito il vecchio FID nel 2024.
- CLS (Cumulative Layout Shift): quanto il layout si sposta da solo. Buono sotto 0,1.
Le scelte di design che li influenzano di più riguardano immagini e font:
<!-- Immagine principale: niente lazy loading, priorità alta -->
<img src="hero.avif" width="1200" height="630"
alt="Dashboard con i grafici delle vendite mensili"
fetchpriority="high">
<!-- Immagini sotto la piega: caricate quando servono -->
<img src="dettaglio.webp" width="800" height="450"
alt="Dettaglio del filtro per data"
loading="lazy" decoding="async">
@font-face {
font-family: "Source Sans 3";
src: url("/fonts/source-sans-3-var.woff2") format("woff2");
font-weight: 200 900;
font-display: swap;
}
widtheheightsu ogni immagine riservano lo spazio prima del download ed eliminano la principale causa di CLS. Per contenitori senza immagine (embed, video) si usaaspect-ratio.loading="lazy"sull’immagine principale peggiora l’LCP: va usato solo sotto la piega.- Un font variabile in WOFF2 sostituisce più file di pesi diversi;
font-display: swapmostra subito il testo con il font di sistema. Scegliere fallback con metriche simili riduce lo spostamento quando arriva il font definitivo. - Gli script non necessari al primo rendering vanno caricati con
defer: meno lavoro sul main thread significa un INP migliore.
Il lato HTML e CSS della velocità è approfondito in performance HTML e CSS.
Errori UX comuni lato sviluppo
Questi errori nascono quasi sempre durante l’implementazione, anche partendo da un buon mockup. Ognuno ha una correzione precisa.
- Gerarchia piatta. Titoli e testo con dimensioni quasi uguali, colori vivaci su elementi secondari. Correzione: una scala tipografica con pochi gradini distinti e l’accento riservato alle azioni.
- Azione principale confusa con le secondarie. Tre pulsanti identici uno accanto all’altro. Correzione: un solo pulsante pieno, gli altri bordati o testuali, con un’etichetta che dice cosa succede.
- Contrasto insufficiente. Grigio chiaro su bianco per didascalie e testi di esempio nei campi dei form. Correzione: verificare ogni coppia testo/sfondo contro 4.5:1, nei due temi.
- Focus rimosso. Un
outline: nonenel reset globale rende il sito inutilizzabile da tastiera. Correzione::focus-visiblecon un outline di almeno 3:1 sullo sfondo. - Larghezze fisse in pixel sui contenitori. Un
width: 960pxgenera scroll orizzontale su mobile. Correzione:max-widthcon unità relative,minmax()nelle griglie, immagini conmax-width: 100%. - Funzioni nascoste su mobile.
display: nonesu filtri o tabelle perché “non entrano”. Correzione: ripensare il componente (filtri in un pannello apribile, tabelle con scroll orizzontale nel loro contenitore), non toglierlo. - Immagini senza dimensioni e troppo pesanti. Il testo salta mentre la pagina carica e l’LCP sale. Correzione:
width/height, formati AVIF o WebP,srcsetper servire la dimensione giusta. - Numeri magici e colori sparsi. Cinque varianti dello stesso blu e spaziature scelte a occhio. Correzione: variabili CSS per colori, spaziature e font-size, e una ricerca nel codice dei valori esadecimali fuori dalle variabili.
- Elementi interattivi che non sembrano tali. Link senza sottolineatura nel testo, pulsanti senza stato hover. Correzione:
:hover,:activee:focus-visiblesu ogni controllo, link sottolineati nei paragrafi.
Per gli errori più legati al CSS in sé (specificità, unità, box model) c’è la raccolta dei 7 errori CSS comuni.
Checklist prima di consegnare
- La pagina sfocata mostra per primi il titolo e l’azione principale.
- Spaziature, colori e font-size vengono tutti da variabili; nessun esadecimale sparso nei componenti.
- Il testo del corpo è almeno 1rem, con interlinea intorno a 1.6 e righe sotto i 75 caratteri.
- Ogni coppia testo/sfondo supera 4.5:1 (3:1 per testo grande, controlli e focus), sia nel tema chiaro sia in quello scuro.
- A 320px di larghezza non c’è scroll orizzontale e nessuna funzione è sparita.
- Tutta l’interfaccia si usa con Tab, Invio ed Esc, e il focus è sempre visibile.
- Con lo zoom del browser al 200% il testo cresce e il layout resta usabile.
- Lighthouse o il report Core Web Vitals danno LCP, INP e CLS nella fascia buona sulle pagine principali.








