Possiamo rendere il default di Tailwind una scelta più accessibile?
Qualche tempo fa, un progetto ha iniziato a rompersi in un modo che non riuscivo a riprodurre. Alcune sezioni saltavano al layout sbagliato. Colonne che dovevano essere affiancate si impilavano, le card traboccavano, tutto su larghezze del viewport che avevo testato una dozzina di volte. Il mio CSS era a posto. La mia dimensione del font root era il sensato default 16px. Alla fine l'ho trovato: chi aveva segnalato il bug aveva alzato la dimensione predefinita del font del browser, e i breakpoint di Tailwind si erano scalati silenziosamente. Il layout cambiava a un viewport più stretto di quanto avessi mai progettato.
E mi ha lasciato con qualcosa a cui non avevo mai pensato prima. Se un utente imposta la dimensione predefinita del font del browser su un valore diverso da 16px, questo sposterebbe silenziosamente i miei breakpoint di Tailwind?
Sembra che non dovrebbe avere importanza. I breakpoint riguardano la larghezza del viewport, la dimensione del font riguarda il testo. Due assi diversi. Tranne che sono silenziosamente collegati, e una volta capito come, inizi a chiederti se il default di Tailwind è quello che tu vuoi davvero. Lascia che ti spieghi. E sarò onesto, la prima volta che ci ho pensato ho sbagliato la risposta.
Prima: cosa Tailwind fornisce davvero
A partire da Tailwind v3.2, i breakpoint predefiniti sono in rem, e la v4 li ha mantenuti invariati:
sm 40rem /* 640px */ md 48rem /* 768px */ lg 64rem /* 1024px */ xl 80rem /* 1280px */ 2xl 96rem /* 1536px */
Quindi md:flex significa davvero @media (min-width: 48rem). I valori in pixel nei commenti sono semplicemente rem × 16. Quel × 16 è tutta la storia.
L'inghippo: il rem in una media query non è il rem che pensi
Il tuo istinto (e il mio) probabilmente dice: «il rem è relativo alla dimensione del font dell'elemento root, quindi se imposto html { font-size: 10px }, i miei breakpoint si spostano».
No. All'interno di una condizione di media query, rem ed em non considerano affatto il tuo CSS:
/* This does NOT change where `min-width: 48rem` triggers. */
html {
font-size: 10px;
}
@media (min-width: 48rem) {
/* still evaluated against the *initial* font size, */
/* not against your html { font-size } */
}
Secondo la specifica Media Queries Level 4, le unità relative nelle media query vengono risolte rispetto al valore iniziale di font-size. Per usare le sue parole: «Le unità relative nelle media query si basano sul valore iniziale, il che significa che le unità non si basano mai sui risultati delle dichiarazioni». Quindi il CSS della tua pagina non ha alcun potere qui.
Questa è la parte che tutti imparano per prima, ed è dove molti post di blog si fermano. Ma è solo metà del quadro, e l'altra metà è quella interessante.
La metà che nessuno menziona: l'utente può comunque spostarli
«Valore iniziale» non significa «16px fisso per sempre». Significa il valore da cui parte lo user agent, e l'utente può dire la sua:
- Impostazione della dimensione predefinita del font del browser (Chrome:
chrome://settings/fonts→ Dimensione del font.
Firefox:about:preferences→ Generale → Font → Dimensione predefinita). Questo modifica il valore iniziale dello UA. Quindi sposta i tuoi breakpointrem/em. - CSS dell'autore (
html { font-size }).
Non li sposta. (Questo è l'inghippo di cui sopra.)
Quindi ecco il mio modello mentale corretto. Tre diversi gesti per «ingrandire», tre comportamenti diversi:
| Cosa fa l'utente | Testo rem |
Breakpoint rem/em |
Breakpoint px |
|---|---|---|---|
| Imposta la dimensione predefinita del font del browser | scala | scala | congelati |
| Zoom pagina (Cmd / Ctrl +/−) | scala | scala | scala |
L'autore imposta html { font-size } |
scala | nessun effetto | n/a |
Rileggi quella colonna centrale. Con i breakpoint rem, un utente che alza la dimensione predefinita del font a 24px perché non riesce a leggere comodamente a 16px ottiene breakpoint che si muovono insieme a lui. Il tuo layout cambia per dare a quel testo più grande più spazio. Con i breakpoint px, il suo testo cresce ma le tue colonne rimangono congelate a 768px, comprimendo allegramente testo da 24px in un layout progettato per 16px. |
Questo è l'argomento a favore del default di Tailwind. È una vera vittoria in termini di accessibilità, ed è il motivo per cui i maintainer l'hanno scelto.
Allora perché nel titolo c'è «non sempre accessibile»?
Perché «buono per il testo dell'utente» e «buono per il tuo layout» non sono garantiti essere la stessa cosa, e la divergenza dipende dal design, non è universale. Quando i breakpoint si muovono con la dimensione del font, hai consegnato il controllo di quale layout viene renderizzato a un'impostazione che non puoi vedere e contro cui non hai fatto test. Ecco alcuni modi in cui questo morde:
- Hai testato a 16px. Il tuo
md:grid-cols-3era perfetto. Un utente con default a 20px inciampa nel layout a 3 colonne a un viewport più stretto di quelli che hai mai controllato, e le tue card traboccano. - Le librerie di componenti con breakpoint interni rigidi possono cambiare layout a metà scorrimento in modi che sembrano bug.
- È invisibile. Lo zoom della pagina puoi riprodurlo all'istante. L'impostazione della dimensione predefinita del font è nascosta, raramente modificata, e quasi mai nella tua matrice di test, quindi quando causa un problema di layout, è un incubo da riprodurre se non ne sei consapevole.
Niente di tutto questo significa che i breakpoint rem siano sbagliati. Significa che il default fa un compromesso che potresti non sapere di stare facendo: più reattivo alle preferenze del font dell'utente, meno prevedibile per te.
Il dibattito vero e proprio
Quindi quale dovresti davvero scegliere? Questa è la parte che vale la pena discutere.
I breakpoint px ti danno un layout che scatta esattamente alla larghezza per cui hai progettato, ogni volta, per tutti. Fanno affidamento sullo zoom della pagina (che scala tutto, inclusi i breakpoint px) per fare il lavoro pesante dell'accessibilità. È una posizione difendibile: lo zoom è il gesto che la maggior parte degli utenti usa davvero, e funziona e basta con i px.
I breakpoint rem/em onorano inoltre l'impostazione dimensione predefinita del font, un gruppo più piccolo ma reale di utenti (spesso quelli che ne hanno più bisogno) che imposta un font di base più grande invece di fare zoom. Anche questo è difendibile.
Quindi la vera domanda è «quale è corretto»? Probabilmente no. La domanda migliore è: per quale utente stai ottimizzando, e l'hai scelto tu o l'hai ereditato?
Hai scelto i breakpoint rem perché hai ragionato sugli utenti che usano lo zoom del testo, o perché era il default e non ci hai mai pensato? Il secondo caso è quello di cui vale la pena parlare. Un buon default è uno che avresti scelto comunque. Un default di accessibilità che non sai spiegare è solo un default.
Una nota su cosa richiede davvero lo standard
È allettante inquadrare la cosa come «i breakpoint px falliscono l'accessibilità», ma non è ciò che dice la specifica. WCAG 1.4.4 (Resize Text) è soddisfatta finché il testo può raggiungere il 200% tramite qualche meccanismo fornito dal browser, e il W3C elenca esplicitamente lo zoom della pagina come tecnica sufficiente. Dato che lo zoom scala senza problemi i breakpoint px, i breakpoint px soddisfano WCAG 1.4.4. Non falliscono lo standard.
Quindi onorare l'impostazione della dimensione predefinita del font del browser non è il minimo, è un passo avanti. Scegliere breakpoint rem per servire gli utenti che modificano quella impostazione (invece dello zoom) è una decisione di andare oltre ciò che WCAG richiede, non una decisione di semplice conformità. È un ottimo motivo per scegliere rem. Vale solo la pena essere onesti sul fatto che è una scelta per superare lo standard, non una casella che lo standard ti obbliga a spuntare.
Se vuoi cambiarlo
Passare ai breakpoint px in Tailwind v4 è un override di @theme:
/* app.css */
@import "tailwindcss";
@theme {
--breakpoint-sm: 640px;
--breakpoint-md: 768px;
--breakpoint-lg: 1024px;
--breakpoint-xl: 1280px;
--breakpoint-2xl: 1536px;
}
Oppure, nel tailwind.config.js della v3:
/** @type {import('tailwindcss').Config} */
module.exports = {
theme: {
screens: {
sm: "640px",
md: "768px",
lg: "1024px",
xl: "1280px",
"2xl": "1536px",
},
},
};
Potresti anche andare nella direzione opposta e spingerti oltre. Tieni i breakpoint rem e assicurati che il tuo contenuto sia interamente basato su rem e testato al 200% del testo, se onorare l'impostazione della dimensione del font è un obiettivo che scegli di proposito.
Il punto non è la risposta
Il punto è che «i default di Tailwind sono accessibili» sta facendo molto lavoro silenzioso in molti codebase, e vale la pena sapere quale accessibilità compra, quale no, e se corrisponde agli utenti che hai davvero.
Quindi: rem, em o px per i tuoi breakpoint? Penso sinceramente che persone ragionevoli arrivino a conclusioni diverse qui.
Vuoi vederlo con i tuoi occhi? C'è una piccola demo live accanto a questo post. Apri, cambia la dimensione predefinita del font del browser e guarda il breakpoint rem muoversi mentre quello px resta fermo.
Fonti
- Media Queries Level 4, §6 "Using Units" (W3C) — la regola della specifica secondo cui le unità relative nelle media query risolvono rispetto al
font-sizeiniziale, non al CSS dell'autore: «Le unità relative nelle media query si basano sul valore iniziale, il che significa che le unità non si basano mai sui risultati delle dichiarazioni». - Tailwind CSS: Responsive design — i breakpoint predefiniti ufficiali (
sm40rem,md48rem,lg64rem,xl80rem,2xl96rem) e la nota che Tailwind usaremper questi. - Note di rilascio di Tailwind CSS v3.2 — la release che ha portato i breakpoint predefiniti a
rem. - Tailwind CSS: Theme variables (
@theme) — come sovrascrivere i valori--breakpoint-*nella v4. - MDN:
font-sizee MDN: CSS values and units — riferimento sulla risoluzione direm/eme sulla dimensione del font root/iniziale. - Understanding WCAG SC 1.4.4: Resize Text (W3C / WAI) — il criterio di accessibilità alla base dell'argomento dei breakpoint
rem: il testo deve poter essere ridimensionato al 200% senza perdita di contenuto o funzionalità. Nota che non richiede di rimanere all'interno di un breakpoint, quindi i breakpointpxsoddisfatti tramite lo zoom della pagina non sono di per sé un fallimento.
Altri articoli recenti
Laravel AI 1.1 e 1.2: Agent Skills, Cohere e structured output più solidi
Le release 1.1.0 e 1.2.0 di laravel/ai portano gli Agent Skills, l'approvazione obbligatoria per i tool built-in e nuovi provider come Cohere e Laya self-hosted. Sul fronte qualità arrivano i fix allo structured output per Bedrock e il supporto alle provider options. Aggiornati anche i modelli di default, con Claude 4.7+ che ignora i sampling params.
Laravel 13.35.0: scheduler più controllabile, code più flessibili e response Markdown
Laravel 13.35.0 porta Schedule::alwaysOnOneServer(), il supporto a schedule:interrupt per i comandi di lunga durata, --memory in percentuale per queue:work e il nuovo metodo markdown() sulla response factory. Tante piccole migliorie anche a test, view compilate in parallelo e dischi local.
BashBox: un interprete Bash in sandbox per agenti AI in PHP
BashBox è un interprete bash scritto in puro PHP che permette di eseguire gli script generati dagli agenti AI senza mai ricorrere a proc_open, exec o shell_exec. Ogni comando è una classe PHP che lavora su un filesystem virtuale, con limiti di esecuzione, quote disco e accesso di rete configurabili. Ideale per chi sviluppa agenti con il Laravel AI SDK o altre librerie simili.