# 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.

> Pagina: https://ui.eneawork.it/blog/tailwind-calc-variabili-css
> Markdown: https://ui.eneawork.it/blog/tailwind-calc-variabili-css.md

*2026-11-01*

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.

La domanda «How to use calc() in tailwind CSS?» vale 292 voti su Stack Overflow (letto il 31 ottobre 2026 con la Stack Exchange API): fonte. Il catalogo EneUI misura 111.985 componenti, 1.556 blocchi, 12.808 icone da 1.472 librerie, letti da /api/stats il 1 novembre 2026. Ogni comando qui sotto è eseguibile in locale: nessun passaggio richiede un account. I conteggi sono stati verificati con una chiamata diretta all'endpoint, non copiati da una bozza precedente.

## 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

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

## 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.

## FAQ

### 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.
