Errori e fix
Tailwind e calc(): variabili CSS, underscore e i casi che non funzionano
Nei valori arbitrari di Tailwind gli spazi separano le classi. Per questo `calc()` va scritto con gli underscore, e per questo alcune espressioni restano impossibili.
In sintesi
- Gli spazi dentro un valore arbitrario vanno sostituiti con `_`: `h-[calc(100dvh_-_var(--header-h))]`.
- I token del tema si leggono con `theme()` in v3 e con variabili CSS in v4: non mischiarli nello stesso calcolo.
- Se l'espressione è lunga o riusata, una classe CSS normale è più leggibile e più veloce da debuggare.
Risposta breve: dentro un valore arbitrario di Tailwind lo spazio separa una classe dalla successiva, quindi calc() va scritto con gli underscore al posto degli spazi. h-[calc(100dvh_-_var(--header-h))] è la forma corretta.
Il meccanismo: lo spazio chiude il token
Tailwind legge l'attributo class e lo divide sugli spazi prima di interpretare qualsiasi cosa. Le parentesi quadre non proteggono il contenuto: il parser prende tutto fino al primo spazio e tratta il resto come un'altra classe.
Nel caso di h-[calc(100vh - 4rem)] il candidato diventa h-[calc(100vh, che non corrisponde a nessun utility, e - più 4rem)] vengono scartati. Non c'è errore, non c'è avviso: la dichiarazione semplicemente non viene generata.
L'underscore è il carattere di fuga: viene convertito in spazio quando Tailwind scrive la regola. Un underscore letterale si scrive con la barra rovesciata, \_. La regola vale in v3 e in v4.
Diagnosi: guarda il foglio generato
Il browser è l'ultimo anello della catena e non sa cosa hai scritto. Genera il CSS una volta e cerca la classe: se la regola non esiste, il problema è nella sintassi; se esiste ma la proprietà manca, il problema è nella variabile.
npx @tailwindcss/cli -i src/app.css -o out.css --content "./src/**/*.tsx"
grep -o "\.h-\\[calc[^{]*" out.css | head
Quando la regola compare ma il valore non si applica, la causa tipica è una var() non definita: la dichiarazione diventa invalida e la proprietà ricade sul valore iniziale, che per height è auto. Il pannello Computed dello strumento di sviluppo mostra la dichiarazione barrata, ed è la prova che la sintassi era giusta.
Con una variabile CSS
:root {
--header-h: 4rem;
}
...
100dvh batte 100vh sul mobile: l'altezza dinamica segue la barra degli indirizzi e smette di tagliare il contenuto quando il browser cambia dimensione. La variabile deve stare su un antenato, non su un fratello.
Token del tema: v3 e v4 non si mescolano
| Versione | Come si legge un token | Esempio |
|---|---|---|
| Tailwind 3 | theme() | w-[calc(100%-theme(spacing.4))] |
| Tailwind 4 | variabile CSS | w-[calc(100%-var(--spacing-4))] |
In Tailwind 4 i token del tema sono variabili CSS. theme() resta per compatibilità, ma la forma nativa è var(). Mischiare le due dentro lo stesso calcolo produce un valore che il browser non riesce a risolvere, e il sintomo è di nuovo un'altezza sbagliata senza errori.
Quando conviene una classe CSS
Se il calcolo appare in tre punti o cambia con il layout, una classe semantica è più leggibile e più facile da ispezionare:
.app-shell {
min-height: calc(100dvh - var(--header-h));
}
La classe arbitraria resta utile per un caso singolo. Non è un sostituto del CSS quando l'espressione fa parte del layout e la leggono altre persone.
Quando la correzione comune peggiora le cose
La prima reazione è aggiungere !important o alzare la specificità con un selettore composto. Non serve: se il candidato non è stato interpretato, non esiste nessuna regola da rendere importante, e nel frattempo la nuova regola vince sulle varianti responsive che volevi usare.
La seconda è spostare il calcolo in un attributo style inline. Funziona, ma perdi ogni variante, md:, dark:, motion-safe:, e il valore esce dal tema: due componenti con lo stesso calcolo tornano a divergere alla prima modifica.
Un limite da mettere in conto: le proprietà non registrate
Una custom property non registrata è una stringa opaca: il browser non sa che contiene una lunghezza, quindi non può interpolarla. Un'animazione su --header-h salta invece di scorrere finché non dichiari @property con syntax: "<length>" e un valore iniziale. Il secondo limite è che calc() non valida le unità: sommare rem e px va bene, sommare un numero e una lunghezza no.
Prevenzione
- Definisci le misure del layout, header, sidebar, footer, come variabili in un solo posto.
- Preferisci
dvhavhper gli shell a tutta altezza. - Aggiungi un commento accanto alla classe arbitraria: chi la legge dopo deve capire perché c'è.
- Tieni una sola versione di Tailwind per progetto: i token cambiano forma fra v3 e v4.
Dove continuare
Nel catalogo: blocco pricing, componenti e icone. Le guide: Flexbox e Grid per designer e dark mode senza secondo design system; il confronto: copy-paste o headless.
Domande frequenti
Perché `h-[calc(100vh - 4rem)]` non funziona?
Lo spazio chiude il valore arbitrario: Tailwind legge `calc(100vh` e scarta il resto. Scrivi `h-[calc(100vh_-_4rem)]`, con un underscore per ogni spazio.
Le variabili CSS funzionano dentro calc()?
Sì, e la variabile deve essere definita su un antenato dell'elemento. Se manca, la dichiarazione diventa invalida e la proprietà ricade sul valore iniziale.
Posso usare `theme()` dentro calc()?
In Tailwind 3 sì, per i token del tema. In Tailwind 4 i token sono variabili CSS, quindi usa `var(--spacing-4)` invece di `theme(spacing.4)` e non mescolare le due forme.
Meglio una classe CSS dedicata?
Se il calcolo compare in tre punti o cambia con il layout, sì. Una classe come `.app-shell` si legge e si ispeziona meglio della stringa arbitraria.
Funziona anche per `min()`, `max()` e `clamp()`?
Sì, con la stessa regola degli underscore. `text-[clamp(1rem,2vw,1.5rem)]` non contiene spazi e quindi non richiede modifiche.