mozilla

The building blocks of responsive design

Cette traduction est incomplète. Aidez à traduire cet article depuis l'anglais.

Dans cet article, nous allons discuter des principales composantes essentielles de la conception adaptée, avec des liens vers des informations suppléméntaires si nécessaire.

Pour les développeurs web, il est maintenant assez commun d'être appelé pour créer un site web ou une application qui change son interface utilisateur en fonction du navigateur ou du dispositif accédant au site, pour fournir une expérience optimisée. Une première approche pour cela consiste à créer différentes versions de votre site/application pour différentes plates-formes ou navigateurs et les générer de façon appropriée après la détection de la plate-forme ou du navigateur qui se connecte à votre site. Cependant cette méthode est de plus en plus inefficace : la détection de navigateur amène facilement à des erreurs, et la maintenance de plusieurs versions de votre code peut s'avérer un cauchemar.

Il est généralement bien préférable de créer une version unique de votre code qui ne se soucie pas du navigateur ou de la plateforme qui accède au site, mais utilise plutôt des fonctions de test pour savoir quel code est pris en charge par le navigateur ou quelles valeurs de certaines fonctions du navigateur le sont, puis ajuste le code en conséquence. On appelle ça le responsive design ou la conception adaptative, deux approches différentes mais connexes. Pour une discussion sur les différences entre les deux , lire Responsive design versus adaptive design.

C'est un moyen plus sûr, plus facilement maintenable, et plus stable sur le long terme. Vous n'avez pas besoin de construire de nouvelles versions du site à chaque nouveau navigateur ou nouvelle plateforme, et à ajuster le code à chaque support différent des fonctionnalités des navigateurs existants.

Il y a aussi des désavantages à cette approche. Si le contenu, le format et les fonctionalités ont besoin de changer énormément à chaque appareil différent, ce n'est sûrement pas la meilleure approche. De plus, prendre un site existant et le modifier pour le rendre responsive, pour le rendre agréable sur mobile ou tablette, peut demander plus d'efforts que de simplement créer une version mobile ou une application séparée, surtout si c'est un vaste site d'entreprise. Plus d'info sur les avantages et désavantages du responsive design.

Grilles fluides

La meilleure façon de commencer est d'obtenir des mesures fluides pour le rendu de notre application, qui consiste principalement à utiliser une combinaison de pourcentage et de ems/rems pour ajuster la taille de votre conteneur et du texte, et non pas avec des largeur fixées, par exemple, en pixels. Cela présente de nombreux avantages dont le fait que la forme va s'adapter aux différentes dimensions de viewport. Prenons un exemple.

Nous avons créé un prototype simple, mais fun, d'une application appelée Snapshot, qui prend un flux vidéo de votre webcam (en utilisant getUserMedia()) et vous permet ensuite de capturer des images de ce flux vidéo (en utilisant la fonction HTML5 <canvas>), et d'y sauvegarder dans la Galerie. Vous pouvez ensuite regarder les images précédemment capturées et les supprimer. D'autres articles viennent détailler les fonctionalités de cette application, mais nous nous intéresserons ici à la forme de celle-ci.

Note : vous pouvez trouver l'application Snapshot sur Github, regarder le code et aider à l'améliorer. Vous pouvez aussi regarder Snapshot tourner en live. Notez que getUserMedia() est une technologie expérimentale, qui ne fonctionne que sur Google Chrome et Firefox pour ordinateur de bureau. Le support sur Firefox OS est prévu sur la version 1.3. Davantage de fonctionalités et une refonte du style de Snapshot sont prévus dans le futur

Notre application pour bureau de Snapshot est composée de trois colonnes contenant respectivement: une vue de la caméra, une vue de la capture d'image, et une galerie.

Le balisage est très simple:

<x-deck selected-index="0">
  <x-card>
    …
  </x-card>
  <x-card>
    …
  </x-card>
  <x-card>
    …
  </x-card>
</x-deck>

Note : ces étranges éléments x- peuvent ne pas vous être familier. Ils font partie de Brick, une collection de composants d'interface de Mozilla pour les application mobiles web. Nous avons utilisé Brick pour créer l'interface mobile pour Snapshot, nous en parlons un peu plus ci-dessous.

Pour les avoir côte à côte nous utilisons les règles suivantes:

x-card {
  width: 100%;
}

x-card:nth-child(1), x-card:nth-child(2) {
  width: 30%;
  float: left;
  padding: 2rem;
}

x-card:nth-child(3) {
  width: 40%;
  float: left;
  height: 100%;
  overflow: auto;
  padding: 2rem;
}

Nous donnons aux deux premières colonnes une propriété width de 30%, et le troisième une propriété width de 40%, mettant en flottant à gauche toutes les colonnes. Ainsi elles restent côte à côte, et leur proportions restent inchangées quelles que soient les variations de largeur de la fenêtre. Ce n'est qu'un exemple de grille fluide, mais vous pouvez très bien appliquer ce principe à des modèles de grilles plus complexes.

Dimensionnement avec border-box

Le padding n'affecte pas la hauteur et la largeur générale des conteneurs car nous avons mis les propriétés box-sizing a border-box:

*, *:before, *:after {
  -webkit-box-sizing: border-box;
  -moz-box-sizing: border-box;
  box-sizing: border-box;
}

Cela signifie que width et {cssxref("height")}} incluront la bordure dans leur définition de la taille et non pas juste le contenu. Donc si vous mettez width: 40%; la boite sera toujours à 40% de la taille de son parent, et padding ainsi que border seront soustraits à la taille du contenu, non ajoutés. Vraiment utile ! Plus d'informations sur * { Box-sizing: Border-box } FTW, par Paul Irish.

Repositionnement d'éléments flexibles

Les choses fonctionnent plutôt bien jusqu'à maintenant, mais il subsiste encore quelque problèmes qui attendent d'être résolus. Pour commencer, regardons ce qui se passe si nous incluons <video> et <img> dans nos deux colonnes, sans aucun style.

Parce que la taille des éléments est dictée par la taille du media à l'intérieur, et que le média possède une taille fixe, ils sortent de leur élément conteneur et détruisent la disposition d'affichage. C'est plutôt horrible, mais généralement ce genre de problème peut facilement être résolu à l'aide d'une simple règle CSS:

img, video {
  max-width: 100%;
}

Cette règle oblige l'élément à rester dans les dimensions de son conteneur, quel qu'il soit. Cependant si cet élément n'est pas aussi grand que son conteneur, il ne va pas s'étirer pour le remplir. Pour l'exemple de SnapShot, nous utilisons un code légèrement différent:

x-card:nth-child(1) video, x-card:nth-child(2) img {
  width: 100%;
    …
}

Car dans notre cas, nous voulons en fait que la vidéo et l'image remplissent toujours leur conteneur quel qu'il soit, une différence subtile mais importante par rapport à max-width, et gardera ainsi toujours la même taille. La vidéo se redimensionne toujours dynamiquement, mais l'écran de de l'image capturée ne l'est pas. De ce fait lors du redimensionnement de l'écran on peut se retrouver avec des éléments de tailles différentes lors de l'utilisation de max-width: 10%; comme:

Media queries

Les grilles fluides sont un bon début, mais vous noterez qu'à certains points (connus sous le nom de breakpoints) la disposition commence à se casser. À ce moment-là, vous aurez envie de modifier la disposition pour empêcher ce problème, et ceci peut être fait en utilisant les media queries.

Note : les Media queries sont une fonctionalité CSS3 qui vous permettent de sélectionner quelles règles CSS appliquer selon les résultats des tests sur le média. Pour plus d'informations, lire Media queries.

Positionnement bureau classique

Dans notre exemple, nous avons un un positionnement de bureau, comme nous le savons déjà. Ceci est fait en utilisant des règle CSS incluses en haut de notre feuille de style, avant tout media queries.

Positionnement mid-width

Nous avons aussi un positionnement mid-width, pour les tablettes et les ordinateurs aux écrans étroits. Ceci est réalisé à l'intérieur de notre première media query :

@media all and (max-width: 1024px) {
  x-card:nth-child(1), x-card:nth-child(2) {
    width: 50%;
  }

  x-card:nth-child(3) {
    width: 100%;
    clear: left;
  }
 
  x-card:nth-child(3) img {
    width: 20%;
  }
}

Ici nous altérons la largeur de nos colonnes et nous supprimons le flottement de la troisième colonne (ainsi qu'un clear pour éviter tout soucis de flottement). Nous avons aussi modifié la largeur des images dans le troisième conteneur (qui n'est plus une colonne, mais une galerie) pour en avoir cinq par ligne (contre trois précédemment).

Positionnement pour mobile/écran étroit

Nous avons ensuite un positionnement pour les écrans étroits, convenant à une application mobile ou une application Web ouverte (par exemple, une application Firefox OS). Ceci est fait en plusieurs parties. Premièrement, comme prévu, il y a une media query dans notre CSS principal, qui est assez lourd, nous allons donc l'expliquer en plusieurs parties.

@media all and (max-width: 480px) {  
  x-card:nth-child(1), x-card:nth-child(2), x-card:nth-child(3) {
    width: 100%;
    float: none;
    padding: 0;
  }
 
  button {
    margin-top: 0;
    border-radius: 0;
  }
 
  x-card:nth-child(1) video, x-card:nth-child(2) img {
    border-radius: 0px;
    border: none;
    padding: 0;
    background-color: 0;
  }

Ce premier bloc reconfigure un certain nombre d'éléments par rapport au positionnement pour écran classique qui ne sont plus requis pour l'application mobile.

  x-card:nth-child(1) video, x-card:nth-child(2) img, x-card:nth-child(3) {
    margin-top: 17.5vw;
  }
 
  x-card:nth-child(1) button, x-card:nth-child(2) button {
    position: absolute;
    bottom: 0;
  }
 
  x-card:nth-child(2) button:nth-of-type(2) {
    bottom: 5.9rem;
  }
  
  x-card:nth-child(1) button {
    font-size: 7vw;
  }
 
  x-card:nth-child(2) button {
    font-size: 7vw;
  }

Les règles suivantes redimensionnent les boutons dans les deux premiers éléments, et donnent à tous les contenus de ces éléments une marge haute pour ne pas perdre le contenu sous les boutons de navigation (voir ci-dessous). Ceci est nécessaire car Mozilla Brick (voir ci-dessous aussi) force les composants à être à 100% de la hauteur/largeur de l'écran. Nous utilisons les unités vw (viewport width) pour cela. 1 vw est équivalent à 1% de la largeur du viewport. Ceci permet aux dimensions de se redimensionner correctement avec la largeur du viewport. Finalement, nous avons placé de façon absolue les boutons en bas de leur cadre, donc le positionnement semble OK a différentes variations de viewport. Nous ajoutons ensuite une règle qui positionne le second bouton dans plus haut dans le cadre. Lorsque vous cliquez sur une image dans la galerie, les options pour supprimer l'image ou quitter apparaissent, et nous ne voulons pas que les boutons se placent l'un au-dessus de l'autre.

x-card:nth-child(3) img {
  width: 50%;
}

Cette règle change simplement la largeur de la galerie d'image pour en avoir deux par ligne.

  nav {      
    width: 100%;
    position: absolute;
    z-index: 1000;
     
    display: -webkit-flex;
    display: -moz-flex;
    display: -ms-flexbox;
    display: flex;
  }
 
  nav button {
    font-size: 6.8vw;
    
    -webkit-flex: 1;
    -moz-flex: 1;
    -ms-flex: 1;
    flex: 1;
    
    border-left: 1px solid rgba(100,100,100,0.4);
  }
 
  nav button:first-child {
    border-left: 0;
  }
}

Dans ce dernier lot de règles, nous changeons la valeur display de <nav> à flex pour le montrer (il était réglé à none dans le CSS par défaut en haut de notre feuille de style, puisqu'il n'était pas utilisé dans les autres vues). Nous utilisons ensuite le positionnement absolu et z-index pour ne mettre aucun écart dans le document, et le placer en haut des x-cards ( c'est pourquoi nous avons donné un propriété top-margin aux x-cards plus tôt).

Ensuite, le font-size des boutons est reglé à 6.8vw. Pourquoi ? Parce que le top-margin de x-cards à été mis à 17vw plus tôt. Tous les boutons de l'applications ont été réglés pour avoir une line-height de 2.5 en haut de la feuille de style CSS par défaut (vous pouvez vérifier si vous ne me croyez pas). Et 6.8 * 2.5 = 17.

Ensuite, nous utilisons flex: 1; pour faire en sorte que les boutons utilisent tous le même espace sur la ligne. Regardons d'un peu plus près le positionnement, dans l'image ci-dessous.

single column layout for mobile app view, with three buttons to navigate between cards, an image viewer, and a Save Picture button at the button.Mais nous avons de nouveaux tours dans notre manche pour la forme de cette application mobile ! Comme mentionné ci-dessus,  nous utilisons Mozilla Brick, une collection de composants d'interface mobile prêts à l'emploi, pour composer l'application mobile. Nous utilisons en particulier le composant deck pour le bel effet de transition entre les cadres quand un bouton est pressé. Pour plus d'informations sur Brick, lire Mozilla Brick: ready made UI components.

Ce qui est plus pertinent pour cet article est que nous ne voulons pas que Brick CSS ainsi que les fichiers JavaScript soient appliqués au balisage tant que ne sommes pas dans la vue mobile. Pour faire cela, nous appliquons Brick CSS à la page en utilisant un <link> séparé avec un attribut media:

<link href="dist/brick.css" type="text/css" rel="stylesheet" media="all and (max-width: 480px)">

Cet attribut indique que la feuille de style ne doit pas être ajoutée  l'HTML tant que la largeur de viewport est de 480px ou moins. Passons au JavaScript, les éléments <script> n'acceptent pas les attributs média, donc nous devons procéder différemment. Heureusement il existe une fonction appelée window.matchMedia(), qui peut faire tourner ou non selon la condition le Javascript selon qu'une media query retourne true ou non. Nous ouvrons brick.js et englobons le tout dans la condition qui suit :

if (window.matchMedia("(max-width: 480px)").matches) {
  // The whole of brick.js goes here!
}

Ceci fait que rien ne se passe dans brick.js tant que la largeur du viewport est supérieur à 480px. Problème résolu.

Écrans très larges

Vous aurez peut-être remarqué que quand le viewport devient très grand (comme sur un affichage cinéma), la disposition arrête de s'agrandir, et ne fait que se recentrer dans l'espace disponible. C'est assez simple à faire. Vous pourriez utilisez une média query avec min-width pour régler la largeur de <body> à un certain point:

@media all and (min-width: 1400px) {
  body {
    width: 1400px;
    margin: 0 auto;
  }
}

Mais il est plus simple de faire cette règle à la place, et de ne pas s'embêter avec un media query:

body {
  max-width: 1400px;
  margin: 0 auto;
}

Problème d'orientation

Nous sommes face à certains problèmes avec l'orientation : l'agencement mobile de notre application est conçue pour une orientation en portrait, et parait horrible sur un appareil en format paysage. Pour réparer cela, nous ajoutons une media query qui n'applique sa règle sur le contenu que si l'appareil est en format paysage :

@media all and (max-width: 480px) and (orientation: landscape) {
  nav {   
    width: auto;
    
    -webkit-flex-direction: column;
    -moz-flex-direction: column;
    -ms-flex-direction: column;
    flex-direction: column;
  }
 
  nav button {
    font-size: 6.8vh;
  }
 
  nav button {
    border-left: 0;
  }
 
  x-card:nth-child(1) video, x-card:nth-child(2) img, x-card:nth-child(3) {
    margin-top: 0;
  }
 
  x-card:nth-child(1) button, x-card:nth-child(2) button {
    font-size: 2rem;
  }
}

Voici ce que ça fait:

  • Ajuster les boutons nav, changer la direction du flexbox dans laquelle ils sont disposés, et modifier la taille de police et les bords pour les faire rentrer verticalement plutôt qu'horizontalement.
  • Supprimer les marges en haut du contenu de x-card pour ne pas finir avec écart en haut de l'écran en mode paysage.
  • Changer la taille des boutons de contrôle (comme Take Picture, Delete Photo) pour qu'ils n'aient pas l'air trop gros.

Et l'image suivante donne le rendu de ces modifications:

Note: Une autre solution pour respecter l'orientation pourrait être de bloquer l'orientation de votre application au mode portrait ou paysage. Si vous travaillez sur une application installée ou sur Firefox OS, vous pouvez le faire facilement avec le champ d'orientation du manifeste. Si vous désirez une solution fonctionnelle sur les applications web en général vous pouvez utiliser l'API Screen Orientation, et/ou envoyer un message pour demander à l'utilisation de tourner leur écran s'ils utilisent la mauvaise orientation (par exemple, si window.innerWidth est plus grand que window.innerHeight on peut supposer que le jeu est en mode paysage et on montre donc un message "Veuillez tourner l'écran".)

Viewport

A dernier problème à résoudre pour notre application d'exemple concerne les navigateurs mobile et les media queries. Si nous avions vu mon exemple dans un navigateur mobile dans l'état présent, nous n'aurions pas vu notre beau positionnement mobile. Mais plutôt l'image ci-dessous.

Vous serez d'accord avec moi pour dire que ce n'est pas tout à fait ce que nous voulions n'est-ce pas? En bref, les navigateurs mobile mentent. Ils ne donnent pas un rendu de page web avec leur véritable largeur de viewport. Au lieu de cela, ils le font avec une largeur plus haute (quelque chose approchant l'écran d'ordinateur portable), and le rétrécissent pour rentrer dans l'écran mobile. C'est un méchanisme de défense sensible, la plupart des vieux sites qui n'ont pas de media queries aurait l'air horrible avec un rendu direct, 320px par 480px. Mais cela ne nous aide pas nous développeurs web, qui avons écrit des dispositions pour petit écran dans notre CSS en utilisant des media queries et qui voulont que l'application affiche ceux-là!

Il existe un moyen d'outrepasser ce comportement mobile: les viewports, qui sont insérés dans nos pages HTML sous la forme d'une balise <meta>. Dans mon exemple, ceci dans notre <head> HTML:

<meta name="viewport" content="width=480">

Cela oblige le navigateur à positioner notre webapp correctement. width=480 dit au navigateur "donne une apparence de 480 pixels de largeur", alors que les media queries ne le font pas correctement. Il existe de nombreuses options disponible pour la balise meta viewport, sur laquelle vous pouvez vous documenter en vous rendant sur Utiliser la balise meta viewport pour contrôler la mise en page sur les navigateurs mobiles.

Note: Il existe une spécification pour le device adaptation, qui définit les mêmes fonctionalités mais en CSS, en utilisant la règle @viewport . C'est probablement un lieu plus adapté pour indiquer ce genre d'informations, mais cette spécifications n'est pas aussi bien supportée que la balise meta viewport, donc vous devriez en rester sur celle-ci pour le moment.

Images/vidéos adaptatifs

Un problème de plus en plus récurrent ces derniers temps est le poids des images/vidéos (poids en Ko) responsive et les dimensions de l'image sur l'écran. Vous voulez que les images soient contenues dans l'Interface Utilisateur de l'application que l'on soit sur ordinateur ou sur mobile, mais vous devriez aussi considérer le fait que les application mobiles possèdent des dimensions plus petites que sur ordinateur, donc vous devriez rendre les images plus légères à télécharger. Les mobiles en général (selon des endroits dans le monde) possèdent une bande passante plus faible et ont moins de mémoire disponble que les ordinateur de bureau, donc oui, c'est kilo-octets en plus comptent.

Une autre difficulté vient avec les écrans à très grande résolution, les graphismes conçues pour les petites résolutions risquent d'apparaitre en petit quand elle sont affichées sur un écran à haute résolution. Les appareils appliquent donc souvent un facteur de zoom par défaut pour le rendu des pages pour éviter ces problèmes. Le problème avec ça, c'est que les images zoomées ainsi peuvent apparaitre pixelisées.

Images de fond CSS

Les paragraphes ci-dessous ne sont pas encore traduits.

For CSS background images this is a fairly easy problem to solve. If you use the mobile first methodology, you will be creating your mobile layout inside your default CSS, before any media queries have been applied. The media queries then supply CSS that is only applied to the markup when the viewport is above a certain width. Let's look at a quick example:

header {
  height: 300px;
  width: 100%;
  background: url(images/small-header.jpg) center;
}

@media all and (min-width: 480px) {
  header {
    background: url(images/large-header.jpg) center;
  }
}

This means that mobile browsers only download the mobile background image asset — not the desktop mobile assets — because they fail the media query tests and therefore ignore the media queries. You can also serve a larger graphic to a higher resolution device using a resolution media query, like so:

button {
  background: url(images/low-res-header.jpg) 1rem center ;
}

@media only screen and (-webkit-min-device-pixel-ratio: 2),
       only screen and ( min-resolution: 192dpi),
       only screen and ( min-resolution: 2dppx) { 
  button {
    background: url(images/high-res-header.jpg) 1rem center ;
  } 
}

This looks rather complicated, but really it's not — we are providing a number of media query options, as at this time different browsers support different resolution media query types and even units. Brett Jankord has a good explanation at Cross Browser Retina/High Resolution Media Queries.

<video>

HTML5 video is fairly well catered for in terms of responsive capabilities. If you wish, you can point to multiple video files via <source> attributes, each with their own source and MIME type:

<video controls>
  <source src="videos/720/crystal720.mp4" type="video/mp4">
  <source src="videos/720/crystal720.webm" type="video/webm">
</video>

But you can go one step further. You can include media attributes on the <source> element containing media queries — the video loaded in the browser will depend on both the format the browser supports, and the results of the media tests. So for example:

<video controls>
  <source src="videos/320/crystal320.mp4" type="video/mp4" media="all and (max-width: 480px)">
  <source src="videos/320/crystal320.webm" type="video/webm" media="all and (max-width: 480px)">
  <source src="videos/720/crystal720.mp4" type="video/mp4" media="all and (min-width: 481px)">
  <source src="videos/720/crystal720.webm" type="video/webm" media="all and (min-width: 481px)">
</video>

This allows your site to serve different video files based on the available space, in order to optimize the user's experience.

<img>

HTML images are a more difficult proposition. There is no mechanism inherent in HTML images for serving different image files dependent on viewport size, and, due to a number of irksome browser behavior realities, solutions are more difficult to hack together than you would imagine. There are currently some standards proposals in the works that would provide this — the W3C responsive images community group discussed this problem for ages and arrived at the <picture> element, which provides a similar markup structure to <video>, with <source> alternatives selectable via media query results. Another proposal, srcset, was put forward by Apple and takes a slightly different approach, instead providing a new srcset attribute for <img> inside which image references are placed along with "hints" that the browser can use to work out which image is most suitable to display given its viewport size, resolution, etc. These are not intended to be mutually exclusive.

This all sounds good. But those solutions are definitely not ready for production yet — both are in a very early stage of standardization, and have no support across browsers. Currently we have to rely on various polyfills and other solutions, none of which are perfect for all situations, so you need to decide which one is right for your particular situation. Some available solutions are as follows:

HiSRC
A jQuery plugin that allows you to create small, medium, and large versions of an image, and then serves the appropriate one according to the browser's resolution and available network speed.
Mobify.js capturing
A very clever technique from Mozilla that allows you to capture the source of the page before it's parsed. This way, you can swap out image src values with JavaScript depending on browser features, circumventing browser preloading issues. This is promising, but doesn't work very well across older browsers.
Picturefill
A JavaScript-based polyfill for <picture>, which works nicely, but it does require a lot of custom markup.
Adaptive images
A server-side solution, which records the viewport size in a cookie, then resizes images via a combination of PHP and .htaccess to a more appropriate size, if appropriate. This doesn't require markup or scripting, but has a number of limitations.

SVG and other vector graphics

For some image requirements (not photographs, but icons and user interface elements are a good fit), a good solution is to use vector graphics. Because vector images are calculated based on mathematical algorithms rather than containing separate data on every pixel in the image, they tend to be smaller in file size, and are infinitely scalable when zoomed or viewed on high resolution devices (at least, in theory). Some ideas follow, which also help to keep the number of HTTP requests down — another key factor in mobile app performance:

  • You should try to use CSS3 features to programmatically generate graphical effects where possible, rather than relying on image files. these include rounded corners, gradients, and drop shadows. These will scale as the resolution changes or the browser zooms, and although they are not supported very well on older browsers such as Internet Explorer 6-8, this is not too much of a concern when you are creating an interface aimed at modern devices such as Firefox OS, and they also tend to gracefully degrade.
  • You could also try using SVG to create interface elements. SVG produces vector graphics and is supported well across modern browsers, with polyfills available for older browser support.
  • Using Web fonts for displaying icons is an effective technique for keeping file size and HTTP requests down, and this is supported well across modern and older browsers.

See also

Étiquettes et contributeurs liés au document

Contributeurs à cette page : Goofy, robertino, stephaniehobson, diomabb, Ilphrin
Dernière mise à jour par : Ilphrin,