Introduzione
Questo articolo esplora i principali approcci per stilizzare componenti React: Vanilla CSS, stili inline, CSS Modules e Tailwind CSS. Ogni approccio funziona in modo diverso e ha caratteristiche specifiche.
Vanilla CSS
Il modo più semplice per stilizzare un’applicazione React è utilizzare CSS tradizionale, senza pacchetti o funzioni speciali aggiuntive. Si tratta di scrivere codice CSS standard e importarlo nei file JavaScript.
Come Funziona
In un progetto React tipico, si può creare un file CSS e importarlo direttamente nel componente o nel file principale dell’applicazione:
import './Header.css';
function Header() { return ( <header> <h1>Titolo</h1> <p>Descrizione</p> </header> );}header { display: flex; flex-direction: column; align-items: center; margin-top: 2rem;}
header h1 { font-size: 2rem; color: #f59e0b;}Lo strumento di compilazione (come Vite) identifica queste importazioni e inietta dinamicamente il CSS nella sezione <head> del documento HTML. Si possono creare più file CSS e importarli dove servono, organizzandoli per componente o per funzionalità.
Il Problema dello Scope
Quando si importa un file CSS in un componente, le regole CSS non sono automaticamente limitate a quel componente. Tutti gli stili vengono iniettati globalmente nella pagina. Se si definisce una regola per p in Header.css, questa influenzerà tutti i paragrafi della pagina, non solo quelli nell’header. Per risolvere questo problema, si possono usare selettori più specifici o passare a soluzioni con scope, come CSS Modules.
Stili Inline
Un’altra opzione per applicare stili è utilizzare gli stili inline direttamente nel codice JSX. In React, il prop style non accetta una stringa come nell’HTML tradizionale, ma un oggetto JavaScript.
Sintassi degli Stili Inline
function Component() { return ( <p style={{ color: 'red', textAlign: 'left' }}> Testo stilizzato </p> );}Le parentesi graffe esterne indicano che si sta passando un valore dinamico (non una stringa), mentre quelle interne creano un oggetto JavaScript.
Le proprietà CSS con trattini devono essere convertite in camelCase:
// ❌ Non funziona<p style={{ text-align: 'center' }}>Testo</p>
// ✅ Funziona<p style={{ textAlign: 'center' }}>Testo</p>Stili Dinamici e Condizionali
Gli stili inline sono particolarmente utili quando si devono applicare stili in modo dinamico o condizionale:
function Input({ isValid }) { return ( <input style={{ backgroundColor: isValid ? '#f3f4f6' : '#fee2e2', borderColor: isValid ? 'transparent' : '#ef4444', color: isValid ? '#1f2937' : '#dc2626' }} /> );}Si può usare un’espressione ternaria per impostare valori diversi in base a una condizione:
function Component() { const [highlighted, setHighlighted] = React.useState(false);
return ( <div> <p style={{ color: highlighted ? 'red' : 'white' }}> Testo dinamico </p> <button onClick={() => setHighlighted(!highlighted)}> Toggle </button> </div> );}Gli stili inline hanno scope limitato (effetto solo sull’elemento) e sono utili per stili dinamici condizionali. Non si possono usare media query o pseudo-selettori complessi.
Stili Condizionali con Classi CSS
Un approccio comune è combinare CSS tradizionale con classi applicate condizionalmente. In questo modo si mantiene la separazione tra CSS e JSX, ma si applicano stili diversi in base allo stato del componente.
Applicare Classi Condizionalmente
function Input({ email, isValid }) { return ( <input className={isValid ? 'input' : 'input invalid'} value={email} /> );}Quando si aggiunge una classe condizionalmente, si usa un’espressione ternaria con undefined invece di &&, che potrebbe generare un avviso:
// ❌ Può generare un avviso<input className={!isValid && 'invalid'} />
// ✅ Corretto<input className={!isValid ? 'invalid' : undefined} />Combinare Classi Fisse e Condizionali
Si possono combinare classi sempre applicate con classi condizionali usando template literals:
function Label({ text, isValid }) { return ( <label className={`label ${isValid ? '' : 'invalid'}`}> {text} </label> );}In modo più pulito:
function Label({ text, isValid }) { const baseClasses = 'label'; const conditionalClasses = isValid ? '' : 'invalid';
return ( <label className={`${baseClasses} ${conditionalClasses}`.trim()}> {text} </label> );}CSS Modules
CSS Modules è una soluzione che permette di scrivere CSS Vanilla ma con scope limitato ai componenti. Lo strumento di compilazione trasforma automaticamente i nomi delle classi CSS in nomi univoci, garantendo che gli stili non entrino in conflitto con altri componenti.
Come Funziona
Per utilizzare CSS Modules, è sufficiente rinominare il file CSS aggiungendo .module prima dell’estensione:
import classes from './Header.module.css';
function Header() { return ( <header> <p className={classes.paragraph}>Testo</p> </header> );}.paragraph { text-align: center; color: gray;}Lo strumento di compilazione trasforma automaticamente .paragraph in un nome univoco come Header_paragraph__abc123, garantendo che questa regola CSS influenzi solo gli elementi nel componente Header.
Classi Condizionali con CSS Modules
Si possono ancora applicare classi condizionalmente con CSS Modules:
function Input({ isValid }) { return ( <input className={isValid ? classes.input : classes.inputInvalid} /> );}Oppure combinare classi:
function Input({ isValid }) { return ( <input className={`${classes.input} ${isValid ? '' : classes.invalid}`.trim()} /> );}CSS Modules garantisce scope limitato: le classi CSS sono automaticamente limitate al componente che le importa. Si scrive ancora CSS Vanilla standard, mantenendo la separazione tra CSS e JSX.
Tailwind CSS
Tailwind CSS è un framework CSS utility-first che permette di stilizzare applicazioni aggiungendo classi predefinite agli elementi HTML. Non è specifico per React, ma funziona molto bene con esso.
Installazione
npm install -D tailwindcss postcss autoprefixernpx tailwindcss init -pPoi si configura il file tailwind.config.js:
/** @type {import('tailwindcss').Config} */export default { content: [ "./index.html", "./src/**/*.{js,ts,jsx,tsx}", ], theme: { extend: {}, }, plugins: [],}E si aggiungono le direttive Tailwind nel file CSS principale:
@tailwind base;@tailwind components;@tailwind utilities;Utilizzo Base
Tailwind funziona aggiungendo classi di utilità agli elementi:
function Header() { return ( <header className="flex flex-col items-center mt-8 mb-16"> <img className="w-44 h-44 object-contain mb-8" src="logo.png" alt="Logo" /> <h1 className="text-4xl font-semibold tracking-wider text-center uppercase text-amber-800"> Titolo </h1> <p className="text-stone-500">Descrizione</p> </header> );}Ogni classe corrisponde a una proprietà CSS: flex = display: flex, flex-col = flex-direction: column, items-center = align-items: center, mt-8 = margin-top: 2rem, text-4xl = font-size: 2.25rem, text-amber-800 = color: #92400e.
Responsive Design
Tailwind usa prefissi per applicare stili solo su determinate dimensioni di schermo:
function Header() { return ( <header className="mb-8 md:mb-16"> <h1 className="text-xl md:text-4xl">Titolo</h1> </header> );}mb-8 = margine inferiore di 2rem (sempre), md:mb-16 = margine inferiore di 4rem (solo su schermi medi e più grandi), text-xl = testo grande (sempre), md:text-4xl = testo molto grande (solo su schermi medi e più grandi).
Pseudo-selettori
Si possono usare prefissi per stati come hover, focus, active:
function Button() { return ( <button className="px-4 py-2 bg-amber-400 text-stone-900 rounded hover:bg-amber-500"> Clicca </button> );}Stili Condizionali
Con Tailwind, gli stili condizionali si gestiscono costruendo dinamicamente le stringhe delle classi:
function Input({ invalid, ...props }) { // Classi base che non cambiano const baseClasses = 'w-full px-2 py-1 rounded border';
// Classi condizionali const conditionalClasses = invalid ? 'bg-red-100 border-red-300 text-red-500' : 'bg-stone-100 border-transparent text-stone-900';
const inputClasses = `${baseClasses} ${conditionalClasses}`;
return ( <input className={inputClasses} {...props} /> );}Oppure, in modo più conciso:
function Input({ invalid, ...props }) { return ( <input className={` w-full px-2 py-1 rounded border ${invalid ? 'bg-red-100 border-red-300 text-red-500' : 'bg-stone-100 border-transparent text-stone-900' } `} {...props} /> );}Personalizzazione
Tailwind è altamente personalizzabile. Si possono aggiungere colori personalizzati, font, e altre utilità nel file di configurazione:
export default { theme: { extend: { fontFamily: { 'title': ['"Font Name"', 'sans-serif'], }, colors: { 'custom': '#ff0000', }, }, },}E si possono ancora aggiungere regole CSS personalizzate nel file CSS principale:
@tailwind base;@tailwind components;@tailwind utilities;
/* Regole personalizzate */body { background-image: url('./background.jpg');}Componenti Riutilizzabili con Tailwind
È buona pratica creare componenti riutilizzabili che incapsulano le classi Tailwind:
function Button({ children, variant = 'primary', ...props }) { const baseClasses = 'px-4 py-2 rounded font-semibold uppercase';
const variantClasses = { primary: 'bg-amber-400 text-stone-900 hover:bg-amber-500', secondary: 'bg-stone-400 text-white hover:bg-stone-500', };
return ( <button className={`${baseClasses} ${variantClasses[variant]}`} {...props} > {children} </button> );}
export default Button;Tailwind permette di creare interfacce senza conoscere CSS approfonditamente. Il build process rimuove le classi non utilizzate. Le classi sono nel codice JSX, quindi alcuni elementi possono avere elenchi lunghi di classi.
Best Practices
Sempre creare componenti riutilizzabili che incapsulano gli stili:
// ✅ Buono: componente riutilizzabilefunction Button({ children, variant, ...props }) { return ( <button className={`btn btn-${variant}`} {...props}> {children} </button> );}
// ❌ Evitare: ripetere classi/stili ovunquefunction Component1() { return <button className="px-4 py-2 bg-blue-500...">Click</button>;}
function Component2() { return <button className="px-4 py-2 bg-blue-500...">Click</button>;}Scegliere un approccio e mantenerlo consistente in tutto il progetto. Con Tailwind, il build process rimuove le classi non utilizzate. Con CSS Modules, tutti gli stili vengono caricati ma sono limitati per scope. Con Vanilla CSS, tutti gli stili vengono caricati globalmente.
Riepilogo
Vanilla CSS: si importa un file CSS nel componente. Lo strumento di compilazione inietta il CSS nel <head>. Le regole CSS non sono limitate al componente e vengono iniettate globalmente.
Stili Inline: si usa il prop style con un oggetto JavaScript. Le proprietà CSS con trattini devono essere convertite in camelCase. Utili per stili dinamici condizionali.
CSS Modules: si rinomina il file CSS aggiungendo .module prima dell’estensione. Lo strumento di compilazione trasforma i nomi delle classi in nomi univoci, garantendo scope limitato automaticamente.
Tailwind CSS: framework utility-first che aggiunge classi predefinite agli elementi. Ogni classe corrisponde a una proprietà CSS specifica. Si usano prefissi per responsive design (md:, lg:) e pseudo-selettori (hover:, focus:). Il build process rimuove le classi non utilizzate.