Migliori Pratiche HTML
Un HTML solido è la base di ogni grande sito web. Questi consigli ti aiutano a scrivere markup semantico, robusto e a prova di futuro che browser e screen reader apprezzano.
Impostare gli attributi espliciti width e height sulle immagini consente al browser di riservare lo spazio corretto prima che l'immagine venga caricata, eliminando il Cumulative Layout Shift (CLS) — una delle metriche Core Web Vitals di Google.
<!-- ❌ Cattivo: il browser non conosce la dimensione finché l'immagine non viene caricata --> <img src="hero.jpg" alt="Immagine hero"> <!-- ✅ Buono: lo spazio viene riservato immediatamente --> <img src="hero.jpg" alt="Immagine hero" width="1200" height="630" loading="lazy">
aspect-ratio: auto nei CSS e il browser calcolerà automaticamente il rapporto corretto, anche quando i CSS ridimensionano l'immagine.Uno schema del documento costruito con intestazioni correttamente annidate aiuta sia i motori di ricerca a comprendere la gerarchia dei contenuti sia gli utenti di screen reader a navigare la pagina in modo efficiente.
<h1>Titolo della Pagina (uno per pagina)</h1> <h2>Sezione Principale</h2> <h3>Sottosezione</h3> <h3>Altra Sottosezione</h3> <h2>Altra Sezione Principale</h2>
Quando si aprono link in una nuova scheda con target="_blank", la pagina aperta può accedere alla tua pagina tramite window.opener. Aggiungere rel="noopener noreferrer" previene questa vulnerabilità di sicurezza e blocca anche l'invio delle informazioni sul referrer.
<!-- ❌ Vulnerabile --> <a href="https://esempio.com" target="_blank">Visita</a> <!-- ✅ Sicuro --> <a href="https://esempio.com" target="_blank" rel="noopener noreferrer">Visita</a>
L'attributo autocomplete indica a browser e gestori di password come precompilare i campi del form. Questo migliora drasticamente i tassi di completamento dei form su mobile.
<input type="text" autocomplete="given-name"> <input type="email" autocomplete="email"> <input type="tel" autocomplete="tel"> <input type="password" autocomplete="current-password"> <input type="text" autocomplete="postal-code">
I meta tag Open Graph controllano l'aspetto della tua pagina quando viene condivisa su piattaforme di social media come Facebook, LinkedIn e Twitter/X. Senza di essi, le piattaforme scelgono i contenuti in modo casuale.
<meta property="og:title" content="Titolo della Pagina"> <meta property="og:description" content="Breve descrizione"> <meta property="og:image" content="https://tuodominio.com/og.jpg"> <meta property="og:url" content="https://tuodominio.com/pagina"> <meta property="og:type" content="website"> <!-- Twitter/X --> <meta name="twitter:card" content="summary_large_image">
Consigli e Trucchi CSS
I CSS moderni sono incredibilmente potenti. Questi suggerimenti coprono layout, proprietà personalizzate e pattern che ti fanno risparmiare tempo e mantengono i tuoi fogli di stile puliti.
Smetti di lottare con hack di posizionamento. La scorciatoia place-items di CSS Grid centra il contenuto sia orizzontalmente che verticalmente in due righe — niente calc, niente transform.
.centra-tutto { display: grid; place-items: center; } /* Funziona anche per centrare a tutta pagina */ body { display: grid; place-items: center; min-height: 100vh; }
Le proprietà personalizzate CSS (variabili) ti permettono di definire i tuoi token di design una volta e riutilizzarli ovunque. Inoltre si aggiornano in tempo reale, rendendo la modalità scura e la tematizzazione banali.
:root { --colore-primario: #6366f1; --colore-sfondo: #ffffff; --raggio-medio: 12px; --spazio-medio: 16px; } @media (prefers-color-scheme: dark) { :root { --colore-sfondo: #0d1117; } } .btn { background: var(--colore-primario); border-radius: var(--raggio-medio); padding: var(--spazio-medio); }
clamp(min, preferred, max) consente alle dimensioni dei caratteri di scalare fluidamente con la larghezza del viewport — senza bisogno di media query. Il valore centrale è solitamente un'unità vw.
/* clamp(minimo, preferito, massimo) */ h1 { font-size: clamp(1.8rem, 5vw, 3.5rem); } p { font-size: clamp(0.9rem, 2vw, 1.1rem); line-height: 1.7; max-width: 65ch; /* larghezza di lettura ottimale */ }
Abilita lo scorrimento fluido globalmente e usa scroll-margin-top sugli elementi di destinazione dell'ancora per garantire che le intestazioni fisse non si sovrappongano al contenuto verso cui si sta scorrendo.
@media (prefers-reduced-motion: no-preference) { html { scroll-behavior: smooth; } } /* Offset per intestazione fissa (es. alta 70px) */ [id] { scroll-margin-top: 90px; }
scroll-behavior: smooth in una media query prefers-reduced-motion: no-preference — alcuni utenti soffrono di chinetosi a causa dello scorrimento animato.Il pattern auto-fill + minmax() crea una griglia che regola automaticamente il numero di colonne in base allo spazio disponibile — senza bisogno di media query.
.griglia-schede { display: grid; grid-template-columns: repeat( auto-fill, minmax(280px, 1fr) ); gap: 24px; } /* 1 colonna su mobile → 2 → 3 → 4 automaticamente */
Non fare mai outline: none globalmente — rompe la navigazione da tastiera. Usa :focus-visible per mostrare gli anelli di focus solo per gli utenti da tastiera, mantenendo il design pulito per gli utenti mouse.
/* ❌ Non farlo mai */ * { outline: none; } /* ✅ Nascondi per il mouse, mostra per la tastiera */ :focus:not(:focus-visible) { outline: none; } :focus-visible { outline: 2px solid #6366f1; outline-offset: 3px; border-radius: 4px; }
Consigli JavaScript
Scrivi JavaScript più pulito e performante con questi pattern moderni e le API del browser.
Invece di ascoltare l'evento scroll (che viene attivato centinaia di volte al secondo), usa l'API IntersectionObserver per reagire solo quando gli elementi entrano o escono dal viewport.
const observer = new IntersectionObserver( (entries) => { entries.forEach(entry => { if (entry.isIntersecting) { entry.target.classList.add('visible'); observer.unobserve(entry.target); // smetti di osservare } }); }, { rootMargin: '0px 0px -50px 0px' } ); document.querySelectorAll('.animate-on-scroll') .forEach(el => observer.observe(el));
Eventi come resize e input vengono attivati molto rapidamente. Il debounce ritarda l'esecuzione fino a quando l'utente smette di interagire, prevenendo lavoro non necessario.
function debounce(fn, delay = 300) { let timer; return (...args) => { clearTimeout(timer); timer = setTimeout(() => fn(...args), delay); }; } // Utilizzo window.addEventListener('resize', debounce(() => { ricalcolaLayout(); }, 200) );
La moderna API navigator.clipboard è basata su promise ed è molto più pulita del vecchio hack document.execCommand('copy').
async function copiaNegliAppunti(text) { try { await navigator.clipboard.writeText(text); mostraNotifica('Copiato!'); } catch (err) { console.error('Copia fallita:', err); } } // Utilizzo document.querySelector('.pulsante-copia') .addEventListener('click', () => copiaNegliAppunti('Testo da copiare') );
localStorage può generare eccezioni in modalità di navigazione privata o quando lo spazio di archiviazione è pieno. Racchiudilo sempre in un helper try/catch.
const storage = { get(key, fallback = null) { try { const item = localStorage.getItem(key); return item ? JSON.parse(item) : fallback; } catch { return fallback; } }, set(key, value) { try { localStorage.setItem(key, JSON.stringify(value)); return true; } catch { return false; } } }; storage.set('tema', 'scuro'); const tema = storage.get('tema', 'chiaro');
Per le animazioni guidate da JavaScript (es. canvas, GSAP), controlla l'impostazione prefers-reduced-motion dell'utente e semplifica o disabilita le animazioni di conseguenza.
const preferisceRiduzione = window .matchMedia('(prefers-reduced-motion: reduce)') .matches; if (!preferisceRiduzione) { avviaAnimazioni(); } else { mostraFallbackStatico(); }
Consigli sulle Prestazioni
I siti web veloci si posizionano meglio, convertono di più e mantengono gli utenti felici. Questi suggerimenti coprono i maggiori vantaggi per le prestazioni web.
Senza font-display: swap, i browser nascondono il testo finché il font personalizzato non viene caricato (FOIT). Con esso, viene mostrato immediatamente il font di sistema, per poi essere sostituito — migliorando notevolmente il Largest Contentful Paint (LCP).
<!-- Step 1: preconnect --> <link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <!-- Step 2: aggiungi &display=swap all'URL --> <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;700&display=swap" rel="stylesheet">
Le immagini WebP e AVIF sono significativamente più piccole dei JPEG e PNG a parità di qualità. Usa l'elemento <picture> per servire formati moderni con un fallback JPEG per i browser più vecchi.
<picture> <source srcset="hero.avif" type="image/avif"> <source srcset="hero.webp" type="image/webp"> <img src="hero.jpg" alt="Immagine hero" width="1200" height="630" loading="lazy"> </picture>
I tag <script> normali bloccano l'analisi HTML. defer esegue lo script dopo che l'analisi è completata (in ordine). async esegue gli script non appena vengono scaricati (ordine non garantito).
<!-- Blocca il rendering ❌ --> <script src="app.js"></script> <!-- Eseguito in ordine, dopo il DOM ✅ (per la maggior parte degli script) --> <script src="app.js" defer></script> <!-- Eseguito subito quando pronto ✅ (analytics, ads) --> <script src="analytics.js" async></script>
Limita le animazioni a transform e opacity — vengono eseguite sul thread del compositore GPU senza attivare layout o paint. Aggiungi will-change solo se fai profiling e vedi un reale beneficio.
/* ❌ Attiva il layout (costoso) */ .cattivo { transition: width 0.3s, top 0.3s; } /* ✅ Composito dalla GPU (economico) */ .buono { transition: transform 0.3s, opacity 0.3s; } /* Usa con parsimonia, solo prima di animazioni complesse */ .modal-entra { will-change: transform; } .modal-entrata { will-change: auto; } /* ripristina dopo */
Usa <link rel="preload"> per indicare al browser di recuperare le risorse critiche in anticipo — prima che vengano scoperte nei CSS o JS. Ideale per immagini hero, font critici e script chiave.
<!-- Precarica immagine hero (elemento LCP) --> <link rel="preload" as="image" href="hero.webp" fetchpriority="high"> <!-- Precarica font critico --> <link rel="preload" as="font" href="font.woff2" type="font/woff2" crossorigin>
Consigli sull'Accessibilità
L'accessibilità non è una checklist — è un buon design. Questi suggerimenti aiutano a rendere i tuoi siti utilizzabili da tutti, inclusi gli utenti di screen reader e coloro che navigano con la tastiera.
I pulsanti che contengono solo un'icona non hanno testo visibile da annunciare per gli screen reader. Aggiungi un aria-label per fornire un'etichetta descrittiva.
<!-- ❌ Lo screen reader dice "pulsante" --> <button><i class="fa-solid fa-times"></i></button> <!-- ✅ Lo screen reader dice "Chiudi finestra" --> <button aria-label="Chiudi finestra"> <i class="fa-solid fa-times" aria-hidden="true"></i> </button>
L'attributo lang indica agli screen reader quale lingua utilizzare, abilita la sillabazione corretta nei CSS e aiuta gli strumenti di traduzione a identificare la lingua del contenuto. Per le lingue RTL, aggiungi anche dir="rtl".
<!-- Italiano --> <html lang="it"> <!-- Arabo (RTL) --> <html lang="ar" dir="rtl"> <!-- Francese --> <html lang="fr">
WCAG 2.1 AA richiede un rapporto di contrasto di almeno 4,5:1 per il testo normale e 3:1 per il testo grande (18px+ grassetto o 24px+ normale). Usa un controllore di contrasto prima di pubblicare.
/* ❌ #6b7280 su #fff = 4,48:1 (non supera AA per testo piccolo) */ .attenuato { color: #6b7280; background: #fff; } /* ✅ #4b5563 su #fff = 7,0:1 (supera AA e AAA) */ .attenuato { color: #4b5563; background: #fff; }
A volte hai bisogno di testo che solo gli screen reader possono ascoltare — come un link "salta al contenuto" o un'etichetta per un elemento visivo. La classe utility .sr-only lo nasconde visivamente mantenendolo accessibile.
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0; } <!-- Esempio di link salta contenuto --> <a href="#main" class="sr-only">Salta al contenuto</a>
Quando una modale è aperta, il focus della tastiera deve essere confinato al suo interno. Altrimenti, premendo Tab si focalizzeranno elementi dietro l'overlay — rendendo la modale inutilizzabile per gli utenti da tastiera.
function intrappolaFocus(modal) { const focusabili = modal.querySelectorAll( 'a, button, input, select, textarea, [tabindex]:not([tabindex="-1"])' ); const primo = focusabili[0]; const ultimo = focusabili[focusabili.length - 1]; modal.addEventListener('keydown', e => { if (e.key !== 'Tab') return; if (e.shiftKey ? document.activeElement === primo : document.activeElement === ultimo) { e.preventDefault(); (e.shiftKey ? ultimo : primo).focus(); } }); primo.focus(); }
Consigli Responsive e Mobile
Il traffico mobile è dominante. Questi suggerimenti assicurano che i tuoi design funzionino perfettamente su ogni dimensione dello schermo — da un telefono da 320px a un monitor 4K.
Senza il meta tag viewport, i browser mobile eseguono il rendering a ~980px di larghezza desktop e poi riducono lo zoom. Consenti lo zoom all'utente — disabilitarlo è una grave barriera all'accessibilità e rompe lo zoom del browser.
<!-- ❌ Disabilita lo zoom utente (violazione accessibilità) --> <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no"> <!-- ✅ Corretto: consente lo zoom fino a 5× --> <meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=5">
Le linee guida HIG di Apple e WCAG 2.5.5 raccomandano un'area di tocco minima di 44×44 pixel CSS. Obiettivi piccoli frustrano gli utenti e portano a tocchi errati. Puoi espandere l'area cliccabile senza modificare la dimensione visiva usando padding o pseudo-elementi.
/* Espandi area di tocco senza modifiche visive */ .pulsante-icona { position: relative; width: 24px; height: 24px; } .pulsante-icona::before { content: ''; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); min-width: 44px; min-height: 44px; }
Le proprietà logiche CSS come margin-inline-start invece di margin-left si invertono automaticamente in base alla modalità di scrittura del documento — rendendo il supporto RTL quasi senza sforzo.
/* Fisico (non si inverte per RTL) */ .icona { margin-right: 8px; } /* Logico (si inverte automaticamente in RTL) */ .icona { margin-inline-end: 8px; } /* Altri esempi di proprietà logiche */ .scheda { padding-inline: 16px; /* sinistra + destra */ padding-block: 12px; /* alto + basso */ border-start-start-radius: 12px; /* in alto a sinistra in LTR */ }
100vh sui browser mobile include l'interfaccia utente del browser (barra degli indirizzi), causando overflow quando la barra appare. La nuova unità dvh (altezza viewport dinamica) si regola quando l'interfaccia del browser appare o scompare.
/* ❌ Overflow su mobile quando appare l'interfaccia del browser */ .hero { height: 100vh; } /* ✅ Con fallback per browser più vecchi */ .hero { height: 100vh; /* fallback */ height: 100dvh; /* altezza viewport dinamica */ }
svh (viewport piccolo, esclude sempre l'interfaccia) e lvh (viewport grande, include sempre lo spazio dell'interfaccia).Altro da Test Sito Responsive
Metti in Pratica Questi Consigli
Verifica come appaiono le tue modifiche responsive su mobile, tablet e desktop — istantaneamente, gratuitamente, senza registrazione.
Testa il Tuo Sito Web Ora