Choisir sa spéc de positionnement CSS

Intro

Carré contenant le texte : CSS is awesome (le dernier mot dépasse du carré)

Mais CSS, ça peut aussi paraître compliqué. Depuis la première spécification CSS 1 (1996), le langage acquiert régulièrement de nouvelles fonctionnalités :

Liste des différentes taxonomies et statuts : CSS 1, CSS 2.1, CSS 3 (avec des pastilles représentant si c'est une recommandation W3C, une candidature de recommandation, dernier appel, brouillon ou obsolète/inactif)

Parmi celles-ci, se trouvent plusieurs spécification ayant pour but de réaliser la mise en page de pages web et plus particulièrement le positionnement des éléments. Récemment, Grid et Flex sont arrivés dans nos navigateurs et nous offrent de nouvelles possibilités de positionnement, pensées à l’ère du mobile et des sites responsives.

Mais avec autant de choix, les plus vieilles spécifications sont-elles pertinentes, ont-elles toujours un intérêt ? Pour vous faire une idée, nous allons lister ici les différentes façons de positionner des éléments avec CSS, tout en présentant leurs avantages et inconvénients.

Position : absolute ;

S’il est bien un positionnement à utiliser avec parcimonie, c’est bien celui-ci. Le positionnement absolu a pour principal défaut de ne pas occuper d’espace. Si vous souhaitez positionner un élément et que sa taille doit être déterminée en fonction de son contenu, passez votre chemin !

En effet, si l’on ajoute position : absolute ; à un bloc, celui-ci se superpose aux autres blocs. Cette contrainte oblige souvent à spécifier des dimensions aux éléments pour placer les éléments alentours sans qu’ils ne se superposent.

Pour rappel, lorsqu’on place un élément de manière absolue, il est nécessaire d’avoir un élément parent qui sert de référence. Cela signifie que notre élément absolu sera positionné par rapport à cet élément de référence, aussi appelé bloc conteneur.

Dans l’exemple ci-dessous, un bloc est positionné de manière absolue. Sa position est relative à son premier parent qui possède une position autre que static qui est la valeur par défaut. La plupart du temps, nous choisirons relative, puisque cela ne changera pas le positionnement de cet élément parent. Dans l’exemple, le bloc absolu est placé à 10 pixels du coin inférieur gauche de l’élément grâce à bottom et left. Il est également nécessaire de spécifier une hauteur fixe à l’élément parent pour qu’il apparaisse à l’écran. Comme dit plus haut, un élément absolu n’occupe pas d’espace dans la page.

See the Pen Exemple absolute #1 – 24 jours de web by Sébastien JEAN (@sebj54) on CodePen.

Ce positionnement permet de réaliser aisément des alignements dans les deux dimensions. Un de ces points forts réside dans la liberté complète qu’il propose. Il est très simple de positionner un élément A dans un bloc B par rapport aux dimensions de ce bloc B. Un exemple concret de ce principe est le centrage vertical (voir exemple ci-dessous). Pour cela, il suffit de positionner le bloc absolu au milieu du bloc parent (grâce à top : 50%;). Mais en faisant cela, on positionne le coin supérieur gauche du bloc à centrer par rapport au coin supérieur gauche de son parent et ne considère pas la taille de l’élément absolu. On corrige alors cela en ajoutant transform : translateY(-50%);. Cela permet de remonter l’élément de la moitié de sa taille sur l’axe vertical. Toutefois, il faut également veiller que le contenu de cet élément ne soit pas trop grand car il dépasserait de son parent sur l’axe Y.

See the Pen Exemple absolute #2 – 24 jours de web by Sébastien JEAN (@sebj54) on CodePen.

C’est là tout le problème du positionnement absolu. Dans de nombreux cas, la taille du contenu peut faire déborder l’élément absolu de son parent ou chevaucher d’autres éléments positionnés en absolu.

Display : table ;

Les tableaux ! Les plus chevronné·e·s d’entre nous se souviendront de la bataille qui a opposé les adeptes des tableaux pour le positionnement aux puristes qui ne voulaient pas positionner autrement qu’en CSS. Ceci fait référence à la Separation of Concerns : le fait de séparer le style CSS du balisage HTML. Ce principe définit qu’on doit respecter la sémantique HTML, c’est-à-dire qu’on ne doit pas utiliser des balises pour la forme qu’elle procure au contenu. En HTML, les tableaux sont destinés à accueillir des données comme indiqué sur MDN.

Mais c’était sans compter sur CSS qui permet de donner le format d’un tableau à n’importe quel élément HTML grâce à display : table ;. Il faut néanmoins, comme dans un tableau, avoir (au moins) un élément table, un table-row et un table-cell. Ceci requiert d’ajouter des balises supplémentaires dans le HTML pour leur définir la valeur de display requise.

Néanmoins, les tableaux permettent l’alignement vertical au sein d’une même ligne (grâce à vertical-align), l’espacement entre les différentes lignes et colonnes (avec border-spacing) et bien sûr la possibilité de spécifier des tailles aux colonnes (grâce à width).

Dans l’exemple ci-dessous, une mise en page simple est réalisée : 2 colonnes (15% et 85%), l’une contenant une image et l’autre contenant un texte.

See the Pen Exemple table #1 – 24 jours de web by Sébastien JEAN (@sebj54) on CodePen.

Contrairement à d’autres techniques, les tableaux ont un impact important sur le rendu parce que le gabarit total de la table dépend de son contenu. Pour éviter cela, il faut utiliser table-layout : fixed ; qui va figer le tableau (plus d’infos).

Enfin, les tableaux peuvent poser des problèmes d’accessibilité puisque le tableau, au lieu de présenter des données, n’est utile que pour le formatage. Pour résoudre ce problème, il est important d’ajouter role=”presentation” à la balise servant de tableau.

Float : left ;

À l’origine, le positionnement par flottant est destiné à être utilisé pour faire flotter des images au sein d’un bloc de texte. Mais la propriété float permet de faire flotter n’importe quel élément HTML dans son conteneur. Très vite, cette propriété a permis la création de colonnes au sein d’un conteneur, simplement en spécifiant une largeur fixe à nos colonnes. Dans l’exemple ci-dessous, deux colonnes sont créées, comme dans l’exemple précédent avec les tableaux.

See the Pen Exemple float #1 – 24 jours de web by Sébastien JEAN (@sebj54) on CodePen.

On remarque dans l’exemple qu’aucun style particulier n’est défini pour l’élément wrapper. Néanmoins, on conserve le principe de ligne (row) comme dans les tableaux. En effet, les flottants sortent du flux ; aucun espace vertical n’est réservé pour eux. Pour contrer cela, force le bloc conteneur à prendre en compte l’espace vertical utilisé par les flottants (avec clear). Sans cela, la deuxième ligne commencerait au niveau de la fin du texte de la première ligne, flottant elle aussi.

Contrairement aux tableaux, les flottants ne permettent pas de spécifier l’espacement entre les colonnes autrement qu’avec margin ou padding. Si l’on souhaite spécifier une taille aux colonnes, il faut prendre en compte cet espacement ; le plus simple étant en couplant padding à box-sizing : border-box ;.

Enfin, les flottants ne permettent pas l’alignement vertical. On ne peut donc pas centrer verticalement ou en bas de la ligne les différents blocs qui la composent. Moins puissant que les tableaux, donc.

Cette implémentation est très similaire à celle de Bootstrap 3 (mais aussi 960gs, Skeleton, Gumby, Goggle WebStarter Kit…). Ce framework s’appuie sur les flottants pour ses colonnes (disponibles avec les classes type col-md-*) ainsi que sur les media-queries pour définir des tailles de colonnes différentes en fonction de la taille de l’écran.
Pour en savoir plus, n’hésitez pas à lire l’excellent article Construire une grille fluide avec float qui détaille le principe de fonctionnement des grilles basées sur float.

Display : inline-block ;

À première vue, le positionnement avec inline-block paraît similaire au positionnement par flottants. En effet, il consiste à rendre un élément de type bloc en ligne ; c’est-à-dire que différents éléments inline-block à la suite vont apparaître côte-à-côte à l’écran, dans le sens du texte.

Pour cette raison, inline-block est à utiliser dans un élément conteneur si l’on souhaite le maîtriser (par exemple pour le centrer horizontalement).

À la différence de float, inline-block permet l’alignement vertical grâce à vertical-align. On peut donc aligner des éléments inline-block dans deux dimensions.

Nous pouvons adapter à nouveau notre exemple précédent en utilisant inline-block.

See the Pen Exemple inline-block #1 – 24 jours de web by Sébastien JEAN (@sebj54) on CodePen.

Mais, devant tous ces avantages, il y a tout de même un inconvénient majeur : les éléments inline-block ne sont pas collés horizontalement les uns aux autres. Tout comme le texte, il y a des espaces entre les différents éléments. Ceci peut être facilement contourné avec un moteur de template supprimant les espaces entre les balises, ou bien un petit script JS. Mais à cela, il faut rajouter l’impossibilité de centrer verticalement s’il n’y a qu’un seul élément. En effet, l’alignement vertical est réalisé en se basant sur le plus grand élément de la ligne ; s’il n’y a qu’un élément, c’est qu’il est déjà aligné.

Display : flex ;

display : flex ; est une propriété qui est la base du module CSS Flexible Box Layout. Ce module propose un nouveau modèle de boîtes (box layout) en plus des existants block, inline, table et position. En somme, tout un tas de propriétés disponibles pour réaliser bon nombre d’options d’alignement, espacement, positionnement….

Comme pour float, on retrouve le concept de conteneur (wrapper) qui va nous permettre cette fois-ci de définir la façon dont les éléments se positionnent. En premier lieu, il convient de définir display : flex ; sur le conteneur. Plusieurs propriétés s’ajoutent à cela, nous permettant de définir :

  • la direction du flux (ligne ou colonne) à l’aide de flex-direction ;
  • l’alignement dans deux dimensions avec align-items, justify-content ;
  • la possibilité de passer à la ligne pour le contenu avec flex : wrap.

Le fonctionnement de ces propriétés peut être repris pour adapter à nouveau le même exemple :

See the Pen Exemple flex #1 – 24 jours de web by Sébastien JEAN (@sebj54) on CodePen.

Dans cet exemple, seules trois propriétés sur le conteneur permettent de définir l’alignement dans deux dimensions de toute la ligne ! Les plus méticuleux feront d’ailleurs remarquer que flex-direction : row ; est facultatif car il s’agit de la valeur par défaut.

Concernant la taille des colonnes, celles-ci ont été conservées des exemples précédents, mais nous aurions très bien pu nous en passer. En effet, Flex permet à des colonnes d’occuper la place restante dans la ligne, voire à plusieurs colonnes de se partager cette taille restante. Il est même possible, comme dans un tableau, de spécifier l’alignement vertical d’un élément pour qu’il soit différent du reste de la ligne.

Maintenant que vous connaissez les plus importantes des possibilités de Flex, vous pouvez tout ré-imaginer en changeant les lignes par des colonnes. En définissant flex-direction : column ;, l’axe horizontal devient vertical et les lignes deviennent des colonnes. Toutes les options qui s’offraient à nous pour aligner les éléments de nos lignes sont disponibles pour les colonnes (alignement horizontal, occupation de l’espace restant de la colonne…).

Il serait très compliqué de résumer Flex à un seul paragraphe d’un article de blog alors que certains en ont fait un livre. Toutefois, il est utile de décrire ici les possibilités qu’offre Flex afin de se faire une idée de la puissance et les cas d’utilisation possibles de ce module.
Flex est donc un excellent concurrent aux précédentes méthodes de positionnement puisqu’il comble bon nombre de manques tout en apportant de nouvelles possibilités.
Toutefois, Flex a l’inconvénient d’être restreint à ce fonctionnement par conteneur puisqu’il empêche les éléments d’être présents sur plusieurs lignes ou colonnes. Flex est donc bloqué dans une dimension à la fois. Et c’est là que display : grid ; intervient.

Display : grid ;

Grid, comme son nom l’indique, repose sur un principe de grilles. Il faut voir grid comme un damier nous permettant de placer des éléments dans des cellules. Ces éléments peuvent même occuper plusieurs cellules de la grille, aussi bien horizontalement que verticalement.

Pour commencer, il faut donc décrire la grille, c’est-à-dire le nombre de cellules pour chaque ligne et / ou le nombre de colonnes. En fonction de l’axe du contenu, Grid permet de répéter autant de lignes ou de colonnes que nécessaire pour accueillir le contenu.

Si l’on reprend notre exemple, on peut imaginer une grille de deux colonnes par ligne avec autant de lignes que nécessaires. Appliqué sur le conteneur, display : grid ; permet d’initialiser la grille. grid-template-columns permet de définir le nombre de colonnes par ligne ainsi que leur taille (ici 15 et 85 %). Il n’y a plus qu’à définir pour chaque élément de la ligne, la cellule de la grille dans laquelle il doit s’afficher à l’aide de grid-column-start.

See the Pen Exemple grid #1 – 24 jours de web by Sébastien JEAN (@sebj54) on CodePen.

On constatera également dans cet exemple qu’on retrouve des propriétés de Flex, comme align-items ou justify-content. Celles-ci permettent d’ajuster l’alignement des élément sur les axes X et Y.

Grid, comme Flex justement, permet également à certaines cellules d’occuper l’espace restant disponible sur une ligne ou une colonne grâce à une nouvelle unité : fr. Mais une fonctionnalité sans précédent est minmax() : une fonction qui permet à une cellule d’avoir une taille comprise dans un intervalle. C’est une fonctionnalité très utile lorsqu’on couple plusieurs unités (par exemple minmax(150px, 15%) nous donnerait une taille comprise entre 150 pixels et 15 % de la taille de la ligne ou de la colonne). C’est donc une nouvelle possibilité de faire des mises en page fluides sans media-queries.

Dans le cas d’une galerie d’images sous forme de grille, par exemple, nous pourrions choisir d’afficher le plus possible d’images sur une même ligne, tant qu’il y a de l’espace disponible et que l’image est dans des dimensions acceptables grâce à minmax mais surtout repeat et auto-fit. Cette fonction et cette valeur permettent, en fonction de l’intervalle défini avec minmax, de déterminer le nombre d’éléments dans la ligne.

See the Pen Exemple grid #2 – 24 jours de web by Sébastien JEAN (@sebj54) on CodePen.

Ces deux exemples nous montrent que Grid propose des fonctionnalités, certes déconcertantes au premier regard, mais vraiment très pratiques lorsqu’il s’agit de positionner sur la base de proportions ou de ratios. L’exemple ci-dessus nous prouve également ses capacités d’adaptation.

Support

Le support dans les navigateurs est un élément déterminant pour l’utilisation d’une méthode de positionnement plutôt qu’une autre. Hormis Flex et Grid, les méthodes de positionnement présentées sont assez anciennes et existent depuis longtemps. Inline-block est pleinement disponible depuis IE8 (2009) et les autres propriétés depuis CSS 2.1.

Flex

Flex est disponible sur l’ensemble des navigateurs récents et même sur IE 11 (2013) mais dans une version buguée (principalement lors d’imbrication). Attention toutefois, la compatibilité avec les navigateurs les plus vieux n’est possible qu’à l’aide d’anciennes syntaxes (cela est dû à une spécification non aboutie). En comptant ces anciennes versions, on aboutit à une couverture de 98 % des utilisateurs de navigateurs.
https://caniuse.com/#feat=flexbox

Grid

Depuis octobre 2017, Grid est disponible sur Edge, dernier ajout à la liste des navigateurs supportant Grid.
https://caniuse.com/#feat=css-grid

En résumé

Position : absolute ;

Pour faire court, le positionnement absolu se superpose, n’occupe pas d’espace et peut déborder. C’est donc à réserver aux éléments à superposer ainsi qu’à des éléments dont la taille du contenu est maîtrisable (c’est-à-dire une taille fixe ou faiblement variable).
Un élément communément placé de manière absolue est l’infobulle : vocation à être superposée, taille maîtrisée (souvent une courte description). Autre exemple, un titre superposé sur une image ; on retrouve la volonté de superposition ainsi que la taille maîtrisée.

Display : table ;

Les tableaux permettent de réaliser facilement des colonnes grâce à quelques règles CSS tout en gérant la largeur des colonnes, leur espacement et l’alignement vertical. Son principal inconvénient réside dans l’ajout de balises HTML. Cette complexité s’accroît lorsqu’on imbrique beaucoup de tableaux, par exemple pour réaliser plusieurs alignements verticaux.
Il faut garder à l’esprit l’usage initial des tableaux qui consiste à afficher des données.

Float : left ;

Les flottants ont fait leur temps ! Ceux-ci vont de moins en moins être utilisés, voire n’être utilisés que pour faire des flottaisons au sein de blocs de texte ou encore reprendre leur role initial (flotter des images). De par leur manque de flexibilité (alignement vertical notamment), on leur préférera Flex dans de nombreux cas. Bootstrap a d’ailleurs changé son système de colonnes flottantes pour des colonnes utilisant Flex par défaut dans sa version 4.

Display : inline-block ;

Bien que peu utilisé pour la mise en page, inline-block n’est conseillé maintenant que pour des éléments isolés ou en présence d’un contexte linéaire (comme du texte par exemple).
Inline-block est, par exemple, un très bon choix pour afficher un bouton type “Voir plus” (Read more) à la suite d’un bloc de texte.
Bien sûr, il reste possible d’utiliser inline-block dans des cas très particuliers, notamment pour réaliser un carousel.

Display : flex ;

Flex est vraiment un atout formidable pour le positionnement. Celui-ci permet de positionner et d’aligner aisément au sein d’une ligne ou d’une colonne dans deux dimensions. La gestion des proportions et de l’espace restant est simple à prendre en main.
Le principe de conteneur fait de flex un premier choix pour une approche composant qui utilise beaucoup le principe d’imbrication. Un composant est un ensemble d’éléments d’une page qui fonctionne de manière autonome (boutons, formulaires, navigation…).

Display : grid ;

Pour savoir s’il est pertinent d’utiliser Grid, il faut immédiatement se demander si l’on a besoin d’une grille. Il faut également se remémorer que Grid gère excellemment les mises en page fluides à l’aide des intervalles de valeur qui permettent de définir la taille des cases, voire du nombre d’éléments par ligne. Il reste également possible d’utiliser les media-queries dans le cas de mises en page complexes, pour ajuster la grille ou la position des éléments sur la grille.

Conclusion

Chacune de ces méthodes de positionnement possède son usage et il convient de les utiliser en présence d’un contexte approprié. Dois-je réaliser un composant ? Est-il composé de lignes, de colonnes ? Les éléments ont-ils des proportions à respecter ? Dois-je superposer cet élément ?

Dans bien des cas, il est possible d’aboutir à un résultat similaire avec chacune des méthodes proposées. Dans ce cas, il convient de choisir celle présentant le meilleur support afin de proposer une expérience similaire au plus grand nombre. Souvent, certaines méthodes seront exclues à cause de leurs contraintes et principalement des choses impossibles à réaliser (comme le centrage vertical).

Même sans utiliser certaines de ces méthodes, il est toujours utile de connaître ce que l’on ne sait pas faire ou ne maîtrise pas (les fameuses inconnues inconnues). Par exemple, même sans utiliser Flex, vous pouvez simplement retenir que cela permet de gérer l’espacement et l’alignement vertical ou horizontal. En présence d’un cas d’utilisation possible, il n’y aura qu’à se renseigner plus sur le sujet et apprendre à le maîtriser.

3 commentaires sur cet article

  1. Nico, le vendredi 15 décembre 2017 à 19:32

    Quelques petites coquilles dans l’article : Le display:table n’est pas vocalisé comme réel tableau (sauf si c’est un vrai code avec table dans le HTML), certes il y avait un bug sur de vieilles versions de certaines synthèses vocales couplées à certains navigateurs, mais ces bugs n’y sont plus sur les AT majeures. Bref, pas de souci de ce côté.

    À noter, les inline-* (inline-table, etc.) qui permettent de chercher certains avantages de certains modes de positionnement en contexte inline.

    Petit point de détail : le positionnement absolu n’est pas une superposition à tous les coups, il est surtout une sortie du flux, qui peut être maîtrisée et contrecarrée par un position : static (parfois utile en responsive). N’oublions pas le position : fixed qui peut être bien utile, et le position : sticky qui peut avoir des avantages aussi ;)
    Par contre, les sorties de flux ont un autre inconvénient : en RTL elles doivent être adaptées, là où d’autres positionnements marchent tous seuls comme des grands :)

  2. Ben, le lundi 18 décembre 2017 à 09:43

    Merci pour cet article complet et qui fait un bon rappel, je le mets de côté pour mes doutes :)

    Concernant le commentaire précèdent, je voulais apporter une remarque :

    https://caniuse.com/#search=sticky

    Est que que le taux de déploiement est assez ”fort” pour se mettre d’implémenter réellement le position ”sticky” ?
    Il a progressé depuis l’année dernière et j’espères que cela va passer en recommandation rapidement

    bonne semaine d’inté :)

  3. Nico, le lundi 18 décembre 2017 à 14:45

    Ben : cela dépend de ta cible. Pour ma part, je ne me permets pas encore de l’utiliser en production. :)