alpha(), progress() e light-dark() per le immagini: cosa puoi togliere dal tuo CSS
Il riepilogo di settembre di web.dev mette tra le novità Baseline tre funzioni che toccano token, temi e fluidità. Cosa sostituiscono, dove si fermano e come usarle senza lasciare indietro i browser meno recenti.
Aprendo il file dei token di un design system capita di trovare serie come --brand-10, --brand-20, --brand-50, create soltanto per avere lo stesso colore con trasparenze diverse. Capita anche di trovare due versioni di uno sfondo o di un logo, una per il tema chiaro e una per quello scuro, agganciate a un blocco @media (prefers-color-scheme: dark). Il 2 ottobre 2026 Rachel Andrew ha pubblicato su web.dev il riepilogo mensile delle funzionalità diventate "Baseline Newly available" a settembre, e tra queste ci sono tre funzioni CSS che intervengono proprio su questi punti: alpha(), light-dark() con valori immagine e progress().
La select personalizzabile, l'altra grande voce del riepilogo, l'ho già trattata in un articolo a parte. Qui mi concentro sulle tre funzioni che riguardano colore, temi e valori fluidi.
alpha(): la trasparenza derivata da un token
Secondo la definizione di web.dev, alpha() prende un colore di partenza e ne restituisce una versione con il canale alfa modificato, mantenendo i canali colore e lo spazio colore. MDN la descrive come una forma più breve della sintassi relativa dei colori. Il caso d'uso più diretto è dichiarare il colore una volta sola e ricavarne le varianti trasparenti:
:root {
--brand: oklch(55% 0.22 270);
}
.overlay {
background-color: alpha(from var(--brand) / 50%);
}
.card:hover {
background-color: alpha(from var(--brand) / 40%);
}
MDN mostra anche la forma relativa: dentro calc() la parola chiave alpha rappresenta l'opacità del colore di partenza, quindi alpha(from var(--my-color) / calc(alpha * 0.5)) dimezza l'opacità di qualunque colore, anche se il token cambia.
Cosa si può togliere: le serie di token che differiscono soltanto per l'alfa, e le dichiarazioni che richiedevano di riscrivere il colore nella sintassi relativa per cambiare un solo canale. Il colore resta definito in un punto, e overlay, bordi e stati hover lo derivano.
Il supporto indicato da web.dev è Chrome ed Edge 151, Firefox 155 e Safari 27.
light-dark() accetta anche le immagini
Fino a poco fa light-dark() funzionava solo con i colori. Ora, scrive MDN, accetta due colori oppure due immagini e restituisce quella corrispondente allo schema attivo, senza bisogno della media feature prefers-color-scheme. Le immagini possono essere url(), gradienti e, in generale, i valori di tipo image di CSS:
:root {
color-scheme: light dark;
}
.hero {
background-image: light-dark(
url("pattern-light.svg"),
url("pattern-dark.svg")
);
}
Ci sono tre condizioni da conoscere. La prima è che serve dichiarare color-scheme con entrambi i valori, altrimenti la funzione non ha uno schema da leggere. La seconda, ricordata da Bramus in un post del 19 marzo 2026 aggiornato a settembre, è che non si possono mescolare i tipi: due colori oppure due immagini, mai un colore e un'immagine nella stessa chiamata. La terza è una mia osservazione: la funzione lavora sui valori CSS, quindi copre sfondi, maschere e simili, ma non cambia la sorgente di un elemento img inserito nel markup.
Per verificare il supporto da CSS, il post di Bramus suggerisce @supports (background-image: light-dark(none, none)). Secondo web.dev i motori che la supportano sono Chrome ed Edge 150, Firefox 150 e Safari 27.
Cosa si può togliere: i blocchi @media (prefers-color-scheme: dark) che servono solo a cambiare un'immagine di sfondo, e con loro il rischio di dimenticarne uno quando si aggiunge un nuovo tema. Un vantaggio ricordato da MDN è che si può sovrascrivere color-scheme su singoli elementi, quindi una sezione sempre scura può avere le sue immagini senza regole speciali.
progress(): un numero tra 0 e 1
progress(valore, inizio, fine) restituisce la posizione del primo valore tra gli altri due, calcolata come (valore meno inizio) diviso (fine meno inizio). Per impostazione predefinita il risultato è limitato tra 0 e 1, e la parola chiave no-clamp permette di superare i limiti. L'esempio di web.dev lega l'opacità alla larghezza del viewport:
.card {
opacity: progress(100vw, 320px, 1200px);
}
MDN precisa che i tre argomenti devono essere dello stesso tipo: tutti numeri, tutte lunghezze o tutte percentuali. Mescolare una lunghezza e un numero rende la dichiarazione non valida. Il valore ottenuto è un numero senza unità, quindi si può moltiplicare per altri valori, per esempio dentro calc(), per ricavare opacità, scale o componenti di un colore.
Non è una funzione per tutti i giorni. Ha senso quando una proprietà deve seguire in modo continuo la larghezza di un contenitore o del viewport e vuoi dichiarare una volta sola i due estremi, invece di ripetere gli stessi valori in più regole. Per i salti netti tra layout restano più adatte le media query e le container query. Il supporto indicato da web.dev è Chrome ed Edge 138, Firefox 155 e Safari 26.
Come adottarle senza lasciare indietro nessuno
Baseline "Newly available" significa che la funzione è disponibile nelle versioni correnti dei principali motori, non che sia presente in tutti i browser in circolazione. Le versioni elencate da web.dev sono recenti, e non ho dati sulla quota di utenti che le usa: va controllata sul traffico del tuo sito.
alpha(): Chrome ed Edge 151, Firefox 155, Safari 27.light-dark()con immagini: Chrome ed Edge 150, Firefox 150, Safari 27.progress(): Chrome ed Edge 138, Firefox 155, Safari 26.
Fonte dei numeri di versione: web.dev, 2 ottobre 2026.
C'è un dettaglio del CSS che conviene tenere presente. Se scrivi una dichiarazione di riserva e poi la versione nuova, un browser che non conosce la funzione scarta la riga nuova e usa la precedente. Ma se il valore contiene var(), il browser accetta la riga al momento del parsing e la scarta solo al calcolo, quando una dichiarazione precedente non può più subentrare. È un comportamento generale del CSS, non specifico di queste funzioni, e con alpha(from var(--brand) / 50%) può far restare senza colore un browser meno recente. In questi casi è più sicuro racchiudere la versione nuova in @supports:
.overlay {
background-color: rgb(40 80 220 / 0.5); /* riserva */
}
@supports (background-color: alpha(from red / 50%)) {
.overlay {
background-color: alpha(from var(--brand) / 50%);
}
}
Il test con @supports è una mia proposta e va provato nei browser che ti interessano prima di metterlo in produzione. Per light-dark() con le immagini vale lo stesso schema, con il test suggerito da Bramus.
Se devo scegliere da dove cominciare, parto da light-dark() con le immagini, perché elimina regole sparse e ha lo stesso numero di versione, 150, su Chrome, Edge e Firefox. Poi alpha(), quando il design system ha davvero token duplicati da ripulire. progress() la riservo ai casi in cui un valore deve seguire una larghezza con continuità.
Fonti
- web.dev, Rachel Andrew, “riepilogo mensile sulle novità della piattaforma web di settembre 2026”, 2 ottobre 2026
- MDN, “alpha()”
- MDN, “light-dark()”
- MDN, “progress()”
- Bramus, “More Easy Light-Dark Mode Switching: light-dark() is about to support images!”, 19 marzo 2026 (aggiornato)