Utilisation de la fonction CSS clamp pour le calcul de tailles relatives | CDW

clamp(), la fonction CSS de calcul de valeur fluide

La fonction CSS clamp permet de calculer une valeur dynamique dite « fluide » entre deux limites minimale et maximale, en fonction d’une 3e valeur relative choisie. Si au premier abord, l’utilisation ,de clamp() peut paraître assez déroutante, comprendre son fonctionnement en profondeur va nous permettre de l’utiliser en nous affranchissant des calculateurs en lignes dans un contexte d’intégration ou de création de sites internet à l’aide du fluid design.

Le concept de fluid design visant à remplacer progressivement nos traditionnelles media queries, utiliser clamp() dans nos règles CSS devient un enjeu majeur de l’intégration web. Je vous propose donc ici 2 cas d’utilisation courante, à travers le calcul de valeurs CSS de tailles de polices de caractères (font-size) et de marges internes (padding).

N’hésitez pas à consulter la description de clamp sur le site MDN pour vous familiariser avec sa signature.

Mise à jour Oct 2024
Un nouveau générateur de fonction clamp() est désormais à votre disposition pour calculer automatiquement les arguments de votre fonction CSS, à partir des valeurs min et max, des 2 breakpoints correspondant, et de la valeur de votre rem.

Syntaxe de la fonction clamp()

Clamp prend 3 arguments :

  1. La valeur minimale souhaitée
  2. La valeur relative calculée (valeur dynamique dite fluide)
  3. La valeur maximale à ne pas dépasser
font-size: clamp( min, fluidVal, max);

On notera que la valeur dynamique est calculée de façon relative à un élément donné. Ainsi, dans le cas d’un calcul de taille de police de caractères, on souhaitera modifier cette valeur en fonction de la taille de l’écran, soit, la largeur du viewport du navigateur (l’agent utilisateur).

Utilisation de la fonction clamp() pour la taille des polices

La fonction clamp() autorise donc le calcul d’une valeur en fonction du viewport ou de la dimension d’un élément de référence. C’est notamment ce que nous faisons habituellement avec nos media queries traditionnelles, lorsque nous définissons la taille de nos titres ou de nos paragraphes.

Nous fixons généralement une taille par défaut correspondant aux écrans de petite taille, puis nous redéfinissions ces valeurs en fonction d’un groupe de supports cibles :

@media only screen and (min-width: 768px) and (max-width: 1023px) {
	.my-section-title {
		font-size: 22px;
	}
}

Ici, nous avons défini une taille de police à 22 pixels pour un titre de section sur un iPad en mode portait, ainsi que pour toutes les tablettes allant jusqu’à 1023 pixels de large.  

Mais si les media queries permettent de définir assez finement la taille de nos blocs de texte du smartphone à l’écran de bureau, elles ne pourront pour autant jamais couvrir l’ensemble des supports et des navigateurs du marché.

Le design « fluide » permet donc de répondre au besoin de couvrir la totalité des lecteurs sans nous soucier du ratio de pixel ou de la dimension de chaque écran du marché.

Des tailles de polices dynamiques, proportionnelles à la taille de l’écran

Nous pouvons partir d’un exemple simple pour déterminer un facteur de taille relatif à la fenêtre d’un navigateur.

Dans cette étude de cas, nous fixons la largeur maximale d’un conteneur central à 1200px, ainsi qu’une taille maximale de 28px pour le titre principal pour notre page web.

Nous souhaitons par ailleurs limiter cette taille de police à 20px sur les supports de petite dimension.

Notre objectif sera donc de définir une taille de police pour notre titre, correspondant à 2.333333333333333% d’un viewport à 1200px, limitée à 20px pour la borne inférieure et 28px pour le maximum.

\[t = 28/1200X100 = 2.333333333333333\]

Or, si nous pouvons ainsi définir une taille de police de caractères uniquement basée sur la taille de l’écran, nous ne contrôlons néanmoins ni sa taille minimale ni sa taille maximale, au risque de voir notre titre s’étendre à plus de 56px pixels sur un écran de 2560px, et réduit à 10 illisibles pixels sur les plus petits smartphones :

h1.titre-principal {
	font-size : 2.33vw;
} 

Nous allons donc pouvoir utiliser la fonction clamp(), afin de limiter (pincer) la taille de notre titre principal sur une échelle allant de 20 à 28px.

N.B. Nous garantissons une compatibilité avec les anciens navigateurs en définissant d’abord une valeur de repli (fallback) pour la taille notre titre :  

h1.titre-principal {
	font-size : 24px ;
	font-size : clamp(20px, 2.33vw, 28px) ;
}

De cette façon, la taille de notre police va pouvoir évoluer de viewport en viewport, entre les deux limites fixées :

ViewportValeur calculéeValeur appliquée
320px7.456px20px
768px17.8944px20px
1024px23.8592px23.8592px
1200px27.96px27.96px
1400px32.62px28px

Ce mode de calcul de valeur (dite « fluide ») nous impose néanmoins de résoudre deux problèmes majeurs :

  1. L’utilisation de valeurs absolues dans la fonction clamp() ne permet pas au résultat de s’adapter au zoom éventuel de l’utilisateur ;
  2. Elle ne permet pas non plus, en l’état, de maîtriser avec précision les points de rupture auxquels seront appliqués les tailles dynamiques, donc de remplacer notre système de media queries traditionnel par un système basé sur clamp().

Nous allons résoudre notre second problème en répondant au besoin du premier, en commençant par définir des breakpoints habituellement utilisés au sein de media queries pour l’intégration de nos sites web.

Prise en compte du zoom utilisateur dans le calcul de valeur relative

Lorsque l’utilisateur modifie le zoom de son navigateur, il redéfinit la taille de police à la racine du document (root). Cette valeur est donc exprimée en « rem » (pour « root em »).

Cette valeur, initialement définit à 16px, pourra ainsi est réécrite « 1rem ».

Nous avons vu que nous pouvions définir notre taille de police comme une fraction (un pourcentage) de la largeur de la fenêtre (ici le viewport x), exprimé en unité viewport-width « vw ». Le calcul de la taille de notre police pourra donc être intégrée en pourcentage, d’un viewport donné, auquel on ajoutera la valeur de notre « rem » :

\[t = v/100.x+r\]

De plus, la fonction clamp() présente l’avantage d’accepter en arguments des opérations de calcul sur des opérandes d’unités différentes – un peu à la manière de la fonction CSS calc() :

.selecteur {
	width : calc( 100vw – 2em ) ;
}

Nous allons utiliser ce comportement pour baser cette fraction du viewport sur la taille relative de la police de l’agent utilisateur (fixée par défaut ou par l’utilisateur lui-même).

L’idée est donc ici d’ajouter à la fraction (v) du viewport (x), pour lequel nous souhaitons définir une taille de police de référence, la taille relative du zoom du navigateur (r) :

.mon-titre {
	font-size : clamp( minRem, v/100.x + r, maxRem )
}

Toute la problématique réside alors dans la définition de la bonne fraction de viewport ainsi que de la bonne taille relative en rem, pour obtenir un calcul utilisable dans la limite des breakpoints de nos média queries.

Votre devis en 48 H chrono !

Demandez à être rappelé !

Nous préciserons ensemble votre projet
de vive voix 

Adaptation aux exigences de breakpoints

L’utilisation des media queries nous a permis jusqu’ici de répondre à des exigences de design définis sur les points de rupture correspondant initialement aux grandes familles de supports (smartphones, tablettes, écrans de bureau, etc.). Une valeur cible pour la taille de notre police est ainsi généralement définie entre deux breakpoints :

@media only screen and ( min-width: 768px) and ( max-width: 1279px ) {
    .container {
        /* minPx + x(minPx + x = maxPx) * ( 100vw - minViewPort) / (maxViewport - minViewPort ) **/
        font-size: calc( 16px + 10 * ( 100vw - 768px ) / (1280 - 768) );
    }
}

Nous avons donc aujourd’hui besoin d’une méthode permettant d’intégrer de ces mêmes breakpoints à notre calcul de de valeur fluide afin de répondre aux mêmes exigences d’intégration web. Voyons cela immédiatement.

Extraction du modèle mathématique de la fonction clamp()

Dans un contexte d’intégration web, la taille de référence de la police de caractères sur un breakpoint précis nous est donnée. Le troisième argument de notre fonction clamp() nous permet ici d’extrapoler une fonction mathématique à 2 inconnues pour lesquels il nous faudra trouver une formule, et permettant de calculer respectivement :

  1. la dimension relative à la largeur de l’écran (v) exprimée en « vw » ;
  2. La valeur du « rem » (r) appliquée au document web.

En mettant de côté les limites min et max de notre fonction clamp() nous allons nous concentrer sur le calcul de la taille de référence de notre police de caractères (t) pour un breakpoint donné (x)…

\[font-size : clamp( minRem, v/100.x + r, maxRem )\]

On pourra donc réécrire assez simplement notre fonction mathématique à 2 inconnues comme suit :

\[t=v/100.x+r\]

2 breakpoints pour résoudre les 2 inconnues de clamp()

Puisque nous disposons de 2 tailles de polices et de 2 breakpoints donnés, nous pouvons écrire 2 fonctions sur ce même modèle, qui nous permettront ensuite de réduire notre équation à 1 seule inconnue, pour calculer tour à tour les valeur x et de v :

Pour un breakpoint à 600px

Viewport 1 = 600px; // x1
Taille 1 = 15px; // t1

// fonction t1

\[t1 = v/100.x1+r\]

Pour un breakpoint à 1280px

Viewport 2 = 1280px; // x2
Taille 2 = 18px; // t2

// fonction t2

\[t2 = v/100.x2+r\]

Afin de réduire notre équation à une seule inconnue, nous allons successivement extraire de t1 des valeurs relatives de r et de v permettant de simplifier notre équation t2.

Commençons par définir dans la fonction t1, une formule de substitution qui nous permettra de calculer v relativement à r au sein de la fonction t2 :

\[t1 = v/100.x1 + r\] \[r = t1 – v/100.x1\]

Nous remplaçons maintenant r dans la fonction t2 pour calculer v :

\[t2 = v/100.x2+r\] \[t2 = v/100.x2 + t1 – v/100.x1\] \[t2 = v/100.(x2-x1) + t1\] \[v/100 = (t2-t1) / (x2-x1)\]

Nous définissons maintenant une formule de substitution qui permettra de remplacer v/100, afin de calculer r au sein de la fonction t2 :

\[t1 = v/100.x1+r\] \[v/100 = (t1- r) / x1\]

Nous intégrons maintenant la valeur fonctionnelle de v/100 à notre fonction t2 pour trouver r, notre valeur en rem (tout ici n’est que problème de distribution) :

\[t2 = v/100.x2 + r\] \[t2 = ( ( t1-r ) / x1 ).x2 + r\] \[t2x1 = ( t1 – r ).x2 + rx1\] \[t2x1 = t1x2 – rx2 + rx1\] \[t2x1 = t1x2 + r(x1 – x2)\] \[( t2x1 – t1x2 ) / ( x1 – x2 ) = r\]

Nous nous retrouvons donc avec 2 fonctions distinctes, nous permettant de calculer les valeurs de v (pourcentage de viewport) et de r (taille relative du navigateur en rem) à passer à la fonction clamp() afin que sont résultat corresponde aux valeurs t1 et t2, attendues sur les 2 viewports x1 et x2 :

\[v = 100( t2 – t1 ) / ( x2 – x1 )\] \[r = ( t2x1 – t1x2 ) / ( x1 – x2 )\]

Nous utilisons finalement dans ces 2 nouvelles fonctions, les valeurs des 2 tailles de polices (15px et 18px) définies pour nos 2 breakpoints (600px et 1280px), afin de calculer les valeurs fluides (de v et r) à passer à la fonction clamp() :

\[v = 100(18 – 15) / (1280 – 600) = .441176470588235vw\] \[r = ( (600 X 18) – (15 X 1280) ) / (600 – 1280)\] \[r = (10800 – 19200) / (-680) = 12.352941176470588px\]

QUID du calcul de padding avec clamp() ?

Maintenant que nous disposons d’un modèle mathématique fiable pour utiliser clamp(), nous pouvons le tester pour intégrer de marges internes fluides dans nous projets web. Ceci devient particulièrement intéressant lorsque nous analysons sur la valeur du « rem » obtenue.

Pour cet exemple, nous calculons un padding fluide variant de 20px (p1) à 45px (p2) entre nos 2 breakpoints de 600px (x1) et 1280px (x2) :

\[v = 100( p2 – p1 ) / ( x2 – x1 )\] \[v = 100(45-20)/680 = 3,676470588235294\] \[r = ( p2x1 – p1x2 ) / ( x1 – x2 )\] \[r = ( p2x1 – p1x2 ) / ( x1 – x2 )\] \[r = ( 27000 – 25600 ) / -680 = -1,4\]

N.B. Nous nous retrouvons ici avec une valeur négative pour notre « rem » qui peut surprendre au premier abord. Mais le test de clamp() avec ces valeurs est sans appel, notre bloc HTML dispose bien d’un padding de 20 à 45px, évoluant de façon fluide entre 600 et 1280px.

Intégrons tout cela immédiatement :

Intégration de la feuille de style

Une pratique commune visant à simplifier l’intégration et la lisibilité des valeurs relatives rem, est de ramener la valeur par défaut du rem à 10px. On effectue cette opération en assignant au sélecteur HTML, une taille relative de police fixée à 62%.

Nous pouvons alors nous contenter de diviser par 10 nos valeurs en pixels pour les passer en unités relatives « rem » :

html {
	font-size: 62.5%;
}
.container p.classy {
	padding: clamp( 2rem, 3.67vw - .14rem , 4.5rem);
	font-size: clamp( 1.5rem, .441176470588235vw + 1.235294rem, 1.8rem );
}
Résultat du calcul de valeurs fluides à l'aide de la fonction CSS clamp(), observables sur l'outil de développement FireFox

Et voilà !

Désormais, et quelle que soit la taille du conteneur central, nos paragraphes bénéficieront d’une taille de police de 15px sur les viewports inférieurs ou égaux à 600px, d’une taille de police fluide allant de 15px à 18px pour les fenêtres de 600px à 1280px, et resteront à cette valeur pour les écrans de dimension supérieure.
Ces blocs présenterons aussi un padding minimum de 2rem et de 4.5rem au maximum, qui s’étendra de manière fluide entre ces 2 mêmes viewports.

Le recours à la fonction CSS clamp() sera de plus en plus répandue à mesure que se développeront les design web fluides. Si quelques CMS (tels que WordPress) commencent à généraliser son usage, seules la compréhension de son fonctionnement interne et quelques manipulations mathématiques permettront au développeur ou à l’intégrateur web de maîtriser son rendu CSS dans les limites d’un design contraint.

Amusez-vous bien  😉 

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *