Rendre une police totalement responsive avec la fonction CSS clamp et un peu de Math.
09/11/2022Le comportement responsive d'une page Web se fait à l'aide des breakpoints CSS qui par définition propose d'appliquer des règles supplémentaires lorsque les dimensions de la page Web sont inférieures/comprises/superieures entre des valeurs fixées par le développeur. Le problème c'est que pour les tailles des polices/fonts de certains éléments spécifiques, cette vision en escalier est insuffisante.
Mise en situtation
Que ce soit à l'aide d'un framework CSS ou from scratch, une bonne pratique est de ne pas utiliser un trop grand nombre de breakpoints mais plutôt d'essayer de se calquer sur des familles de périphériques. Par exemple dans Bootstrap5, il y a 6 breakpoints de base :
- Extra Small <576px : mobile.
- Small ≥576px : gros mobile, petite tablette.
- Medium ≥768px : tablette, mobile en paysage.
- Large ≥992px : ordinateur portable, tablette.
- Extra Large ≥1200px : ordinateur portable, écran.
- Extra Extra Large ≥1400px : ordinateur portable (haut de gamme), écran.
Ce découpage permet de bien cibler les différents type de périphérique et ainsi de proposer des templates différents en jouant sur les dispositions, les tailles...
Si je prends l'exemple d'une topbar, avec un titre contenant le nom du site étalé sur toute la largeur (width:100%). Pour que ça rentre sur une ligne, on va par exemple définir une taille minimal du bloc et une taille de police (min-width:320px, font-size:1.5rem) comme règles de base.
Puis ensuite, on va de manière ascendante, c'est à dire du plus petit périphérique au plus grand écrire des règles qui surchargent les anciennes.
Pour notre topbar, cela veut dire qu'il faut définir un breakpoint (media-query ≥ 576) qui comporte une règle qui rédéfini la taille de la police en plus gros (font-size:2.5rem).
Sauf que l'on se rend bien compte que pour des largeurs autour des 500px, la taille de la police n'est pas adaptée, elle mériterait de prendre plus de largeur, surtout quand c'est le nom du site...
On peut multiplier les breakpoints mais cela rajoute du code et l'on aura toujours un comportement en escalier alors que l'on cherche finalement plutôt à avoir une taille qui s'adapte linéairement.
La fonction clamp()
Comme le dit la documentation, clamp() est une function CSS qui retourne une valeur de type "taille" à partir de trois paramètres.
- minimum value : la valeur minimum.
- preferred value : la valeur préférée, qui sera retournée tant qu'elle est comprise entre min et max.
- maximum value : la valeur maximum.
Comme c'est une fonction, elle peut être utilisée par toute les propriétés qui utilise des tailles (width, height, font-size,..) et combinée avec d'autres fonctions (min, max, calc,...).
Le coté dynamique s'obtient en définissant la valeur préférée dans une unité dynamique, comme par exemple les % ou les vw.
Pour tout ce qui est taille des éléments comme les blocs, la fonction est assez simple à utiliser, par contre, dans le cas des polices cela devient beaucoup plus obscure car on utilise des unités complétements différentes (rem vs vw).
On sent bien que ce n'est pas évident d'imaginer une correlation entre la variation de la largeur de la page (qui évolue entre 320px et 1400px) et la taille de la police exprimée en rem et c'est encore plus flagrant avec des polices exotiques.
Un peu de théorie
Pour la théorie complète cela se passe dans cet article en anglais, mais cela peux se résumer à un seul graphique (extrait de l'article).
- En dessous de 360px de large, la police sera toujours fixée à 1rem.
- Au dessus de 840px de large, la police sera toujours fixée à 3.5rem.
- Entre les deux, on souhaite avoir une interpolation linéaire issue de ses deux bornes, ici pour une largeur de 600px, la font-size calculée doit être de 2.25rem.
L'idée c'est d'essayer de calculer la pente de la droite et de l'exprimer par rapport à la largeur. Pour cela on a besoin de 4 paramètres à définir en fonction de sa police et de son template (360px, 1rem, 840px, 3.5rem).
Le calcul de la pente se fait via la formule ci dessous avec maxWidth et minWidth qui pour le calcul doivent être convertis en rem (division par 16 sur la base de 1rem = 16px).
Ensuite on a besoin de calculer le point d'intersection avec l'axe des Y défini par la formule suivante.
Enfin, l'expression de la valeur préférée peut-etre définie par la formule suivante.
Voilà, notre taille est défini avec une base fixe et un paramètre variant en vw. Pour rappel, le vw est une unité de type pourcentage sur la largeur de la page que l'on peut utiliser dans tous les contextes (contrairement au simple %).
1rem = 16px ?
Pour avoir des données calculable, on part du principe que 1rem est égale à 16px, cette valeur est issue du règlage par défaut du navigateur mais peut être surchargée en définissant la propriété font-size du noeud body.
Le principe d'utilisation de cette unité est donc de définir les tailles des polices relativement à une référence fixe contrairement au em qui s'applique en cascade au blocs parents. C'est d'ailleurs la force principale de cette unité. Y'a un coté magique à voir grossir/rappetissir en propritions les polices en changeant uniquement la font-size du noeud body.
Sauf que si le design du site redéfini cette propriété, comme c'est le cas de ce billet (font-size à 1.15rem soit 18px), le calcul est un peu différent.
Un peu de code
Pour terminer, rien de mieux qu'un peu de code pour calculer automatiquement la propriété clamp accompagné d'une simulation pour se rendre compte du résultat.