Images responsives : picture et srcset

Charger les images en fonction de la résolution d'écran de l'utilisateur, avoir des images responsives, faire de la direction artistique... la révolution est en marche ! Belle présentation par Eric Portis.

Par

Tout ce qui précède pourrait être résumé ainsi : concevoir des pages adaptables, c'est concevoir des pages accessibles. Et peut-être la plus belle promesse du web, loin d'être accomplie aujourd'hui, est-elle l'accessibilité, malgré les difficultés, à l'information.
John Allsopp, Le Tao du Web Design

Parmi les éléments d'information disponibles sur le web, les images sont certainement l'un des plus importants et pourtant, depuis 25 ans que le web existe, elles n'ont jamais été très adaptables. Tout en elles est obstinément fixe : leur taille, format, découpage, tout est gravé dans le marbre d'un simple attribut src.

Les auteurs de pages HTML ont commencé à ressentir douloureusement ces limitations lorsque les écrans haute-résolution et les mises en pages responsives ont asséné au web un double coup de poings. Les auteurs, désireux de voir leurs images bien nettes sur de grands écrans haute-résolution, ont commencé à envoyer des sources de plus en plus lourdes, la taille moyenne des images est partie en flèche... et des gens très intelligents ont qualifié le web design responsif de unworkably slow (“impraticablement lent”).

L'obstacle n°1 à la mise en oeuvre de pages responsives réellement adaptables et performantes, ce sont les images. Mais tout ceci est sur le point de changer.

La dernière spécification de l'élément <picture> est le résultat d'années (d'années !) de débats sur la façon de rendre les images adaptables. Elle donne aux auteurs la possibilité de regrouper de façon sémantique plusieurs versions d'une même image, chacune ayant ses caractéristiques techniques qui la rendent plus ou moins appropriée à tel ou tel utilisateur. La nouvelle spécification a fait l'objet d'un large consensus et elle est actuellement en cours d'implémentation dans Chrome, Opera et Firefox (peut-être même Internet Explorer !).

Il est temps de commencer à apprendre !

Avant de nous lancer dans le nouveau balisage, regardons comment les environnements de navigation peuvent varier, c'est à dire comment et à quoi nous voulons adapter nos images.

  1. Nos images doivent être capables d'être nettes quel que soit le device-pixel-ratio. Nous voulons que les écrans à haute-résolution reçoivent des images en haute-résolution mais nous ne voulons pas envoyer ces images à des utilisateurs qui ne verraient pas ces pixels supplémentaires. Appelons ceci le cas d'utilisation device-pixel-ratio.
  2. Si notre mise en page est fluide (responsive), alors nos images devront pouvoir s'agrandir ou se réduire pour s'adapter. Appelons ceci le cas d'utilisation image fluide.
  3. Ces deux cas sont liés : pour les résoudre, nous aurons besoin d'images en résolutions multiples. Nous appellerons cas d'utilisation image à taille variable la résolution simultanée des deux problèmes.
  4. Nous aurons parfois besoin d'adapter nos images d'une façon qui va plus loin que le simple changement d'échelle, par exemple en les recadrant ou en modifiant subtilement leur contenu. Appelons ceci le cas d'utilisation direction artistique.
  5. Dernier point, les navigateurs ne sont pas tous compatibles avec les mêmes formats d'images. Nous voudrons peut-être envoyer un format nouveau tel que WebP à des navigateurs compatibles, et un bon vieux JPEG aux navigateurs non compatibles. Appelons ce cas d'utilisation changement de type.

La nouvelle spécification <picture> répond à chacun de ces cas d'utilisation. Passons-les en revue.

différents devices avec présentation d'images
Réorganiser les images en fonction des différentes résolutions est assez facile, mais charger les images en fonction de la résolution d'écran de l'utilisateur l'est beaucoup moins. Enfin, plus maintenant !

Le cas device-pixel-ratio

Commençons simplement, nous prendrons une image à largeur fixe que nous voulons adapter à des device-pixel-ratio différents. Pour ce faire, nous allons utiliser le premier outil que nous offre la nouvelle spécification, pour grouper et décrire les sources d'images : l'attribut srcset.

Supposons que nous ayons deux versions d'une même image :

  • small.jpg (320 x 240 pixels)
  • large.jpg (640 x 480 pixels)

Nous voulons envoyer large.jpg aux utilisateurs d'écran haute résolution et à eux seuls. Avec srcset, voici à quoi ressemblerait notre balisage :

<img srcset="small.jpg 1x, large.jpg 2x"
   src="small.jpg"
   alt="A rad wolf" />

L'attribut srcset accepte une liste d'url séparées par une virgule, chacune contenant un descripteur x qui indique à quel device-pixel-ratio l'image est destinée.

L'attribut src est là pour les navigateurs qui ne comprennent pas srcset. Et alt sert, comme d'habitude, pour le cas où le navigateur n'affiche pas l'image. Un élément et trois attributs nous donnent une image nette sur les devices à haute-résolution et une dégradation élégante jusqu'au cas extrême d'absence d'affichage. Pas trop mal !

Les cas image fluide et taille variable

Ce balisage ne nous permettra toutefois pas de réduire ou d'augmenter la taille de notre image dans une mise en page fluide. Avant de répondre à ce cas d'utilisation, un bref rappel sur la façon dont fonctionnent les navigateurs.

Le préchargement des images est, selon Steve Souders, “la meilleure amélioration des performances que les navigateurs aient jamais réalisée”. Les images sont souvent les éléments les plus lourds sur une page, c'est l'intérêt de tous de les charger le plus tôt possible. Par conséquent, la première chose que fera un navigateur sera de scanner le HTML pour trouver les url des images et commencer à les charger. Le navigateur fait cela avant même d'avoir construit le DOM, chargé le CSS externe ou peint la mise en page. Du coup, résoudre le cas d'utilisation image fluide s'avère complexe, le navigateur devant choisir une source avant même de connaître la taille à laquelle l'image sera rendue.

Ce que connaît le navigateur en revanche, c'est l'environnement dans lequel l'image sera rendue : la taille du viewport, la résolution de l'écran de l'utilisateur, et toutes ces sortes de choses. Ce sont ces informations que nous utilisons lorsque nous écrivons des media queries, qui adaptent nos mises en pages à un environnement de navigation spécifique.

Donc, pour contourner le problème du préchargement, les premières propositions ont suggéré d'attacher des media queries aux sources. Nous aurions basé le mécanisme de choix des sources sur la taille du viewport, que le navigateur connaît dès le début, et non sur la taille finale de l'image, qu'il ne connaît pas.

un petit bonhomme qui mesure une image
Gérer les images responsives s'est avéré un cauchemar. Une meilleure façon d'informer le navigateur sur son environnement est de lui donner la taille de l'image. Évident, non ?

En fait, c'est une mauvaise idée. Bien que réalisable techniquement, calculer les media queries nécessaires est ennuyeux et source d'erreur. Une bien meilleure idée est de dire au navigateur quelle sera la taille de l'image dans le rendu final !

Une fois que le navigateur sait de combien de pixels il a besoin (via un nouvel attribut sizes) et de combien de pixels chacune des sources est constituée (via le descripteur w dans srcset), choisir une source est un jeu d'enfant. Le navigateur choisit la plus petite source qui rendra de manière raisonnablement nette dans son contenant.

Pour que ce soit plus concret, développons notre exemple précédent. Supposons que nous ayons trois versions de notre image :

  • large.jpg (1024 x 768 pixels)
  • medium.jpg (640 x 480 pixels)
  • small.jpg (320 x 240 pixels)

Nous voulons placer ces images dans une grille flexible, une grille qui commence comme une simple colonne, puis se transforme en trois colonnes lorsque le viewport est plus large, comme ceci :

exemple de grille responsive avec des photos de loups
Un exemple de grille responsive

Voici le balisage :

<img srcset="large.jpg  1024w,
      medium.jpg 640w,
      small.jpg  320w"
   sizes="(min-width: 36em) 33.3vw,
      100vw"
   src="small.jpg"
   alt="A rad wolf" />

Nous utilisons srcset à nouveau, mais au lieu de descripteurs x nous attachons des descripteurs w à nos sources. Ceux-ci décrivent la largeur réelle, en pixels, du fichier en question. Donc si vous "sauvegardez pour le web..." ("Save for Web...") à 1024 x 768 pixels, décrivez cette source dans srcset en mettant 1024w.

Remarquez bien que nous ne spécifions que la largeur de l'image. Pourquoi pas la hauteur ? Les images dans notre mise en page ont une contrainte relative à leur largeur, celle-ci est indiquée explicitement par le CSS, pas leur hauteur. L'immense majorité des images responsives que l'on trouve sur le net ont une contrainte relative à leur largeur, donc pour rester simple la spécification ne s'intéresse qu'à la largeur. Il y a certes de bonnes raisons d'inclure la hauteur également - mais pas pour l'instant.

Donc, w dans srcset indique combien de pixels a chacune de nos sources. Passons maintenant à l'attribut sizes. Cet attribut indique au navigateur de combien de pixels il a besoin, en décrivant la largeur finale de notre image telle qu'elle sera rendue. Considérez sizes comme une façon de donner au navigateur un peu d'information à l'avance sur la mise en page, afin qu'il puisse choisir une source avant d'avoir parsé ou rendu le CSS de la page.

Pour ce faire, nous donnons au navigateur une longueur CSS (CSS length) qui décrit la largeur de l'image qui sera rendue. Les longueurs CSS peuvent être absolues (par exemple 99px ou 16em) ou relatives au viewport (33.3vw, comme dans notre exemple). Cette partie “relative au viewport” est ce qui permet aux images d'être flexibles.

Si notre image occupe un tiers du viewport, notre attribut sizes doit être :

sizes="33.3vw"

Notre exemple n'est pas si simple. La mise en page a un breakpoint à 36em. Quand la largeur du viewport est inférieure à 36em, la mise en page change. En-dessous de ce breakpoint, l'image remplira 100% de la largeur du viewport. Comment codons-nous cette information dans nos attributs sizes ?

Nous le faisons en combinant les media queries et les longueurs :

sizes="(min-width: 36em) 33.3vw,
       100vw"

La syntaxe est la suivante :

sizes="[media query] [length],
   [media query] [length],
   etc…
   [default length]"

Le navigateur passe en revue chaque media query jusqu'à ce qu'il trouve celui qui corresponde, puis il utilise la longueur associée (dans notre exemple, tant que la largeur du viewport est supérieure ou égale à 36em, la longueur CSS est 33.3vw, soit 33,3% de la largeur du viewport). Si aucun media query ne correspond, le navigateur utilise la longueur “par défaut”, c'est à dire n'importe quelle longueur qu'il rencontre et qui n'a pas de media query associé (dans notre exemple, c'est 100vw, qui signifie 100% de la largeur du viewport).

Avec une longueur sizes et un ensemble de sources comportant des descripteurs w dans srcset, le navigateur a tout ce qu'il lui faut pour charger efficacement une image dans une mise en page fluide et responsive.

w et srcset donnent également au browser toutes les informations nécessaires pour adapter l'image à des device-pixel-ratio changeants. En convertissant la longueur CSS que nous lui donnons dans sizes en pixels CSS, et en multipliant ce chiffre par le device-pixel-ratio de l'utilisateur, le navigateur sait le nombre de pixels qu'il doit remplir - quel que soit le device-pixel-ratio de l'utilisateur.

Alors que l'exemple de notre cas d'utilisation device-pixel-ratio ne fonctionne que pour les images à largeur fixe et ne concerne que les écrans 1x et 2x, cet exemple srcset et sizes répond aux cas d'utilisation image fluide et s'adapte à toute densité d'écran.

Nous avons résolu les deux problèmes en même temps. Parmi les catégories définies au début de cet article, w dans srcset et sizes couvrent le cas d'utilisation image à taille variable.

Et de manière plus extraordinaire encore, ce balisage donne des marges de manoeuvre au navigateur. Le fait d'attacher des conditions spécifiques aux sources signifie que le navigateur choisit à partir d'un ensemble de conditions strictes. Nous lui disons “si l'écran est en haute-résolution, alors tu dois utiliser telle source”. En décrivant simplement les dimensions des ressources avec w dans srcset, et la surface qu'elles occuperont avec sizes, nous permettons au navigateur d'appliquer tout ce qu'il sait sur l'environnement utilisateur à un problème de choix de sources. La spécification permet par exemple aux navigateurs de charger des sources plus petites lorsque la bande passante est faible ou coûteuse.

Encore une chose. Dans notre exemple, la taille de l'image est toujours un simple pourcentage de la largeur du viewport. Mais que se passe-t-il si notre mise en page comporte à la fois des longueurs absolues et relatives, par exemple si nous ajoutons une sidebar fixe de 12em à notre présentation en trois colonnes comme ceci ?

la même grille responsive, cette fois avec une sidebar fixe à droite
Une mise en page combinant des longueurs absolues et relatives.

Nous utiliserions la fonction calc(), étonnamment compatible, dans notre attribut sizes (NdT: pour plus d'infos sur calc, en français, cliquer ici).

sizes="(min-width: 36em) calc(.333 * (100vw - 12em)),
       100vw"

Et voilà !

Le cas Direction Artistique

Nous sommes maintenant super efficaces ! Nous avons appris comment baliser des images de tailles variables qui s'agrandissent et se réduisent efficacement, et sont rendues avec la netteté voulue sur tout écran ou terminal.

Et si nous voulions aller un peu plus loin dans l'adaptabilité ?

Quand Apple a lancé l'iPad Air l'an dernier, le site web montrait une énorme image du device. Rien de remarquable, sauf si - en bon geek du web design - vous vous amusiez à redimensionner votre fenêtre. Lorsque la largeur du viewport était suffisamment étroite, l'iPad faisait une chose remarquable : il tournait pour s'adapter au viewport !

Nous appelons ce genre de choses la “direction artistique”.

Apple a opéré ce traitement artistique en maltraitant quelque peu HTML et CSS : en balisant son image, qui était clairement du contenu, comme une div vide et en échangeant son background-image avec CSS. La nouvelle spécification <picture> nous permet de faire avec HTML ce genre de traitements artistiques basés sur des breakpoints.

La spécification facilite ceci en ajoutant une autre méthode de groupement de sources au-dessus de srcset : <picture> et source.

Revenons à notre exemple. Supposons qu'au lieu de laisser notre image remplir toute la largeur du viewport sur de petits écrans nous recadrons l'image pourqu'elle devienne carrée, nous zoomons sur la partie la plus importante du sujet, et présentons ce petit carré à une taille fixe, flotté à gauche, et en laissant beaucoup d'espace pour un texte descriptif, comme ceci :

même grille, images carrées et sous-textes
Un exemple d'images combinées avec un texte descriptif.

Pour y parvenir, nous avons besoin de quelques sources supplémentaires :

  • cropped-small.jpg (96 × 96 pixels)
  • cropped-large.jpg (192 × 192 pixels)
  • small.jpg (320 × 240 pixels)
  • medium.jpg (640 × 480 pixels)
  • large.jpg (1024 × 768 pixels)

Et notre balisage :

<picture>
   <source media="(min-width: 36em)"
      srcset="large.jpg  1024w,
         medium.jpg 640w,
         small.jpg  320w"
      sizes="33.3vw" />
   <source srcset="cropped-large.jpg 2x,
         cropped-small.jpg 1x" />
   <img src="small.jpg" alt="A rad wolf" />
</picture>

Cet exemple est aussi complexe que possible, il fait usage de toutes les caractéristiques que nous avons rencontrées jusqu'ici. Regardons-les en détail.

L'élément <picture> contient deux source et une img. Les source représentent les deux versions de l'image (le recadrage carré, et l'image non recadrée). L' img (obligatoire) nous sert de solution de rechange. Comme nous allons le découvrir bientôt, c'est elle qui fait une bonne partie du travail en arrière-plan.

Regardons d'abord la première source :

<source media="(min-width: 36em)"
   srcset="large.jpg  1024w,
      medium.jpg 640w,
      small.jpg  320w"
   sizes="33.3vw" />

Cette source représente la version non recadrée de notre image. Nous ne voulons montrer l'image complète que dans la mise en page sur trois colonnes, c'est à dire lorsque le viewport est plus large que 36em. Le premier attribut ici, media="(min-width: 36em)" est ce qui rend cela possible. Si une query dans un attribut media est évaluée comme vraie, alors le navigateur doit utiliser cette source - sinon on passe à la suivante.

Les deux autres attributs de sourcesrcset et sizes - sont copiés pour l'essentiel sur notre exemple antérieur d'image à taille variable. Une différence toutefois : parce que cette source sera choisie uniquement pour la mise en page sur trois colonnes, notre attribut sizes n'a besoin que d'une longueur, 33.3vw.

Quand le viewport est plus petit que 36em, le premier media query de source est évalué comme faux, et nous passons au second :

<source srcset="cropped-large.jpg 2x,
         cropped-small.jpg 1x" />

Ceci représente notre petite image carrée. Cette version est affichée à une largeur fixe, mais nous voulons qu'elle soit bien nette sur les écrans haute-résolution. Nous avons donc fourni les deux versions (1x et 2x).

Enfin, nous arrivons à l'important (et obligatoire !) img.

Tout enfant d'un élément audio ou video qui n'est pas une source est traité par les navigateurs compatibles comme un contenu de rechange (fallback) et caché. Vous pourriez penser qu'il en va de même avec img. Eh bien non ! En fait, c'est l'élément img que les utilisateurs voient quand nous utilisons <picture>. Sans img, il n'y a pas d'image. <picture> et toutes ses source sont là en fait pour alimenter img en sources.

Pourquoi ? Un des principaux griefs contre la première spécification <picture> était qu'elle réinventait la roue, créant un élément media HTML entièrement nouveau, dans le sillage d'audio et video , qui pour l'essentiel dupliquait les fonctionnalités d'img. Des fonctionnalités dupliquées signifient la duplication de la mise en oeuvre et de la maintenance, un travail que les navigateurs n'étaient pas prêts à entreprendre.

D'où le retour à img dans la nouvelle spécification. <picture> est invisible, un peu comme un span magique. Ses source sont simplement là pour aider le navigateur à aller chercher des versions alternatives d'une image. Une fois l'url choisie, celle-ci est passée à l'img.

Conséquence pratique : pour appliquer des styles à l'image (par exemple max-width: 100%) on les applique à img, pas à <picture> !

Ok, et maintenant notre dernier point.

Le cas changement de type

Admettons que nous ne soyons pas intéressés par tous ces étirements d'images et autres adaptations aux viewports, mais que nous voulions simplement essayer un nouveau format et fournir une solution de rechange pour les navigateurs non compatibles. Pour cela, nous suivons le même schéma qu'audio et video : source type.

<picture>
   <source type="image/svg" src="logo.svg" />
   <source type="image/png" src="logo.png" />
   <img src="logo.gif" alt="RadWolf, Inc." />
</picture>

Si le navigateur ne connaît pas le type MIME image/svg, il passe à la source suivante. Et s'il n'arrive pas à se dépatouiller de image/png, alors il revient à img et au GIF.

Pendant la douloureuse période de transition GIF-PNG, les designers web auraient tué pour avoir une telle fonctionnalité. L'élément <picture> nous le donne, ouvrant la voie à l'adoption facile de nouveaux formats dans les années qui viennent.

C'est fini !

Et voilà, nous avons fait le tour de la spécification <picture> et des raisonnements sur lesquels elle est fondée. Au final, srcset, x, w, sizes, <picture>, source, media et type constituent une riche boîte à outils avec lesquels nous pouvons réaliser des images réellement adaptables, des images qui peuvent (enfin !) se couler efficacement dans nos mises en page flexibles et répondre aux contraintes de tous les terminaux.

La spécification n'est pas encore finalisée. Les premières implémentations sont en cours et on utilise encore des flags expérimentaux. Ses auteurs et expérimentateurs y travaillent chaque jour et débattent des moindres détails, tout ceci sous la responsabilité du Responsive Images Community Group. Si vous êtes intéressés, vous pouvez rejoindre le groupe, suivre le IRC channel, participer à une discussion sur GitHub ou en proposer une nouvelle, vous inscrire à la newsletter ou suivre le RICG sur Twitter.


Intéressé par le Responsive Web Design ? Retrouvez une liste des meilleurs articles et ressources du web.

Articles sur les mêmes thèmes dans la Cascade :

Images responsives : cas d’utilisation et snippets


Ressources complémentaires en anglais

Ressources complémentaires en français


original paru le dans Smashing Magazine.

Sur l'auteur : a grandi dans la banlieue de Urbana, Illinois, il a étudié le dessin et l'imprimerie à St. Louis, et il s'est établi au soleil de Denver, Colorado où il travaille pour une petite imprimerie depuis 2006. On peut le suivre sur Twitter et sur Google+.

Traduit avec l'aimable permission de Smashing Magazine et de l'auteur.
Copyright Smashing Magazine © 2014.