Il diavolo ha messo una penalità sulle cose della vita che ci piacciono.
L’articolo prosegue sotto
Il 62% del peso del web è costituito dalle immagini e più passano i giorni più inviamo byte di immagini. Sarebbe fantastico se tutti questi byte fossero usati in maniera ottimale, ma su schermi piccoli o a bassa risoluzione la maggior parte dei dati viene sprecata.
Perché? Nonostante il web sia stato progettato per essere accessibile da tutti, con qualunque dispositivo, è solo recentemente che il panorama dei device si è diversificato abbastanza da forzare un movimento grande quanto il settore verso il responsive design. Quando progettiamo in maniera responsive, il nostro contenuto fluisce elegantemente ed efficientemente in ogni device. Tutto il nostro contenuto, ad eccezione delle bitmap. Le immagini bitmap hanno una risoluzione fissa e il loro contenitore, il venerabile img
con la sua purtroppo singola src
non permette alcun adattamento.
Posti di fronte ad una scelta di Sophie – se rendere le pagine sfuocate per alcuni o lente per tutti – la maggior parte dei designer sceglie l’ultima, mandando a tutti le immagini che dovrebbero riempire gli schermi più grandi e a maggior risoluzione. Quindi, spreco.
Ebbene, dopo tre anni di dibattito, sono emersi alcuni markup per risolvere il problema delle immagini responsive:
srcset
sizes
picture
- e la nostra vecchia amica
source
(presa in prestito daaudio
evideo
)
Questi nuovi elementi ed attributi ci permettono di inserire nel markup più sorgenti alternative e mandano ad ogni client la sorgente che meglio gli si adatta. Si sono fatte strada nelle specifiche ufficiali e la loro prima implementazione completa, in Chrome 38, è uscita a Settembre. Con fallback eleganti e un polyfill per tamponare il problema, possiamo e dovremmo implementare le immagini responsive fin d’ora. Allora facciamolo!
Prendiamo una pagina web esistente e rendiamo le sue immagini responsive. Lo faremo in tre passaggi, applicando ciascun pezzo del nuovo markup a turno:
- Ci assicureremo che le nostre immagini si ridimensionino efficientemente con
srcset
esizes
. - Applicheremo l’art direction alle nostre immagini con
picture
esource media
. - Forniremo un formato di immagine alternativo usando
picture
esource type
.
In questo processo vedremo di persona gli incredibili guadagni in termini di performance permessi dalle nuove feature.
Lo status quo#section1
Credo non mi interessi così tanto essere vecchio quanto mi interessi essere grasso e vecchio.
Benjamin Franklin (o era Peter Gabriel?)
Prendiamo come tema una piccola pagina sulle bizzarre coperte denominate quilt. È una semplice pagina responsive. Non c’è molto che possa intralciare il suo contenuto primario: immagini giganti (di coperte!). Vogliamo mostrare sia il design generale di ogni coperta sia quanti più intricati dettagli possibile. Così, per ciascuna, presentiamo due immagini:
- l’intera coperta, della larghezza del paragrafo
- un dettaglio della stessa, che riempia il 100% della larghezza della viewport.
Come potremmo assegnare la dimensione e che markup dovremmo usare per le nostre immagini senza usare quello nuovo?
Per prima cosa prendiamo in considerazione le coperte intere. Per essere sicuri che appaiano sempre ben definite, dobbiamo capire la dimensione più grande possibile del layout. Ecco il CSS che ci interessa:
* {
box-sizing: border-box;
}
body {
font-size: 1.25em;
}
figure {
padding: 0 1em;
max-width: 33em;
}
img {
display: block;
width: 100%;
}
Possiamo anche calcolare la larghezza più grande possibile per il display di img
prendendo la max-width
di figure
, sottraendone il padding
e convertendo gli em in pixel:
100% <img> width
x ( 33em <figure> max-width
- 2em <figure> padding )
x 1.25em <body> font-size
x 16px default font-size
= 620px
Oppure possiamo imbrogliare rendendo davvero grande la finestra e dando un sbirciata ai dev tools:
(io preferisco il secondo metodo).
In entrambe i modi arriviamo alla massima larghezza del display che mostra l’img
dell’intero quilt con larghezza di 620px. Renderemo le nostre immagini sorgente al doppio di questa per accomodare gli schermi con risoluzione 2x: 1240 pixel di larghezza.
Ma cosa facciamo con le nostre immagini del dettaglio? Si espandono fino a riempire l’intera viewport, la cui dimensione non ha un limite superiore fissato. Così scegliamo qualcosa di leggermente più grande e dal sapore di standard e le rendiamo, diciamo, al massimo a 1920 pixel di larghezza.
Quando rendiamo le nostre immagini a queste dimensioni la nostra pagina dello status quo pesa un notevole 3.5MB. Tutto tranne 5.7kB di quel peso è costituito da immagini. Possiamo intuire che molti dei byte di quelle immagini costituiscono un overhead invisibile quando vengono inviati a piccoli schermi a bassa risoluzione, ma quanti? Mettiamoci al lavoro.
Primo passaggio: ridimensionamento con scrset
e sizes
#section2
Teatherball with a tennis ball for his shoelaces
Naturally adapt to have more than two faces
Il primo problema che affronteremo è far sì che le nostre immagini si ridimensionino in maniera efficiente tra le varie larghezze della viewport e le risoluzioni di schermo. Offriremo più risoluzioni della nostra immagine, così che possiamo inviare file sorgenti giganti solo agli schermi giganti e/o ad alta risoluzione e versioni più piccole a tutti gli altri. In che modo? Con srcset
.
Ecco una delle nostre immagini dei dettagli larga quanto la viewport:
<img
src="quilt_2-detail.jpg"
alt="Detail of the above quilt, highlighting the embroidery
and exotic stitchwork." />
quilt_2-detail.jpg
ha una larghezza di 1920 pixel. Rendiamo due versioni più piccole che devono andare con questa e scriviamo il markup in questo modo:
<img
srcset="quilt_2/detail/large.jpg 1920w,
quilt_2/detail/medium.jpg 960w,
quilt_2/detail/small.jpg 480w"
src="quilt_2/detail/medium.jpg"
alt="Detail of the above quilt, highlighting the embroidery
and exotic stitchwork.">
La prima cosa da notare riguardo a questa img
è che ha ancora src
, che verrà caricata dai browser che non supportano la nuova sintassi.
Per i client più capaci, abbiamo aggiunto qualcosa di nuovo: un attributo srcset
, che contiene una lista separata da virgole di URL per le risorse. Dopo ciascun URL includiamo un “descrittore di larghezza”, che specifica la larghezza in pixel di ciascuna immagine. La vostra immagine è 1024 x 768? Mettete un 1024w
dopo il suo URL in srcset
. I browser che riconoscono srcset
usano queste larghezze in pixel e tutto il resto che sanno dell’attuale ambiente di browsing per scegliere una sorgente da caricare tra quelle specificate nell’insieme.
In che modo fanno la loro scelta? Ecco quel che preferisco di srcset
: non lo sappiamo! Non possiamo saperlo perché la logica che sta dietro alla scelta è stata intenzionalmente lasciata non specificata.
Le prime soluzioni proposte per il problema delle immagini responsive cercavano di dare più controllo agli autori. Avremmo dovuto farcene carico noi, creando insiemi esaustivi di media query – piani di emergenza che elencano tutte le combinazioni di dimensioni di schermo e di risoluzioni, con una sorgente creata su misura per ciascuna.
srcset
ci salva da noi stessi. Il controllo dei particolari è ancora disponibile quando ci serve (di più su questo a breve), ma la maggior parte delle volte è meglio se lasciamo che sia il browser a decidere. I browser hanno un patrimonio di conoscenza dello schermo, della viewport, della connessione e delle preferenze di una persona. Cedendo il controllo, descrivendo le nostre immagini piuttosto che prescrivendo sorgenti specifiche per una miriade di destinazioni, permettiamo al browser di far valere la sua conoscenza. Otteniamo una funzionalità migliore (future-friendly!) da molto meno codice.
Tuttavia c’è un inghippo: scegliere una sorgente sensata richiede la conoscenza della dimensione del layout dell’immagine. Però non possiamo chiedere ai browser di ritardare la scelta finché tutto l’HTML, il CSS e il JavaScript siano stati tutti caricati e ne abbia fatto il parsing. Quindi dobbiamo dare ai browser una stima della larghezza del display dell’immagine usando un altro attributo nuovo: sizes
.
Come ho fatto a nascondervi questa scomoda verità fino ad ora? Le immagini dei dettagli nella nostra pagina di esempio sono un caso particolare. Occupano l’intera larghezza della viewport—100vw
—che quindi diventa il valore di default delle sizes
. Tuttavia, le nostre immagini delle coperte intere, hanno la stessa larghezza del paragrafo e spesso occupano significativamente meno spazio. È necessario che noi si dica al browser esattamente quanto grandi debbano essere con sizes
.
sizes
accetta le lunghezze CSS. Quindi:
sizes="100px"
…dice il browser: questa immagine verrà mostrata alla larghezza fissa di 100px
. Semplice!
Il nostro esempio è più complesso. Mentre le img
della coperta hanno come stile una semplice regola width: 100%
, le figure che le ospitano hanno una max-width
di 33em
.
Fortunamtamente, sizes
ci permette di fare due cose:
- Ci permette di dare misure diverse elencandole separate da virgole.
- Ci permette di agganciare delle media condition alle misure.
In questo modo:
sizes="(min-width: 33em) 33em, 100vw"
Dice: la viewport è più larga di 33em
? Questa immagine sarà larga 33em
. Altrimenti, sarà 100vw
.
Questo si avvicina a quello che serve a noi, ma non va completamente bene. Il nostro esempio è reso complicato dalla natura relativa degli em. Il corpo della nostra pagina ha un font-size
di 1.25em
, quindi “1em” nel contesto del CSS delle nostre figure
sarà 1.25 volte la dimensione di default del font del browser. Ma all’interno delle media condition (e quindi, all’interno di sizes
), un eem è sempre uguale al font size di default. Occorre fare delle moltiplicazioni per 1.25: 1.25 x 33 = 41.25.
sizes="(min-width: 41.25em) 41.25em,
100vw"
Questo cattura piuttosto bene la larghezza delle nostre coperte e, francamente, probabilmente è sufficientemente buono. È accettabile al 100% che sizes
fornisca la browser una stima grezza della larghezza del layout delle img
. Spesso, barattare una piccola quantità di precisione per degli enormi guadagni in termini di leggibilità e manutenibilità è la scelta giusta. Ciò detto, proseguiamo e rendiamo preciso il nostro esempio calcolando gli em di padding su entrambe i lati della figura: 2 lati x 1.25 em della media-condition ciascuno = 2.5em di padding da tenere presenti.
<img
srcset="quilt_3/large.jpg 1240w,
quilt_3/medium.jpg 620w,
quilt_3/small.jpg 310w"
sizes="(min-width: 41.25em) 38.75em,
calc(100vw - 2.5em)"
src="quilt_3/medium.jpg"
alt="A crazy quilt whose irregular fabric scraps are fit
into a lattice of diamonds." />
Rivediamo cosa abbiamo fatto in questo caso. Abbiamo dato al browser la versione grande, media e piccola della nostra immagine usando srcset
e abbiamo dato loro delle larghezze in pixel usando i descrittori w
, e abbiamo detto al browser quanto spazio del layout dovranno occupare mediante sizes
.
Se questo fosse un semplice esempio, avremmo potuto dare al browser una singola lunghezza nel CSS come sizes="100px"
o sizes="50vw"
. Ma non siamo stati così fortunati. Abbiamo dovuto dare al browser due lunghezze CSS e stabilire che la prima lunghezza si applica solo quando è vera una certa media condition.
Fortunatamente, tutto quel lavoro non è stato fatto in vano. Utilizzando srcset
e sizes
, abbiamo dato al browser tutto quello di cui ha bisogno per scegliere una sorgente. Una volta che conosce le larghezze in pixel delle sorgenti e la larghezza del layout della img
, il browser calcola la proporzione della larghezza sorgente-layout per ogni sorgente. Quindi, supponiamo che sizes
ritorni 620px. Una sorgente di 620w
avrebbe 1x i pixel della img
. Una sorgente di 1240w
ne avrebbe 2x. 310w
? 0.5x. Il browser individua queste proporzioni e poi sceglie qualsiasi sorgente voglia.
Vale la pena notare che la specifica permette di fornire direttamente proporzioni e che alle sorgenti senza un descrittore verrà assegnata la proporzione di default 1x
, permettendoci di scrivere markup come questo:
<img src="standard.jpg" srcset="retina.jpg 2x, super-retina.jpg 3x" />
Questo è un modo carino e compatto per dare immagini ad hi-DPI. Ma, funziona solo per immagini con larghezza fissa. Tutte le immagini della nostra pagine delle coperte folli sono fluide, quindi questa è l’ultima volta in cui sentiremo parlare dei descrittori x
.
Misurare#section3
Ora che abbiamo riscritto la nostra pagina delle coperte folli usando srcset
e sizes
, cosa abbiamo guadagnato in termini di performance?
Il peso della nostra pagina è ora (gloriosamente!) reattivo alle condizione del browsing. Varia, pertanto non possiamo rappresentarlo con un solo numero. Ho ricaricato la pagina molte volte in Chrome e ho messo su un grafico il suo peso in una varietà di viewport di larghezza diversa:

La prima linea grigia e piatta in alto rappresenta il peso dello status quo, 3.5MB. Le linee spesse (schermi 1x) e sottili (2x) di colore verde rappresentano il peso della nostra pagina per ogni larghezza di viewport tra 320px e 1280px aggiornata con srcset
e size
.
Su schermi 2x di 320px di larghezza abbiamo tagliato il peso della nostra pagina di due terzi: prima la pagina era di 3.5MB, adesso inviamo solo 1.1MB. Su schermi 1x di 320px, la nostra pagina è meno di un decimo il peso che era: 306kB.
Da qui, le dimensioni in by crescono man mano che carichiamo sorgenti più grandi per riempire viewport più grandi. Sui device 2x facciamo un salto significativo alle larghezze della viewport di circa 350px e torniamo al peso dello status quo dopo 480px. Sugli schermi 1x, il risparmio è notevole: abbiamo risparmiato tra il 70 e l’80% del peso originale della pagina finché non abbiamo passato i 960px. Là raggiungiamo il massimo con una pagina che è più piccola del 40% circa rispetto a quella da cui siamo partiti.
Questi tipi di riduzioni – 40%, 70%, 90% – dovrebbero farvi fermare di colpo. Abbiamo tolto quasi due megabyte e mezzo di carico per ogni iPhone Retina. Misuratelo in millisecondi o moltiplicatelo per migliaia di pageview e vedrete per che cosa è tutta questa agitazione.
Secondo step: picture
e art direction#section4
srcset
se sei pigro,picture
se sei matto™
Allora, per le immagini che devono semplicemente ridimensionarsi, facciamo una lista delle sorgenti e delle loro larghezze in pixel in srcset
, facciamo sapere al browser quale sarà la larghezza dell’img
visualizzata usando sizes
e lasciamo perdere il nostro desiderio da pazzi di avere il controllo. Però, ci saranno momenti in cui vorremo che le nostre immagini si adattino in modi che vanno oltre il ridimensionamento. Quando lo facciamo, abbiamo bisogno di riappropriarci di una parte di quel controllo sulla scelta della sorgente. Faccia il suo ingresso picture
.
Le nostre immagini dei dettagli hanno un aspect ratio grande, 16:9. Su schermi grandi vanno benissimo ma sui telefoni appaiono piccole. Le cuciture e i ricami che dovrebbero essere mostrati nei dettagli sono troppo piccoli per risaltare.
Sarebbe bello se potessimo fare un ingrandimento sugli schermi piccoli, presentando un crop più alto e stretto.
Questo tipo di ritaglio su misura del contenuto dell’immagine perché si adatti ad ambienti specifici è detto “art direction”. Ogni volta che facciamo un crop o alteriamo un immagine perché sia adatta ad un breakpoint (piuttosto che fare un semplice ridimensionamento dell’intera immagine), stiamo facendo art direction.
Se includessimo i crop ingranditi in un srcset
, non si potrebbe dire quando verrebbero scelti e quando no. Utilizzando picture
e source media
, possiamo rendere espliciti i nostri desideri: carica solo i crop rettangolari larghi quando la viewport è più larga di 36em. Nelle viewport più piccole, usa sempre quelle quadrate.
<picture>
<!-- 16:9 crop -->
<source
media="(min-width: 36em)"
srcset="quilt_2/detail/large.jpg 1920w,
quilt_2/detail/medium.jpg 960w,
quilt_2/detail/small.jpg 480w" />
<!-- square crop -->
<source
srcset="quilt_2/square/large.jpg 822w,
quilt_2/square/medium.jpg 640w,
quilt_2/square/small.jpg 320w" />
<img
src="quilt_2/detail/medium.jpg"
alt="Detail of the above quilt, highlighting the
embroidery and exotic stitchwork." />
</picture>
Un elemento picture
contiene un qualsiasi numero di elementi sorgente e un’img
. Il browser esamina le source
di picture
finché trova quella il cui attributo media
si abbina all’ambiente attuale. Invia il corrispondente srcset
della source
all’img
, che è ancora l’elemento che “vediamo” sulla pagina.
Ecco un caso più semplice:
<picture>
<source media="(orientation: landscape)" srcset="landscape.jpg" />
<img src="portrait.jpg" alt="A rad wolf." />
</picture>
Nelle viewport in modalità landscape, img
riceve landscape.jpg
. Quando siamo in protrait (o se il browser non supporta picture
) img
rimane così com’è e si carica portrait.jpg
.
Questo comportamento può sorprendere in parte se si è abituati ad audio
e video
. A differenza di questi elementi, picture
è un wrapper invisibile: uno span
magico che semplicemente invia un srcset
alla sua img
.
Possiamo metterla anche in questi termini: img
non è un fallback. Stiamo applicando il progressive enhancement a img
inserendola in un elemento picture
.
In pratica, questo significa che qualunque stile vogliamo applicare all’immagine che verrà visualizzata dovrà essere impostato su img
, non su picture
. picture { width: 100% }
non fa alcunché. picture > img { width: 100% }
fa quello che volete.
Ecco la nostra pagina di coperte pazze con applicato quel pattern. Tenendo a mente che il nostro scopo per l’utilizzo di picture
era di fornire agli utenti su piccolo schermo più pixel (più significativi), vediamo come cambia la performance:

Niente male! Stiamo mandando pochi byte in più agli schermi 1x piccoli, ma per qualche complicata ragione avendo a che fare con le dimensioni delle nostre immagini sorgente, abbiamo in realtà esteso il range di dimensioni di schermo che vedo un risparmio a 2x. Il risparmio sulla pagina al primo passaggio si era fermato a 480px sugli schermi 2x, ma dopo il nostro secondo passaggio, continua fino a 700px.
La nostra pagina adesso si carica più rapidamente e appare migliore sui device più piccoli. E non abbiamo ancora finito!
Terzo step: formati multipli con source type
#section5
I 25 anni di storia del web sono stati dominati da due formati bitmap: JPEG e GIF. C’è voluta una decade dolorosa per PNG prima di potersi unire al club esclusivo. Nuovi formati come WebP and JPEG XR stanno bussando alla porta, promettendo agli sviluppatori una miglior compressione e offrendo caratteristiche utili come gli alpha channels e modalità lossless. Ma a causa della tristemente singola src
di img
, la loro adozione è stata lenta: gli sviluppatori hanno bisogno di un supporto quasi universale per un formato prima di poterlo utilizzare. Non più ora. picture
rende semplice offrire più formati seguendo lo stesso pattern source type
stabilito da audio
e video
:
<picture>
<source type="image/svg+xml" srcset="logo.svg" />
<img src="logo.png" alt="RadWolf, Inc." />
</picture>
Se il browser supporta un type
di source
, manderà il srcset
di quella source
all’img
.
Questo è un esempio chiaro, ma quando sovrapponiamo i cambiamenti di source type
sulla nostra pagina esistente delle coperte pazze, ad esempio per aggiungere il supporto per WebP, le cose si fanno spinose (e ripetitive):
<picture>
<!-- 16:9 crop -->
<source
type="image/webp"
media="(min-width: 36em)"
srcset="quilt_2/detail/large.webp 1920w,
quilt_2/detail/medium.webp 960w,
quilt_2/detail/small.webp 480w" />
<source
media="(min-width: 36em)"
srcset="quilt_2/detail/large.jpg 1920w,
quilt_2/detail/medium.jpg 960w,
quilt_2/detail/small.jpg 480w" />
<!-- square crop -->
<source
type="image/webp"
srcset="quilt_2/square/large.webp 822w,
quilt_2/square/medium.webp 640w,
quilt_2/square/small.webp 320w" />
<source
srcset="quilt_2/square/large.jpg 822w,
quilt_2/square/medium.jpg 640w,
quilt_2/square/small.jpg 320w" />
<img
src="quilt_2/detail/medium.jpg"
alt="Detail of the above quilt, highlighting the
embroidery and exotic stitchwork." />
</picture>
È davvero molto codice per un’unica immagine e stiamo inoltre gestendo un gran numero di files: 12! Tre risoluzioni, due formati e due crop per immagine sono davvero molti. Tutto quello che abbiamo guadagnato in performance e funzionalità ha avuto un costo pagato dalla complessità anticipata e dalla sostenibilità futura.
L’automazione è vostra amica: se la vostra pagina conterrà enormi blocchi di codice che fanno riferimento a numerose versioni alternative di un’immagine, sarà meglio che evitiate di scriverla a mano.
Si deve anche sapere quando troppo è troppo. Nel nostro esempio, ho usato tutti gli strumenti presenti nella specifica. Questa cosa non è quasi mai prudente. Si possono avere guadagni incredibili usando una qualsiasi delle nuove feature da sola e dovreste studiarvi bene le complessità derivanti dalla somma di ciascuna prima di estrarre le tenaglie e buttarvi nella riparazione dello scarico del lavandino.
Detto questo, diamo un’occhiata a quello che può fare WebP per le nostre coperte.

Abbiamo un ulteriore 25-35% di risparmio in più oltre a quello che abbiamo già ottenuto, non solo nella parte bassa, ma su tutto il piano. Sicuramente non è da sottovalutare! La mia metodologia in questo caso non è per nulla rigorosa: la performance del vostro WebP potrebbe cambiare, ma il punto è: nuovi formati che possono dare benefici significativi rispetto allo status quo attuale costituito da JPEG/GIF/PNG e altri che continueranno ad arrivare. picture
e source type
abbassano la barriera in ingresso, spianando per sempre la strada per l’innovazione nei formati di immagine.
size
the day#section6
Questo è il segreto della perfezione:
When raw wood is carved, it becomes a tool;
[…]
The perfect carpenter leaves no wood to be carved.
Per anni, abbiamo saputo cosa rallentava le nostre pagine responsive: le immagini, quelle grandi, create soprattutto per gli schermi molto grandi, che abbiamo inviato a tutti. È da un po’ che sappiamo come sistemare questo problema, inviando sorgenti diversi a client diversi. Il nuovo markup ci permette di fare proprio questo. srcset
ci permette di offrire più versioni di un’immagine ai browser, che con un po’ di aiuto da sizes
, sceglie la sorgente più appropriata da caricare tra quelle offerte. picture
e source
ci permettono di intervenire e di esercitare un po’ più di controllo, assicurandoci che verranno scelte determinate sorgenti in base o alle media query o al supporto del tipo di file.
Insieme, queste feature ci permetto di creare un markup adattabile, flessibile e responsive per le immagini. Ci permettono di mandare a ciascuno dei nostri utenti una sorgente su misura per il suo device, permettendo immensi guadagni in termini di performance. Armati di un eccellente polyfill e di un occhio verso il futuro, gli sviluppatori dovrebbero cominciare ad usare questo markup ora!
Illustrazioni: {carlok}
Nessun commento
Altro da ALA
Webwaste
Uno strumento essenziale per catturare i vostri progressi lavorativi
Andiamo al cuore dell’accessibilità digitale
JavaScript Responsabile, Parte II
JavaScript Responsabile: parte prima