Costruisci sull'HTML semantico prima di ricorrere a JavaScript
Usa l'elemento nativo corretto per ogni compito - button, a, nav, main, label, heading, liste - perché il markup semantico porta gratis supporto da tastiera, semantica per screen reader e resilienza, seguendo il principio del progressive enhancement. Stratifica CSS e JavaScript sopra per arricchire, non per sostituire, una base funzionante.
- Fai: usa un vero
- Non fare: collegare un handler di click a un
e reinventare male focus, tastiera e gestione del ruolo.
Imposta sempre le dimensioni di immagini e media
Dai a ogni immagine, video ed embed un width e un height espliciti o un aspect-ratio così che il browser riservi il box corretto prima che l'asset si carichi, prevenendo il Cumulative Layout Shift. Riservare lo spazio impedisce al contenuto circostante di saltare quando il media arriva.
- Fai:
così il layout è stabile dal primo paint.
- Non fare: spedire un
senza dimensioni che spinge giù il contenuto al caricamento, facendo schizzare il CLS oltre la soglia di 0,1.
Ottimizza con cura l'elemento Largest Contentful Paint
Identifica l'elemento che è il tuo LCP - di solito una hero image o un titolo - e dai priorità alla sua consegna con il preload, servendolo da una CDN, usando formati moderni e rimuovendo le risorse render-blocking che lo precedono. Un LCP buono è 2,5 secondi o meno ed è la metrica che gli utenti percepiscono come "la pagina si è caricata".
- Fai:
per la hero image. - Non fare: applicare il lazy load all'immagine LCP, che ritarda proprio il paint misurato dalla metrica.
Spedisci meno JavaScript con code splitting e tree shaking
Mantieni piccolo il bundle iniziale suddividendo il codice per route e facendo il lazy load delle feature pesanti con import() dinamico, e importando solo ciò che usi da moduli ES privi di side effect così che il bundler possa eliminare il resto con il tree shaking. Meno JavaScript significa Time To Interactive più veloce e INP migliore.
- Fai: carica pigramente una libreria di grafici solo nella route di analytics che ne ha bisogno.
- Non fare: importare un'intera libreria di utility per usare una funzione, trascinando tutto nel bundle.
Rendi l'interfaccia operabile da tastiera con un focus visibile
Assicurati che ogni controllo interattivo sia raggiungibile e attivabile con la sola tastiera, in un ordine di tabulazione logico, e non rimuovere mai il contorno di focus senza sostituirlo con uno altrettanto visibile. L'accesso da tastiera è un requisito WCAG di base e il fondamento di molte tecnologie assistive.
- Fai: dai uno stile a
:focus-visiblecon un contorno chiaro così che chi usa la tastiera veda sempre dov'è. - Non fare: impostare
outline: noneglobalmente, lasciando chi usa la tastiera incapace di seguire il focus.
Preferisci gli elementi HTML nativi ad ARIA
Segui la prima regola di ARIA: se un elemento HTML nativo fornisce la semantica e il comportamento che ti servono, usalo invece di ricrearli con ruoli ARIA e JavaScript. Un ARIA scorretto inganna attivamente le tecnologie assistive ed è peggio di nessun ARIA.
- Fai: usa
- Non fare: costruire
quando un elemento reale farebbe il lavoro correttamente.
Tratta ogni input utente come non fidato ed evita l'iniezione di HTML non sicura
Affidati alla codifica automatica dell'output del tuo framework quando renderizzi dati, e non passare mai contenuto utente non sanificato a innerHTML o dangerouslySetInnerHTML. Se devi renderizzare HTML, sanificalo con una libreria collaudata, perché l'XSS è una delle vulnerabilità front-end più comuni.
- Fai: renderizza il testo utente come testo tramite interpolazione, o sanifica con DOMPurify quando serve HTML.
- Non fare: costruire markup con concatenazione di stringhe di input utente e assegnarlo a
element.innerHTML.
Distribuisci una Content Security Policy rigorosa come difesa in profondità
Aggiungi un header Content Security Policy che limiti le origini di script e risorse, idealmente basato su nonce, così che anche se un'iniezione passa, il browser si rifiuti di eseguire script non autorizzati. Abbinala alla Subresource Integrity per gli script di terze parti per intercettare CDN manomesse.
- Fai: servi
Content-Security-Policy: script-src 'self' 'nonce-...'e aggiungi hashintegrityagli script esterni. - Non fare: affidarti al solo escaping del framework e consentire
script-src 'unsafe-inline', che vanifica la policy.
Progetta in modo responsive, mobile-first
Scrivi i tuoi stili di base per lo schermo più piccolo e usa media (o container) query min-width per arricchire i layout verso l'alto, così che l'esperienza più semplice sia il default e la complessità si aggiunga solo dove c'è spazio. Una singola codebase fluida serve la gamma imprevedibile di dispositivi reali meglio dei layout fissi per dispositivo.
- Fai: scrivi il layout di base per schermi stretti, poi
@media (min-width: 48rem)per aggiungere colonne. - Non fare: progettare desktop-first e attaccare override
max-width, combattendo la cascade per il mobile.
Limita il lavoro su eventi ad alta frequenza con debouncing o throttling
Avvolgi gli handler per eventi di scroll, resize e digitazione così che non scattino a ogni singolo evento, usando il debouncing quando vuoi la chiamata finale dopo che l'attività si ferma e il throttling quando vuoi una cadenza costante e limitata. Questo mantiene libero il main thread e protegge reattività e INP.
- Fai: applica il debounce a una richiesta di ricerca-mentre-digiti così che scatti quando l'utente fa una pausa.
- Non fare: emettere una richiesta di rete a ogni tasto premuto, inondando server e thread dell'interfaccia.
Scegli la strategia di rendering per route, non per app
Abbina ogni pagina alla modalità giusta: generazione statica (SSG) per contenuti che cambiano di rado, rendering server (SSR) per pagine dinamiche personalizzate o critiche per la SEO, e rendering client per app shell molto interattive. I framework permettono di mescolarle, perciò ottimizza ogni route invece di imporre una sola strategia ovunque.
- Fai: pre-renderizza le pagine marketing con SSG e renderizza sul server la dashboard autenticata con SSR.
- Non fare: renderizzare client una landing critica per la SEO, lasciando crawler e utenti al primo paint con un guscio vuoto.
Imposta e applica un performance budget in CI
Definisci limiti per dimensione del bundle e metriche chiave (LCP, INP, CLS, JavaScript totale) e controllali automaticamente a ogni modifica così che le regressioni siano intercettate prima del rilascio, non in produzione. Un budget trasforma le performance da ripensamento a barriera.
- Fai: fai fallire la pipeline quando il bundle principale supera il budget o i punteggi Lighthouse calano.
- Non fare: testare le performance solo manualmente prima del lancio, poi guardarle erodere in silenzio a ogni feature.
Mantieni lo stato il più locale possibile
Tieni lo stato al livello più basso che ne ha bisogno e solleva o centralizza solo ciò che deve essere condiviso, distinguendo lo stato del server (dati prelevati che necessitano caching e invalidazione) dallo stato di interfaccia client. Centralizzare tutto in uno store globale troppo presto aggiunge boilerplate, accoppiamento e re-render inutili.
- Fai: usa una libreria di stato server (es. TanStack Query) per i dati API e lo stato di componente per l'interfaccia locale.
- Non fare: scaricare ogni toggle transitorio e campo di form in un unico store globale "per sicurezza".
Fornisci nomi e label accessibili per ogni controllo
Assicurati che ogni elemento interattivo esponga un nome accessibile chiaro - tramite una visibile, aria-label o aria-labelledby - così che chi usa lo screen reader sappia cosa fa, e associa i campi dei form alle loro label. I button con sola icona hanno particolarmente bisogno di un nome accessibile.
- Fai:
e. - Non fare: spedire un button con sola icona o un input senza label che viene annunciato come "pulsante" o "campo di testo" senza scopo.