CSS est difficile, à ce qu’il paraît. Mais avez-vous déjà essayé d’écrire du CSS en vous battant contre un framework qui modifie votre DOM ?
Le problème
Je travaille sur une interface simple : header, contenu, footer. Le header et le footer ont une taille fixe, et je souhaite positionner le contenu au centre de l’espace restant.
Me voilà donc avec cette implémentation simple :
<body>
<header></header>
<main></main>
<footer></footer>
</body>
body {
display: flex;
flex-direction: column;
justify-content: space-between;
}
Je suis donc heureux, comme tout développeur front quand les choses s’alignent correctement.
Mais alors que l’application grandit, je réalise que je vais avoir besoin d’englober le main
et le footer
à l’intérieur d’un composant Angular unique, pour des raisons structurelles propres à l’application.
C’est alors que ma belle page se brise, et vous pouvez comprendre pourquoi en regardant la structure générée par l’apparition de ce nouveau composant :
<body>
<header></header>
<my-component>
<main></main>
<footer></footer>
</my-component>
</body>
Hélas ! Flexbox ne s’intéresse à rien d’autre qu’à ses propres enfants directs, qui sont maintenant header
et my-component
.
Ma première idée fut de faire de my-component
un conteneur flex lui-même, mais je n’allai pas très loin avec cette idée.
my-component {
flex-grow: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
}
Pensez-y un instant : comment vous y prendriez-vous ?
Une méthode douteuse
Je sais que vous n’y avez pas pensé un instant et que vous avez continué votre lecture. Je ne vous en veux pas.
Une méthode à laquelle je n’avais pas pensé avant d’écrire cet article est d’ajouter un pseudo-élément à my-component
:
my-component::before {
content: "";
}
my-component
a désormais trois enfants : le pseudo-élément before
, le main
et le footer
.
Voici le résultat en action !
Il n’y a qu’à remplacer le contenu du pseudo-élément par une chaîne vide, et voilà ! Parfois, une solution un peu sournoise comme celle-ci est si simple qu’elle en devient attractive...
Une méthode plus propre
Mais ajouter un pseudo-élément juste pour tromper flexbox ne me paraît pas extrêmement propre !
Il y a quelques semaines, j’ai lu cet excellent article de Rachel Andrews : Digging Into The Display Property: Box Generation.
En lisant les informations à propos de display: content
, j’avais deux pensées parallèles :
- Ça alors, c’est fort intéressant...
- ...mais je ne vais jamais m’en servir.
Mais en travaillant sur mon problème de layout, l’article me revint à l’esprit. En voici un extrait :
En d’autres termes, cela sera équivalent (dans notre cas !) à ce qu’il se passerait si l’on commentait l’élément (mais pas ses enfants) :
<body>
<header></header>
<!-- <my-component> -->
<main></main>
<footer></footer>
<!-- </my-component> -->
</body>
Et voici comment procéder dans les faits :
my-component {
display: contents;
}
font-size
, color
...) appliquées à l’élément impactent toujours ses enfants. Donc commenter l’élément n’est pas strictement équivalent, mais vous avez l’idée.Donc... Cela signifie-t-il que du point de vue de flexbox, main
et footer
sont de nouveau des enfants directs du body
? Oui ! my-component
n’a plus besoin d’être un conteneur flex lui-même. La propriété justify-content: space-between
appliquée au body
fonctionne de nouveau.
C’est la solution la plus courte présentée dans cet article : un sélecteur et une propriété. Presque magique.
Notez que la bordure bleue qui entourait my-component
a disparu : en effet, la boîte n’est plus rendue, donc les propriétés correspondantes (margin
, padding
, border
...) n’ont plus d’effet.
Bien que j’aime la belle simplicité de cette solution, display: contents
possède ses inconvénients.
display: contents
peut provoquer des problèmes d’accessibilité, car l’élément est caché de l’arbre d’accessibilité. Si vous l’utilisez sur un élément ul
, l’information qu’il s’agit d’une liste sera perdue dans de nombreux navigateurs.
Ce n’est pas un problème dans notre cas, cependant, puisque my-component
ne véhicule pas de sens sémantique particulier. Mais l’on pourrait toutefois se méfier du support navigateur, qui est dans cette zone grise que j’appelerai la zone meh...
.
display: contents
est désormais largement supporté, bien que caniuse.com rapporte toujours de nombreux problèmes d’accessibilité. À juger au cas par cas, donc.Une méthode encore plus propre
Voici donc la technique margin: auto
.
Ce n’est qu’en rédigeant cet article que je me suis souvenu d’un autre article, un des meilleurs que j’ai lu sur le sujet: Flexbox’s Best Kept Secret, de Sam Provenza. Il date de 2015 et j’ai utilisé ce « secret » un grand nombre de fois depuis.
Quatre ans plus tard, je ne suis pas sûr qu’il soit bien connu. Mais vous pouvez utiliser margin-left: auto
, pas exemple, pour « pousser » un élément flexbox aussi loin que possible vers la droite.
Voyez plutôt ce Codepen. L’élément de prix possède la propriété margin-left: auto
.
C’est extrèmement utile ! Avec cette technique, il devient possible de pousser un élément dans n’importe quelle direction, horizontale ou verticale.
Mais il y avait une partie de l’article que j’avais oubliée :
Ainsi, le « secret » fonctionne aussi avec plusieurs marges auto
.
Et la voici donc, cette merveilleuse solution à notre problème : faire de my-component
un conteneur flex de nouveau, et utiliser margin: auto 0
sur l’élément main
.
margin-block: auto
nous éviterait d’avoir à spécifier le 0
, qui m’a toujours un peu gêné...my-component {
flex-grow: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
}
main {
margin: auto 0;
}
Et voici le résultat en action.
L’espace libre est distribué équitablement entre le dessus et le dessous de l’élément main
.
Pas de pseudo-élément magique. Moins de risque d’accessibilité et de compatibilité. Simplement la majestueuse et toute puissante flexpower™.
L’épopée du centrage vertical ne s’achève jamais.
Cet article a été initialement publié sur dev.to
✍️ Aucun commentaire pour le moment