Transformer un layout avec Flexbox

Pour comprendre flexbox en profondeur, Zoe Gillenwater transforme un layout préexistant créé avec table. Grâce aux media queries et aux possibilités d'ordonnancement de flexbox, ce layout devient encore plus responsif.

Par

Nous allons apprendre à utiliser flexbox pour fixer deux éléments à deux extrémités du viewport, avec un titre centré au milieu. Grâce aux media queries et aux possibilités d’ordonnancement de flexbox, nous pourrons rendre ce layout encore plus responsif.

Une bonne façon d’apprendre une nouvelle technologie est de prendre une chose créée avec les méthodes existantes et de le recréer avec les nouvelles méthodes. Philip Walton nous a montré dans ses démos “Solved by Flexbox” que flexbox nous permettait de créer beaucoup plus facilement beaucoup de choses qui étaient possibles mais laborieuses avec les méthodes CSS traditionnelles, tout en offrant plus de flexibilité aux contenus et aux scénarios utilisateurs. Je voudrais vous proposer un défi similaire : prendre un layout existant, et voir comment flexbox peut l’amener à un autre niveau.

Le design que nous allons convertir provient de l’article de Dan Mall sur son redesign du site de Crayola — et plus particulièrement les captures d’écran pleine page montrant comment le layout peut s’adapter, du plus petit au plus large.

image
La mise en page de Dan Mall, avec un smartphone fixé à gauche et un grand écran fixé à droite, alignés par le bas, et un texte au milieu.

Lorsque j’ai vu cet écran pleine page avec deux contenus épinglés de part et d’autre et un bloc au milieu, j’ai tout de suite pensé à justify-content:space-between de flexbox, et j’ai décidé d’essayer de recréer ce layout avec flexbox. Attention, il n’y a rien d’erroné dans le design de Dan, c’est simplement un défi que je me suis lancé pour apprendre : comment pourrait-on faire différemment, et quels sont les avantages et les inconvénients de chaque méthode.

La version display:table

Dan utilise le positionnement absolu, donc pas besoin de me lancer là-dedans. Une autre option que je voulais comparer avec flexbox était la mise en page display:layout, qui est souvent une bonne solution de repli pour les mises en page flexbox, surtout les layouts plein écran. Pour que display:table fonctionne, il m’a fallu mettre la colonne la plus à gauche en premier dans mon HTML, puis la colonne du milieu, puis la dernière.

// html
<div class="layout-table">
  <figure class="stretch-photo small">
    <img src="img/flexbox_stretch_small.png" width="58" height="127" alt="mobile"/>
  </figure>
  <h2 class="stretch-title"><span>looks great from here to there</span></h2>
  <figure class="stretch-photo big">
    <img src="img/flexbox_stretch_big.png" width="361" height="294" alt="desktop"/>
  </figure>
</div>

Cet ordre des sources n’est pas idéal car le texte du milieu est, d’un point de vue sémantique, un titre pour les deux images. Le résultat ne sera pas bon pour l’accessibilité, les utilisateurs de technologies comme les lecteurs d’écrans ne comprendront pas comment s’organise la page. C’est malheureusement souvent ce à quoi nous contraint la technique du display:table. Mais si vous ne pouvez pas avoir un HTML parfait, au moins vous pouvez avoir un joli CSS !

//CSS
.layout-table {
  display: table;
  width: 100%;
}
.layout-table > * {
  display: table-cell;
  vertical-align: bottom;
}
.layout-table .stretch-title {
  width: 100%;
  padding-bottom: 4rem;
}
.layout-table .stretch-title span {
  display: block;
  border-bottom: 1px solid #ccc;
}

Ci-dessous, voici à quoi ressemble ma version table-layout. Je ne cherchais pas à reproduire à l’identique la version de Dan, simplement les éléments basiques de la mise en page. Vous remarquerez que dans ma version le texte est centré entre les deux images alors que dans celle de Dan il est centré par rapport aux blocs de texte au-dessus et en-dessous de lui. Ce n’est pas un avantage de l’une ou l’autre version, juste une différence, tout dépend du type d’alignement que vous souhaitez.

image
Ma version du pattern “d’ici à là” réalisé avec display:table-cell

La version flexbox

À partir de ce point de comparaison, je me suis lancée d’abord dans la correction du problème d’ordre des sources.

//HTML
<div class="layout-flex-basic">
  <h2 class="stretch-title">looks great from here to there</h2>
  <figure class="stretch-photo small">
    <img src="img/flexbox_stretch_small.png" width="58" height="127" alt="mobile"/>
  </figure>
  <figure class="stretch-photo big">
    <img src="img/flexbox_stretch_big.png" width="361" height="294" alt="desktop"/>
  </figure>
</div>

Si proche, et pourtant bien meilleur !

Pour utiliser flexbox sur ce beau HTML, on va utiliser display:flex sur la div container des trois éléments ( NdT : n’oubliez pas d’utiliser Autoprefixer pour les préfixes constructeurs). J’utilise également align-items:flex-end pour aligner verticalement les trois éléments, devenus des “flex items”, par le bas.

//CSS
.layout-flex-basic {
  display: flex;
  align-items: flex-end;
}

Mes trois morceaux sont bien alignés sur une rangée, mais bien sûr le titre vient en premier puisqu’il est le premier dans mon HTML. Je peux placer l’image du smartphone en premier en utilisant la propriété order. Tous les items flex ont la propriété order réglée sur 0 par défaut, donc -1 place l’image de smartphone avant les autres, et le titre se retrouve maintenant au milieu.

//CSS
.layout-flex-basic .small {
  order: -1;
}

Il ne nous reste plus qu’à étirer le titre pour qu’il prenne toute la place sur la ligne, une fois que les images ont pris la leur. En fixant flex:1, le problème est résolu. Je lui ai aussi donné un peu de margin-bottom pour le faire remonter légèrement du bas, ainsi qu’une bordure pour créer la ligne qui va d’une image à l’autre.

//CSS
.layout-flex-basic .stretch-title {
  flex: 1;
  margin-bottom: 4.5rem;
  border-bottom: 1px solid #ccc;
}

Et voilà ! Pas mal, non ? À part le problème des variantes navigateurs, qu’on peut résoudre avec Sass ou d’autres outils (Autoprefixer), c’est vraiment aussi simple qu’avec la version display:table. Le résultat est le même, mais il est accessible et l’ordre des sources est sémantique.

L’avantage d’avoir un bon ordre des sources est que je peux facilement changer le layout sur des écrans de taille réduite afin d’avoir le titre en haut, dans sa position “naturelle”, avec les deux images empilées en-dessous. Ou bien je peux au contraire le placer sous les deux images :

//CSS
@media screen and (max-width:50em) {
  .layout-flex-basic {
    flex-wrap: wrap; /* permet d’avoir 2 rangées */
    justify-content: space-between; /* déplace les images à chaque bout de leur rangée */
  }
  .layout-flex-basic .stretch-title {
    flex: 1 0 100%; /* make it wrap to its own line */
    order: 1; /* déplace au bout */
    margin-bottom: 0;
    margin-top: 1em;
    border-bottom: none;
    border-top: 1px solid #ccc;
  }
  .layout-flex-basic .small {
    order: 0; /* revient à sa place normale */
  }
}
image
Même si le titre est le premier dans l’ordre des sources, je peux le déplacer en-dessous sur des écrans plus étroits, grâce à la propriété order.

C’est impossible de le faire avec la version display:table. Je peux certes changer le CSS pour les écrans étroits, mais mon titre restera coincé entre les deux images dans mon HTML. L’ordre des sources est vraiment important.

Bien sûr, je peux aussi vouloir que le titre soit entre les deux images sur des écrans très réduits, et c’est très aisé à réaliser avec flexbox.

//CSS
@media screen and (max-width:480px) {
  .layout-flex-basic {
    flex-direction: column; /* empilement vertical */
    flex-wrap: no-wrap; /* tout dans une seule colonne */
    align-items: center; /* centré horizontalement */
  }
  .layout-flex-basic .stretch-title {
    flex: 1 0 auto; /* affecte sa hauteur, pas sa largeur */
    order: 0; /* revient à sa place normale */
    width: 100%;
    margin: 1em 0;
    padding: .5em 0;
    border-bottom: 1px solid #ccc;
    border-top: 1px solid #ccc;
  }
  .layout-flex-basic .small {
    order: -1; /* placé au sommet */
  }
}
image
Nouveau changement d’ordre visuel, cette fois-ci pour des écrans encore plus étroits.

Étendre la version flexbox

La version flexbox peut être étendue pour inclure bien des choses que nous n’avons pas dans la version display:table. Je peux ajouter d’autres morceaux et les placer avec plus de précision et de flexibilité qu’avec display:table. Admettons que je veuille ajouter une légende à chaque image, mais en les alignant sur le côté comme on le voit ci-dessous :

image
L’ajout de légendes à côté plutôt qu’en-dessous de chaque image ajoute une couche de complexité, mais c’est très faisable.

Voici le HTML logique :

//HTML
<div class="layout-flex-advanced">
  <h2 class="stretch-title">looks great from here to there</h2>
  <figure class="stretch-photo small">
    <img src="img/flexbox_stretch_small.png" width="58" height="127" alt=""/>
    <figcaption>mobile</figcaption>
  </figure>
  <figure class="stretch-photo big">
    <img src="img/flexbox_stretch_big.png" width="361" height="294" alt=""/>
    <figcaption>desktop</figcaption>
  </figure>
</div>

Pour le réaliser avec flexbox, je transforme chaque <figure> en un flex container. Ce sont toujours des flex items, de même que leur élément frère <h2>, mais maintenant les éléments <img> et <figcaption> à l’intérieur de chaque <figure> sont également des flex items. Cela signifie qu’ils peuvent être alignés de toutes les façons possibles.

//CSS
.layout-flex-advanced .stretch-photo {
  display: flex;
  align-items: flex-end;
}

Je veux que chaque légende <figcaption> soit à côté de son parent <figure>, donc j’ai besoin de coder “en dur” la largeur de chaque <figure> (pas terrible, je sais) afin qu’elle ne s’étende pas en recouvrant sa légende. Je dois m’assurer que rien à l’intérieur de <figure> ne soit flexible, et que tout reste à sa largeur native.

//CSS
.layout-flex-advanced img,
.layout-flex-advanced figcaption {
  flex: 0 0 auto;
}
.layout-flex-advanced .small {
  width: 58px;
}
.layout-flex-advanced .big {
  width: 361px;
}

Chaque légende est en overflow de sa <figure>, à droite, puisque le contenu va de gauche à droite.

image
Par défaut, les deux légendes sont en overflow de leur container figure. Ce n’est pas génial sur l’image de droite, dont la légende est à peine visible à l’écran.

Mais je peux changer cela, en utilisant le propriété flex-direction. Vous savez sans doute qu’on peut utiliser flex-direction pour passer d’une disposition row à une disposition column. mais ce ne sont pas les deux seules valeurs de flex-direction, on peut aussi lui donner les valeurs column-reverse et row-reverse. Elles fonctionnent de la même façon que leurs alter ego column et row, mais font partir leur empilement dans la direction opposée à celle qu’on attend normalement. Donc, pour une langue écrite de gauche à droite comme le français, flex-direction:row aurait pour effet de disposer les flex items de gauche à droite, mais flex-direction:row-reverse les dispose de droite à gauche. C’est exactement ce que nous voulons pour notre grande image : placer l’image à droite de la rangée (premier dans l’ordre des sources), puis sa légende à sa gauche (deuxième dans l’ordre des sources).

//CSS
.layout-flex-advanced .big {
  width: 361px;
  flex-direction: row-reverse;
} 

Et le problème est réglé.

Bien sûr, pour des largeurs d’écran réduites, nous pouvons placer les légendes à nouveau en-dessous de leur image — ou au-dessus, ou bien où nous le souhaitons, grâce à flexbox.

image
La première image a sa légende en-dessous, la seconde au-dessus, et le titre est en sandwich entre les deux.

Le meilleur de tout est que flexbox peut être ajouté comme une amélioration progressive. Je pourrais utiliser le positionnement absolu comme l’a fait Dan, en tant que base, et ajouter par dessus ma construction flexbox, qui m’offre des options supplémentaites de placement et d’alignement que le positionnement absolu ne permet pas de réaliser.

Ce pattern “d’ici jusque là” peut aussi se retrouver dans une bannière principale ou juste dans une jolie photo montrant comment un bébé tout mignon est devenu grand :

image
Cette version utilise la même structure de markup que les autres, avec un peu de style ajouté (il est pas trognon mon fiston ?)

Vous pouvez retrouver toutes les versions dans la page de démo et regarder le CSS. Comme toujours, redimensionnez la largeur de la fenêtre pour voir comment le layout s’ajuste aux différentes largeurs. Et bien entendu, utilisez un navigateur qui supporte flexbox ( NdT : c’est à dire aujourd’hui tous les bons navigateurs).


Intéressé par CSS ? Retrouvez une liste des meilleurs articles et ressources du web.

Retrouvez ici tous les articles sur Flexbox parus dans la Cascade.


original paru le dans le blog de Zoe Gillenwater Zomigi.

Sur l’auteur : est web designer et développeur, elle est spécialisée dans l’expérience utilisateur, le web design responsif et les techniques d’accessibilité. Ell est l’auteur de plusieurs ouvrages sur CSS et donne régulièrement des conférences à travers le monde. On peut la suivre sur son blog Zomigi, sur Google+ et sur Twitter.

Traduit avec l’aimable autorisation de Zomigi et de l’auteur.
Copyright Zomigi © 2014.