Messenger permet de changer la couleur des messages que l’on envoie en un superbe dégradé de couleur qui ne bouge pas quand on scroll. Je me suis assez rapidement demandé comment cet effet était possible: chaque message avait-il un background propre donc l’affichage était géré par du JS (spoiler : non !) ? Était-ce possible uniquement en CSS ? Je me suis donc mis en tête de recréer cet effet, et je vous propose donc de voir ensemble comment faire !
Préparer notre balise body
Tout d’abord nous allons préparer notre body. On le met en display: flex et en justify-content: center pour que son contenu soit centré horizontalement, puis en align-items: center pour que le contenu se centre verticalement. Ensuite étant donné que par défaut notre body fait la taille de son contenu, nous allons lui donner une height de 100vh. Enfin on retire son margin et on met la police en sans-serif (c’est quand même plus joli). Ce qui nous donne quelque chose comme ça:
body {
display: flex;
min-height: 100vh;
justify-content: center;
align-items: center;
font-family: sans-serif;
margin: 0;
}
Le container
Nous allons ensuite créer un conteneur pour notre messagerie ayant pour class « messenger ». Sur mon ordinateur, par défaut un item de messagerie sur le site Facebook fait 284px sur 350px donc j’ai décidé de garder cette taille. Je lui ai ajouté un border-radius de 8px sur les coins supérieurs et un overflow: hidden pour éviter que ses enfants n’apparaissent par dessus les arrondis. Là encore je l’ai mis en display: flex pour gérer plus facilement l’alignement des éléments à l’intérieur, et en flex-direction: column puisque mes trois éléments principaux (le nom de l’ami, le container de messages et l’input) sont alignés verticalement. Ensuite pour être un peu fidèle à sa version Facebook j’ai ajouté une légère ombre tout autour avec un box-shadow: 0 1px 4px rgba(0, 0, 0, .3) !
.messenger {
width: 284px;
height: 350px;
border-radius: 8px 8px 0 0;
box-shadow: 0 1px 4px rgba(0, 0, 0, .3);
overflow: hidden;
display: flex;
flex-direction: column;
}
Pour ce qui est de l’élément du haut je suis resté sur quelque chose de simple: un conteneur avec dedans une div représentant la photo de l’utilisateur et un paragraphe avec son nom :
<div class="messenger">
<div class="top">
<div class="photo"></div>
<p>Un ami</p>
</div>
<!-- Nous placerons ici la suite -->
</div>
On met notre class top en display: flex, on centre verticalement les éléments avec un align-items: center et on lui ajoute une petite box-shadow: 0 2px 1px rgba(0, 0, 0, .1) pour avoir un effet similaire à la version Facebook. Et comme nous aurons besoin d’éléments en position absolute par la suite, on peut lui attribuer dès maintenant un z-index à 9 (on n’utilisera pas 9 couches mais j’aime commencer par 9) !
.top {
display: flex;
align-items: center;
box-shadow: 0 2px 1px rgba(0, 0, 0, .1);
z-index: 9;
}
Pour la photo de l’utilisateur, je suis partit sur quelque chose d’assez simple, un rond de 30px sur 30px avec un fond noir et une légère marge sur les côtés pour espacer le tout:
.top .photo {
margin: 10px 20px;
height: 30px;
width: 30px;
background-color: #000;
border-radius: 50%;
}
Avant de nous occuper de la partie compliquée, nous allons d’abord styliser le formulaire du bas. Nous allons donc créer une div « messages-container » et la laisser vide pour le moment et en dessous un formulaire contenant simplement un champs de type text (je ne me suis pas embêté à faire tous les boutons en bas, la partie intéressante résidant principalement dans les messages).
<div class="messages-container">
<!-- Nous placerons ici les messages -->
</div>
<form>
<input type="text" placeholder="Écrivez un message...">
</form>
La balise form elle-même n’a pas besoin d’être stylisée, je me suis donc contenté de lui attribuer un padding de 15px sur le haut et le bas et de 5px sur les côtés et de retirer ses bordures pour les remplacer par une bordure supérieure de 1px en gris. J’ai ensuite passé sa width à 100% et, afin d’éviter que le bloc ne soit plus grand que le conteneur, j’ai défini son box-sizing en border-box:
input {
padding: 15px 5px;
width: 100%;
border: 0;
border-top: 1px solid #EEE;
box-sizing: border-box;
}
Les messages
Passons donc à la partie marrante ! Reprenez votre messages-container et mettez-le en display: flex et en flex-direction: column ! Pour avoir autant de messages qu’on veut, mettez son overflow en scroll. Et nous voulons également qu’il remplisse tout l’espace que ne remplissent pas la partie haute et le formulaire, donc ajoutez-lui un flex: 1. Il ne reste plus qu’à lui attribuer le dégradé qu’on veut voir apparaître sur les messages (avec toujours une couleur de fond au cas où le navigateur ne gère pas les gradient), ce qui nous donne:
.messages-container {
background-color: rgb(240, 29, 106);
background-image: linear-gradient(rgb(0, 95, 255) 0%, rgb(146, 0, 255) 50%, rgb(255, 46, 25) 100%);
flex: 1;
overflow: scroll;
display: flex;
flex-direction: column;
}
Et c’est là que se cache l’entièreté du secret de ce gradient: il n’est pas présent sur chaque message mais directement sur le conteneur, tout le reste est simplement colorié en blanc ! Vous pouvez d’ailleurs vous en rendre compte par moment quand vous tombez sur des petits bugs graphiques ! On peut y voir des rainures qui ont la même couleur que le gradient, on peut donc deviner que le fond est composé de plusieurs blocs blancs cachant ledit dégradé !
Passons donc au contenu. Si vous avez déjà comme moi inspecté un élément sur Facebook vous savez à quel point ils aiment imbriquer des divs les unes dans les autres, ce qui donne un code final assez indigeste. Nous allons nous contenter d’une structure beaucoup plus simple mais qui fonctionne tout autant:
<div class="messages right">
<div>Hey :)</div>
<div class="fill"></div>
</div>
<div class="fill"></div>
Chaque message sera donc composé d’une div contenant le message textuel suivie d’une div de remplissage, et à la fin de notre message container se trouvera une div de remplissage général au cas il n’y aurait pas suffisamment de messages à charger pour tout remplir. Notez que j’utiliserai des class « left » et « right » pour distinguer l’auteur du message. Notre class fill aura donc simplement d’un background-color blanc et d’un flex égal à 1.
.messages-container .fill {
flex: 1;
background-color: #FFF;
}
Ce qui, si vous supprimez temporairement le message, donne ceci:
Les items messages
Nous allons donc pouvoir écrire le style de nos messages. Commençons par donner à notre class messages une petite bordure blanche de 1px sur le haut et le bas (afin de séparer deux messages qui se suivent) et de 20px sur les côtés (afin de les espacer du bord). Mettez-le en display flex afin d’aligner ses enfants et en overflow: hidden (très important, mais nous y reviendront plus tard).
.messages-container .messages {
border: solid #FFF;
border-width: 1px 20px;
display: flex;
overflow: hidden;
}
Nous allons ensuite cibler la première div de notre class message (qui est celle qui contient le message) et nous lui attribue un padding de 10px sur le haut et le bas et de 15px sur les côtés. Afin d’éviter que les messages ne prennent toute la place, on règle sa width maximale à 180px. On le met en position relative (là encore l’explication vient un peu plus tard, soyez patients) et une font-size à 14px:
.messages-container .messages div:first-child {
padding: 10px 15px;
max-width: 180px;
position: relative;
font-size: 14px;
}
Ensuite nous voulons afficher les messages « right » à droite (on ne peut pas faire un message sur deux comme une même personne peut envoyer plusieurs messages d’affilée). Pour cela nous allons simplement inverser le flex-direction avec un flex-direction: row-reverse. Et comme il s’agit des messages sur un fond coloré, nous allons mettre leur couleur de texte en blanc:
.messages-container .right {
flex-direction: row-reverse;
color: #FFF;
}
Comme sur Facebook les messages envoyés par les autres sont grisés, on change la couleur des messages à gauche:
.messages-container .left {
background-color: #EEE;
}
Et histoire de garder le meilleur pour la fin, nous allons d’abord séparer les groupes de messages en fonction de leur provenance. Pour cela nous allons simplement cibler les premiers .left qui suivent un .right et inversement. Pour cibler le premier élément suivant un autre mais étant au même niveau que celui-ci nous pouvons utiliser le sélecteur « + ». Une fois ciblés, nous augmentons simplement la taille de sa bordure supérieure:
.left + .right, .right + .left {
border-top-width: 10px;
}
Pour le moment vous devriez obtenir quelque chose comme sur le screen ci-dessus. Il ne nous reste plus qu’à arrondir les coins. Sauf que comme notre fond est transparent, arrondir les coins de notre div ne servirait à rien (c’est sur cette partie que je me suis un peu arraché les cheveux). La solution trouvée par Facebook est « tout simplement » d’enfermer les messages dans un « donut » (un peu étiré) qui cachera simplement les coins.
Pour ce faire, nous allons donc passer par un pseudo-élément :after de façon à ce que notre cher donut fasse la même taille que le message. Ciblons donc notre première div de notre class messages. Comme il s’agit d’un pseudo-élément, la première chose à faire est toujours de mettre son content à « ». Afin de le manipuler comme bon nous semble nous allons le mettre en position absolute. Nous allons ensuite lui donner une bordure blanche de 30px, et ensuite le décaler à 30px de chaque bord mais vers l’extérieur ! Pour cela nous attribuons à son top, son right, son bottom et son left la valeur de 30px. et enfin nous pouvons l’arrondir avec un border-radius à 55px (vous pouvez mettre plus ou moins si vous voulez, mais 55 rend plutôt bien).
.messages-container .messages div:first-child:before {
content: "";
border: 30px solid #FFF;
position: absolute;
top: -30px;
right: -30px;
left: -30px;
bottom: -30px;
border-radius: 55px;
}
Et voilà, si vous observez votre résultat vous avez désormais votre chat Messenger avec un gradient fixe au scroll (je vous conseille donc fortement de mettre plusieurs messages en .right afin de tester). Vous pouvez vous amuser à l’améliorer un peu comme par exemple en mettant une marge au dessus du premier message et en dessous du dernier message, en faisant le même effet d’arrondi diminué entre deux messages du même destinataire, …
Et en attendant les prochains tutoriels CSS, je vous laisse regarder comment créer un chronomètre interactif sans javascript.