È uscito il Corso Java Completo
Torna al blog

Cos'è Tailwind CSS e come cambia il modo di scrivere stili

Cos'è Tailwind CSS e perché usa classi di utilità: il problema del CSS che cresce, i token di design, le varianti e perché @apply è quasi sempre un errore.

Edoardo Midali

Edoardo Midali

Developer · Content Creator

10 min di lettura

Apri un file Tailwind per la prima volta e la reazione è quasi sempre la stessa: un div con dodici classi attaccate, l'HTML che sembra sporco, e la sensazione che qualcuno abbia buttato via vent'anni di buone pratiche. In questo articolo trovi perché quel compromesso è consapevole e quale problema reale risolve, cosa sono i token di design, perché @apply è la trappola di chi arriva dal CSS classico, e quando Tailwind non ti serve.

Cos'è Tailwind CSS

Tailwind è una raccolta di classi CSS minuscole, ognuna delle quali fa una cosa sola, che componi direttamente nell'HTML invece di scrivere regole in un foglio di stile separato.

p-4 mette il padding, flex imposta il display, text-red-500 colora il testo. Nessuna di queste classi sa cosa stai costruendo: è tutta la differenza rispetto a una classe come .card.

<!-- CSS classico -->
<div class="card">...</div>

<!-- Tailwind -->
<div class="rounded-lg border border-gray-200 bg-white p-6 shadow-sm">...</div>

A prima vista la seconda versione sembra peggio. Il motivo per cui in molti la preferiscono non riguarda la scrittura: riguarda cosa succede sei mesi dopo.

Il problema vero: il CSS che cresce e non si può togliere

Il CSS ha una caratteristica scomoda: è globale e non ha modo di dirti chi lo sta usando.

Prendi un foglio di stile di quattromila righe, su un progetto di due anni. Trovi questa regola:

.product-card-header-title--compact {
  margin-bottom: 12px;
  font-weight: 600;
}

La puoi cancellare? Nessuno lo sa. Dovresti cercare quella stringa in tutti i template, i componenti, le email, ogni contenuto che potrebbe generarla dinamicamente. E anche trovandola in un punto solo, non sai se qualcos'altro ci fa affidamento per ereditarietà.

Quindi non la cancelli. E nessuno cancella mai niente. Il CSS di un progetto cresce sempre e non cala mai, accumulando strati di regole morte che nessuno osa toccare.

Il secondo problema è dare i nomi. BEM, SMACSS e le convenzioni interne sono tentativi seri di risolvere una questione senza soluzione pulita. Quanto tempo passi a decidere se quella cosa è un card-header, un card-top o un card-title-wrapper? È lavoro che non produce nulla per chi usa il sito.

Tailwind risolve entrambi eliminando il livello intermedio. Le classi non hanno nome perché non descrivono cosa sono le cose: descrivono cosa fanno. E il CSS non cresce, perché il set di utilità è finito: la centesima pagina riusa le classi già presenti. Se cancelli un componente, il suo stile sparisce con lui — sta tutto lì dentro.

Il senso dello scambio si coglie meglio sapendo come il browser applica le regole, argomento di cos'è il DOM.

L'HTML "sporco" è un compromesso, non pigrizia

Questa è la critica principale, ed è legittima. Vale la pena affrontarla sul serio invece di liquidarla.

Chi non lo sopporta ha delle ragioni: l'HTML diventa difficile da leggere a colpo d'occhio, la separazione tra struttura e presentazione — un principio insegnato per vent'anni — salta, e stai imparando una sintassi proprietaria invece del CSS vero. Non sono obiezioni sciocche: chi le fa ha in genere lavorato su CSS complesso.

La risposta di chi lo usa è che la separazione tra HTML e CSS era in gran parte un'illusione. Se cambi il CSS di .card, il componente cambia aspetto: erano già accoppiati, solo in due file diversi. Quella divisione separava file, non responsabilità.

Con i componenti — React, Vue, Svelte — l'unità di riuso non è più la classe CSS: è il componente. E se l'unità è il componente, avere lo stile dentro di esso è coerente, non caotico. Il ragionamento è quello descritto in cos'è React.

Il compromesso, detto onestamente: peggiora la leggibilità di una riga di HTML per migliorare la manutenibilità dell'intero progetto. Chi lavora a lungo termine con più persone di solito considera lo scambio conveniente. Chi scrive pagine piccole e curate spesso no — ed è una posizione difendibile.

I token di design: il beneficio più sottovalutato

Questa parte si nota poco all'inizio e diventa la ragione principale per cui molti non tornano indietro.

Quando scrivi CSS a mano, ogni valore è libero. Nascono spaziature da 12, 13, 14, 15 e 16 pixel nella stessa pagina, tre grigi leggermente diversi perché nessuno ha controllato quale fosse quello giusto, cinque dimensioni di font ravvicinate. Nessuna scelta è sbagliata da sola: l'insieme risulta impreciso senza che si capisca perché.

Tailwind non ti dà valori liberi: ti dà una scala.

<!-- p-3 = 0.75rem, p-4 = 1rem, p-6 = 1.5rem -->
<div class="p-4 text-gray-700">...</div>

Non c'è un p-4.5. E il vincolo è il valore: le micro-decisioni scompaiono. Non ti chiedi se mettere 14 o 16 pixel, prendi il gradino della scala. Pagine scritte in momenti diversi, da persone diverse, vengono coerenti senza che nessuno abbia dovuto controllare.

La scala si personalizza, ed è la prima cosa da fare in un progetto vero: ci metti i colori del tuo marchio, i tuoi font, i tuoi raggi di bordo. Da quel momento Tailwind diventa il tuo design system, espresso in modo che sia difficile uscirne per sbaglio.

Le classi con valore arbitrario (p-[13px], text-[#3a5f2b]) servono per i casi eccezionali. Se ne trovi molte, non è Tailwind a essere limitato: è la tua scala che non riflette il design. Il posto dove intervenire è la configurazione, non il singolo componente.

Varianti responsive e di stato

La seconda idea forte è che la stessa classe può essere condizionata da un prefisso.

<button class="bg-blue-600 px-4 py-2 hover:bg-blue-700 focus:ring-2 disabled:opacity-50">
  Invia
</button>

<div class="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3">...</div>
PrefissoQuando si applica
md: lg: xl:Da quella larghezza di schermo in su
hover: focus:Interazione con il mouse o la tastiera
disabled:Elemento disabilitato
dark:Tema scuro attivo
group-hover:Passaggio del mouse su un elemento genitore
peer-checked:Un elemento fratello è selezionato

I breakpoint partono dal piccolo: grid-cols-1 vale sempre, md:grid-cols-2 si attiva dalla larghezza media in su. È l'approccio mobile-first, e scriverlo al contrario è l'errore più frequente di chi inizia. Il ragionamento completo è in cos'è il responsive design.

Il vantaggio concreto è che lo stato di un elemento si legge tutto nello stesso punto. Nel CSS classico l'hover sta trenta righe più giù e la versione per tablet in una media query in fondo al file: ricostruire il comportamento significa saltare avanti e indietro.

La ripetizione si risolve con i componenti, non con @apply

Arriva il momento in cui hai scritto le stesse dodici classi su otto bottoni. La reazione istintiva di chi viene dal CSS classico è cercare un modo per dargli un nome. Tailwind lo offre, e si chiama @apply:

/* Sembra la soluzione ovvia. Non lo è. */
.btn-primary {
  @apply rounded-lg bg-blue-600 px-4 py-2 font-medium text-white hover:bg-blue-700;
}

Così ti sei ricreato esattamente il problema da cui Tailwind ti stava tirando fuori. Hai di nuovo un nome da inventare, un file CSS che cresce e una classe globale che nessuno saprà se può cancellare: lo svantaggio di entrambi gli approcci.

La risposta giusta è estrarre un componente, che è l'unità di riuso che già usi per tutto il resto:

function Bottone({ variante = "primario", children, ...props }) {
  const base = "rounded-lg px-4 py-2 font-medium transition";
  const varianti = {
    primario: "bg-blue-600 text-white hover:bg-blue-700",
    secondario: "bg-gray-100 text-gray-900 hover:bg-gray-200",
  };

  return (
    <button className={`${base} ${varianti[variante]}`} {...props}>
      {children}
    </button>
  );
}

Ora la ripetizione è sparita davvero: c'è un posto solo da modificare, il componente si porta dietro anche il comportamento, e se lo cancelli non resta nulla in giro. È la differenza tra nascondere la duplicazione e rimuoverla, il cuore di cos'è il refactoring.

Quando @apply ha senso davvero: in un progetto senza componenti, dove non hai altro modo di riusare, oppure per stili di base su elementi che non controlli, come il contenuto HTML generato da un editor. Casi limitati, non la strategia principale.

Cosa fa perdere ore

  • Costruire nomi di classe concatenando stringhe. text-${colore}-500 non funziona: lo strumento cerca le classi complete nel sorgente, e una classe che esiste solo dopo l'esecuzione non viene generata. Vanno scritte per intero dentro un oggetto di ricerca.
  • Classi in conflitto che si annullano. px-4 px-6 dà un risultato che dipende dall'ordine nel CSS generato, non da quello in cui le hai scritte. Capita quando un componente riceve classi dall'esterno: serve una utility che le unisca risolvendo i conflitti.
  • Aspettarsi che le classi arbitrarie usino i tuoi colori. bg-[#fff] funziona ma scavalca la scala: è un valore fuori sistema che nessuno ritroverà.
  • Combattere gli stili di terze parti. Il reset di Tailwind azzera i margini predefiniti: il contenuto che arriva da un CMS diventa un blocco indistinto. Serve il plugin per la tipografia.
  • Preoccuparsi delle dimensioni del CSS finale. Timore infondato: solo le classi presenti nel codice finiscono nel file prodotto, che resta di pochi kilobyte. Come velocizzare un sito indica i colli di bottiglia veri, che sono altri.

Quando Tailwind non serve

Onestà, come sempre.

Su un sito piccolo non guadagni niente. Una landing page, un portfolio di tre pagine, un blog statico: cento righe di CSS a mano sono più semplici da leggere, non richiedono build, e il problema del CSS fuori controllo a quella scala non esiste.

Se il tuo team ha già un design system CSS che funziona, con convenzioni condivise e componenti collaudati, sostituirlo costa moltissimo per un beneficio incerto: risolveresti un problema già risolto.

Se non conosci il CSS, prima impara il CSS. Tailwind è una scorciatoia sopra flexbox, grid, il box model e la specificità: senza quelle basi aggiungi classi a caso finché l'aspetto non torna, e quando si rompe non sai perché.

Dove rende di più: applicazioni con molte schermate, progetti di lungo periodo, squadre in cui più persone toccano la stessa interfaccia. Contesti in cui il costo dell'HTML verboso è basso e il beneficio della coerenza è alto.

In sintesi

Tailwind sostituisce le classi semantiche con classi di utilità composte direttamente nell'HTML.

Il problema che risolve non è scrivere CSS più in fretta: è il CSS che cresce senza limite, con regole che nessuno osa cancellare perché nessuno sa chi le usa.

L'HTML verboso è un compromesso consapevole: peggiora la leggibilità di una riga per migliorare la manutenibilità del progetto. Chi lo critica per la separazione persa tra struttura e presentazione ha argomenti validi.

Il beneficio più sottovalutato sono i token di design: una scala fissa di spaziature, colori e dimensioni che rende il progetto coerente senza che nessuno debba controllare.

Contro la ripetizione si estrae un componente, non si usa @apply — che ricrea esattamente il problema di partenza.

E non serve sempre: su un sito piccolo, o dove esiste già un design system funzionante, il CSS scritto a mano è la scelta migliore.

Per il contesto in cui Tailwind dà il meglio, vedi cos'è React e cos'è Next.js. Per lo strumento di build che quasi certamente ci userai insieme, vedi cos'è Vite.