Les media-queries et le responsive design CSS3

Avec l’arrivée des smartphones et des tablettes, c’en est terminé des écrans grands et larges sur lequelles un site web peut afficher plusieurs informations côte à côte. Les téléphones ont de petits écrans et il convient d’adapter sa page en conséquences.

Les media queries sont là pour ça.

Le principe de base des media-queries

Si vous voulez faire un site spécialement pour les mobiles et dont la détection plante la moitié du temps, libre à vous. Mais sachez que vous pouvez vous en passer : le responsive design et les media queries rendent votre page lisible sur tous les écrans.

Ces dernières permet de cibler du CSS spécifiquement à une taille d’écran. Par exemple, le code suivant appliquera du CSS uniquement aux écrans plus petits que 1024 pixels de large :

@media (max-width: 1024px) {

    /* CSS ici */

}

Ainsi, mon blog vu sur mobile possède un affichage en mode portrait différent et mieux adapté que celui du mode paysage :

portrait paysage de mon blog

Il y a pas mal d’astuces pour le responsive design et je vais vous en montrer un maximum ici, en commençant par la base.

Quelques mots sur le « pixel-ratio »

Sur les mobiles, bien que les écrans soient beaucoup plus petits, les définitions des écrans sont proches de celles qu’on trouve sur les ordinateurs. Les écrans Full-HD affichent par exemple un écran d’une définition de 1920×1080 pixels, dans un rectangle de 12 cm de diagonales (5 pouces). Un ordinateur avec la même définition affichera la même chose dans un rectangle de prés de 43 cm (17 pouces). Le nombre de pixels par unité de surface est donc beaucoup plus élevée sur un téléphone.

À l’usage, la différence est très grande, et la lisibilité également. Si l’on ne fait rien, la page web s’affichera de la même façon sur un téléphone que sur un ordinateur, seulement en beaucoup plus petit :

vue de mon blog avec un mauvais pixel-ratio

Pour comparer, voilà un bon affichage :

vue de mon blog avec un bon pixel ratio

Il est évidemment toujours possible de zoomer, mais ce n’est pas pratique puisqu’une partie de la page (la colonne de droite par exemple) sortira de l’écran.

Il faut donc dire au navigateur que vu qu’il possède plus de pixels par unité de surface, il ne doit pas utiliser le même mode d’affichage qu’un écran d’ordinateur. Ainsi, au lieu d’utiliser par exemple 14 pixels pour afficher la lettre « M », il devra en utiliser 28.
Le nombre de pixels à utiliser pour une seule lettre a ici été doublé : on dit que le pixel-ratio est de deux.

En spécifiant un pixel-ratio de 2 le navigateur affichera la page comme si la définition de l’image était deux fois plus petite : de 1920×1080 pixels, on passe alors à 960×540 pixels. L’image est alors zoomé de façon optimale et le lecture de nouveau rendue lisible.

Notez qu’alors, si vous utilisez le CSS « @media (max-width: 1024px) », il faudra tenir compte du pixel-ratio que vous imposez. Si vous voulez que l’affichage soit différent selon l’orientation du téléphone, il faudra donc utiliser une valeur située entre 540 px et 960 px et non plus entre 1080 px et 1920 px.

Enfin, s’il est possible de spécifier explicitement la valeur du pixel-ratio dans le HTML ou le CSS, il est également possible (et recommandé selon moi) de laisser l’OS du téléphone calculer automatiquement le bon pixel-ratio pour le transmettre au navigateur. Il suffira dès lors de dire au navigateur d’afficher la page avec le pixel-ratio du système. Ceci se fait en ajoutant cette ligne dans l’entête du fichier HTML :

<meta name="viewport" content="initial-scale=1.0" />

En pratique cependant, je conseil ceci :

<meta name="viewport" content="initial-scale=1.0, user-scalable=yes" />

Le initial-scale=1.0 signifie au navigateur qu’il doit initialiser les dimensions en se basant sur le pixel-ratio du système, et le user-scalable=yes autorise l’utilisateur à zoomer s’il le souhaite. Mettre cette dernière variable à no empêcherait tout zoom, ce qui est mauvais pour l’ergonomie et l’accessibilité de la page.

L’importance du pixel-ratio est telle que l’ajout de cette seule ligne de HTML suffit en général à rendre un site beaucoup plus lisible sur de petits écrans.

Dans la suite, je considère que vous utilisez le code HTML ci-dessus dans vos pages.

Le pixel-ratio et le JavaScript

On la vu, si vous utilisez un code CSS pour un écran plus grand que 1000px de large, et que vous avez un écran en full-HD, le CSS ne sera jamais appliqué sur le pixel-ratio est de 2, car la taille de l’écran est donc divisée par 2 (la largeur passe de 1920 à 960 pixels).

Le CSS prend donc bien en charge le pixel-ratio, même pour les dimensions de l’écran. Ceci n’est pas toujours le cas du JavaScript. En effet, JavaScript, lui, continuera à voir l’écran comme ayant une largeur de 1920 pixels quelque soit le pixel-ratio de l’écran défini dans la balise meta.

De plus, tous les navigateurs n’ont pas le même comportement : ainsi, Opera Mobile 12x, le navigateur d’Android 4, Dolphin Browser afficheront la taille réelle de l’écran (c’est à dire sans tenir compte du pixel-ratio, soit 1920×1080 sur notre exemple d’écran HD), et les navigateurs Chrome 32, Firefox Mobile et Opera Browser 15+ tiendront compte de la division par deux des valeurs (c’est à dire qu’ils ne verront qu’un écran de dimensions 960×540).

Les grandes lignes dans la création d’un theme mobile

Placez les éléments les uns sous les autres

En général, un design d’une page web se présente sommairement de quatre éléments :

Comme ceci :

exemple de template basique

Sur mobile, la largeur de la page étant très limitée, surtout après l’application du correcteur du pixel-ratio, il faut linéariser au maximum la page lorsque vous détectez que l’écran n’est pas assez large : il faut placer les éléments les un en dessous des autres, la hauteur étant infinie, elle. Comme ceci :

exemple de template basique

Ceci est très simplement possible avec quelque chose comme ceci :

/* CSS Normal */
#sidebar {
   float: right;
   width: 200px;
}
#main {
   margin-right: 200px;
}

@media (max-width: 1024px) {
   /* CSS appliqué aux petits écrans */
   #sidebar {
      float: none;
      width: auto;
   }
   #main {
      margin-right: 0px;
   }
}

Bien-sûr, la mise en place d’un code CSS qui va rendre votre responsive est facilité si le site est déjà adaptatif d’origine. Si vous saviez un template à largeur fixe, il faut changer beaucoup plus de choses.

Gagnez de la place : réduisez les marges

Sur mobile, il faut favoriser la lisibilité : un utilisateur utilise en général son mobile pour avoir l’information à porté de main, et pas forcément pour tout le « bling-bling » autour.

Les marges larges et hautes rendent la lecture facile sur un ordinateur, mais réduisent beaucoup la taille sur les petits écrans. Réduisez les marges latérales (margin- et padding-) à leur minimum. Favorisez l’aération verticale : l’aspect aéré sera conservé. Pensez aussi à utiliser des hauteurs de ligne assez importantes : un téléphone étant tenu à la main, lire est plus difficile que sur un écran d’ordinateur qui lui est fixe : plus la lisibilité est importante, moins le lecteur se perd dans la page.

Tirez partie des capacités tactiles (avec JavaScript)

Sur l’ordi, on pouvait utiliser les effet de survol et de clic pour déclencher des événements en vue d’afficher un menu par exemple. Ceci est plus compliqué sur un écran tactile et parfois mauvais pour l’accessibilité : qu’est-ce qu’un clic sur un smartphone ?

En revanche, il est possible de faire plein de choses nouvelles sur un écran tactile, comme le « swipe » pour passer d’une page à une autre ou utiliser divers actions utilisant le multi-touch. Pensez-y quand vous faites un site mobile !

Quelques astuces particulières

Pour les images

Les images doivent avoir une hauteur et une largeur pour un rendu plus rapide de la page. Cependant, il est recommandé de mettre ces valeurs dans les attributs « height » et « width » de l’élément :

<img src="image.jpg" alt="lorem ipsum" height="320" width="640">

Ensuite, ajouter ceci au code CSS destiné aux petits écrans :

img {
    max-width: 100%;
    height: auto;
}

De cette façon, les images ne dépasseront jamais de la largeur de l’écran : si l’écran est plus petit que l’image, alors c’est l’image qui se redimensionne en conservant ses proportions.

Éviter le débordement des images de fond

Quand on utilise la propriété CSS background-image il se peut que vous utilisiez une image de la taille exacte du bloc auquel il est appliqué. Dans ce cas, si vous réduisez ce bloc pour qu’il soit affiché correctement sur un téléphone, l’image de fond est en partie masquée.
Pour remédier à cela, vous pouvez utiliser la propriété CSS3 background-size : celle-ci va alors décider de la façon dont l’image de fond occupe l’espace de son conteneur.

Voici quelque exemple : le bloc fait à chaque fois 350px par 200px, et l’image de fond fait 300px par 300px :

Notez que l’on peut également jouer avec background-position et background-repeat pour obtenir d’autres effets.

Changer l’organisation des tableaux à un seule entrée

Cette astuce consiste à transformer les tableaux en blocs, et donc de linéariser la disposition des blocs (un exemple est disponible ici) :

Sur un écran large, l’affichage est normal et en forme de tableau :

le tableau est affiché normalement

Sur un écran de mobile, quand l’affichage normal ne permet plus de placer toutes les cellules du tableau côte à côte, on linéarise le tableau :

le tableau est linéarisé

Le code CSS est facilement compréhensible :

table, tbody { display: block; }
tr { display: table; }
td { display: table-row; }

Changer l’organisation des tableaux à deux entrée

Ici, une solution (un peu moins propre) peut-être de masquer certaines colonnes à de moindre importance au fur et à mesure que la page est rétrécie. Certes, on masque de l’information, mais au moins ce reste lisible ce qui est visible.

le tableau est réduit à son minimum

Au nouveau du code, il suffit de masquer des cases quand on réduit l’écran :

@media (max-width: 600px) {
    table tr th:nth-of-type(2),
    table tr td:nth-of-type(2) {
        display: none;
    }
}

@media (max-width: 500px) {
    table tr th:nth-of-type(3),
    table tr td:nth-of-type(3) {
        display: none;
    }
}

@media (max-width: 400px) {
    table tr th:nth-of-type(4),
    table tr td:nth-of-type(4) {
        display: none;
    }
}

N’oubliez pas de changer les numéros des cases à masquer par celles qui sont les moins importantes.

Remplacer les flottants par un affichage enligne-bloc

Les éléments flottants sont très utilisés sur un site web, par exemple dans les menus : chaque élément de menu est placé en flottant à gauche. Ceci n’est pas pratique quand on redimensionne la page : les éléments sortent de leur cadre et masquent le contenu suivant.

ul.menu {
    height: 40px;
}

ul.menu li {
    float: left;
    height: 40px;
    width: 100px;
}

le menu n’est pas bon

À la place, il ne faut pas donner de hauteur au conteneur (au besoin une hauteur minimale), supprimer le flottant et les afficher en inline-block. Le bloc menu est alors automatiquement agrandit, et le contenu qui vient après est repoussé sans être masqué :

ul.menu {
}

ul.menu li {
    display: inline-block;
    width: 100px;
    height: 40px;
}

le menu est bon

En général, je déconseille l’usage des flottants pour tout ce qui est éléments du design : préférez un positionnement absolu à la place : on peut penser que le positionnement est plus difficile à comprendre et à employer que les flottants, mais c’est l’inverse : les flottants sont un type de disposition très particulier avec beaucoup de pièges.

Briser les lignes trop longues

Si vous avez une ligne de texte sans espace, alors elle restera sur une seule ligne, même si elle dépasse de son conteneur. Si l’écran est trop petit, cette ligne peut casser tout le design de votre site. Il y a pourtant des méthodes en CSS pour forcer le retour à la ligne :

prévisualisation du word-wrap

body { word-wrap: break-word; }
pre { white-space: pre-wrap; } /* pour firefox et les <pre> */

Le texte ne déformera alors plus les blocs, mais sera remis à la ligne.

Des tailles de polices proportionnelles à la taille de l’écran

Pour les tailles de polices, on connaît les pixels, les em, les en ou les %. Mais il y en a maintenant des nouvelles. Une d’elles permet de faire en sorte que la taille de police soit proportionnelle à la taille de l’écran : ce sont les unités vw, vh, vmin et vmax (v comme viewport, w pour la largeur, h pour la hauteur).

L’avantage avec ça, c’est que le texte aura toujours la même proportion par rapport à la taille de l’écran, sans glisser sur plusieurs lignes :

prévisualisation de la police proportionnelle à la taille de l’écran

Préférez min-height à height pour des hauteurs dynamiques

Si vous avez un bloc de texte dont vous réduisez la largeur, c’est sa hauteur qui s’agrandira pour contenir tout le texte. Il ne faut donc jamais donner une hauteur fixe à un bloc de texte en responsive-design, sous peine de voir le contenu sortir de son cadre.

Utilisez plutôt une hauteur minimale : la hauteur sera alors au moins de cette valeur, mais augmentera pour tout contenir quand le texte aura subitement besoin de plusieurs lignes pour s’afficher. Ceci est particulièrement vrai pour les titres et les menus auxquelles on a toujours tendance à appliquer une hauteur fixe.

Le pixel-ratio et les images

Si vous utilisez des images de fond en CSS, il faudra tenir compte du pixel ratio aussi : si votre écran a un pixel ratio de 2, une icône de 16 pixels sera étirée jusqu’à 32 pixels. C’est bien, car sinon les icônes sont bien trop petites, mais l’image s’en retrouve pixelisée, comme vous pouvez le voir sur cette capture :

exemple d’icônes altérées

À gauche, c’est sur l’ordinateur avec un pixel-ratio de 1. À droite, c’est sur un mobile avec un pixel-ratio de 2.
Les icônes du haut sont dans leur taille normale, et les icônes du bas sont de résolution double (pixel-ratio de 2) mais affichées comme un pixel-ratio de base.

On voit tout de suite que l’icône sur mobile est bien plus jolie : elle est plus nette. C’est normale car en réalité elle fait 32 pixels et est du coup affichée sans être étirée. Sur l’ordinateur, elle est rétrécie, mais ceci n’altère pas la qualité de l’image de façon visible.

Afin d’arriver à ce résultat, il faut évidemment utiliser une icône plus grande dès le départ : 32 pixels, dans notre exemple.
Ensuite, on utilise très simplement du CSS pour la réduire à la taille de l’icône :

#bouton-16 {
    width: 16px;
    height: 16px;
    background: transparent url(icon32.png) no-repeat;
    background-position: 0px 0px;
    background-size: 16px 16px;
}

Que l’on peut condenser en :

#bouton-16 {
    width: 16px;
    height: 16px;
    background: transparent url(icon32.png) no-repeat 0px 0px / 16px 16px;
}

(attention, avec ça, la position et le size doivent se suivre et être séparés par un slash)

L’icône, le fichier, mesure bien 32 pixels, mais avec background-size on la rapporte à 16 pixels : le pixel-ratio de l’image sera alors de 2 et elle restera nette même sur les mobiles. Si vous prévoyez d’utiliser l’application web sur des écrans avec un pixel-ratio encore plus grand, prévoyez des images 3 voire 4 fois plus grande (donc 48px ou 64px).
Faites gaffe cependant : l’image sera plus grande et donc aussi plus lourde. Mais je pense qu’il est préférable de charger une seule image pour tous les appareils plutôt que spécifier des images différentes selon les appareils : dans la plupart des cas, la taille des fichiers ne sera pas beaucoup plus grande (perso je suis passé de 9 ko à 13 ko, ce qui est négligeable devant l’intérêt de l’astuce).

Cette méthode fonctionne aussi avec la méthode des portes coulissantes pour les images avec plusieurs icônes dedans. Si votre image avec les sprites mesure par exemple 100px sur 32px, déclarez une size de 50px par 16px. La position doit être effectuée comme si l’image mesurée réellement 50×16px, donc comme la taille en CSS, pas la taille du fichier-image.

L’effet du :hover sur un mobile

Sur un navigateur mobile, que ce soit Firefox Mobile ou Opera, ou même les autres, un effet de « :hover » est déclenché avec le tapotement sur l’écran. Si vous avez un menu déroulant, il est possible de dérouler en tapant dessus, tout simplement.

Il y cependant un petit bug qui apparaît : si le menu contient des liens ou des boutons cliquables, alors le « tapotement » pour le :hover peut accidentellement se propager sur un lien de la liste, ce qui n’est pas pratique pour naviguer.

Il y a une petite astuce cependant : délayer le déroulement du menu. On peut le faire en JS, perso je le fais en CSS avec les transitions.

Si vous utilisez un effet d’affichage basé sur le display: none/block, ceci ne marchera pas. En revanche, si vous utilisez quelque chose basé sur le right: -9999, alors ça marchera !

Il suffit d’appliquer une transition de durée 0s sur le right, mais délayée d’une petite durée :

#nav:hover ul {
	transition: 0s linear .05s;
}

Ici, l’apparition du menu est bien direct, mais différé de 50 millisecondes. Ce délai est suffisamment faible pour être imperceptible, mais sa présence est néanmoins suffisante pour éviter un clic accidentel sur un lien. La valeur de 50 millisecondes provient de mes propres tests, vous pouvez essayez avec d’autres valeurs si vous voulez..

À ajouter dans ce tuto...

// TODO-List

– utiliser les formulaires HTML5 et les attributs bien utiles.

Page créée en janvier 2014. Mise à jour le mardi 7 août 2015
Adresse de la page : http://lehollandaisvolant.net/tuto/responsive-css/