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

VersioneCome si legge un tokenEsempio
Tailwind 3theme()w-[calc(100%-theme(spacing.4))]
Tailwind 4variabile CSSw-[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 dvh a vh per 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.