Catégories
Astuces et Design

Mettre à l'échelle linéairement la taille de la police avec la pince CSS () En fonction de la fenêtre

La typographie responsive a été essayée dans le passé avec une multitude de méthodes telles que les requêtes multimédias et CSS calc().

Ici, nous allons explorer une manière différente de mettre à l'échelle du texte de manière linéaire entre un ensemble de tailles minimale et maximale à mesure que la largeur de la fenêtre augmente, dans le but de rendre son comportement à différentes tailles d'écran plus prévisible – Le tout dans une seule ligne de CSS , grâce à clamp().

La fonction CSS clamp() est un gros frappeur. C'est utile pour une variété de choses, mais c'est particulièrement bien pour la typographie. Voici comment cela fonctionne. Il prend trois valeurs:

clamp(minimum, preferred, maximum);

La valeur qu'elle renvoie sera la valeur préférée, jusqu'à ce que cette valeur préférée soit inférieure à la valeur minimale (à quel point la valeur minimale sera renvoyée) ou supérieure à la valeur maximale (à quel point la valeur maximale sera renvoyée).

Dans cet exemple, la valeur préférée est de 50%. Sur la gauche, 50% de la fenêtre d'affichage de 400 pixels est de 200 pixels, ce qui est inférieur à la valeur minimale de 300 pixels utilisée à la place. Sur la droite, 50% de la fenêtre d'affichage de 1400 pixels équivaut à 700 pixels, ce qui est supérieur à la valeur minimale et inférieur à la valeur maximale de 800 pixels, ce qui équivaut donc à 700 pixels.

Ne serait-ce pas toujours la valeur préférée, en supposant que vous n’êtes pas bizarre et que vous la définissiez entre le minimum et le maximum? Eh bien, vous êtes plutôt censé utiliser une formule pour la valeur préférée, telle que:

.banner {
  width: clamp(200px, 50% + 20px, 800px); /* Yes, you can do math inside clamp()! */
}

Supposons que vous souhaitiez définir le minimum d'un élément font-size à 1rem lorsque la largeur de la fenêtre est de 360 ​​px ou moins, et définissez le maximum sur 3,5rem lorsque la largeur de la fenêtre est de 840 px ou plus.

En d'autres termes:

1rem   = 360px and below
Scaled = 361px - 839px
3.5rem = 840px and above

Toute largeur de fenêtre comprise entre 361 et 839 pixels nécessite une taille de police mise à l'échelle linéairement entre 1 et 3,5rem. C'est en fait super facile avec clamp()! Par exemple, à une largeur de fenêtre de 600 pixels, à mi-chemin entre 360 ​​et 840 pixels, nous obtiendrions exactement la valeur médiane entre 1 et 3,5rem, soit 2,25rem.

Le graphique en courbes avec l'axe vertical mesuré en taille de police rem unit de 0 à 4, et l'axe horizontal mesurant la largeur de la fenêtre de 0 à 1060 pixels. Il y a quatre points bleus sur la grille avec une ligne bleue les reliant.

Ce que nous essayons de réaliser avec clamp() est appelé interpolation linéaire: obtention d'informations intermédiaires entre deux points de données.

Voici les quatre étapes pour ce faire:

Étape 1

Choisissez vos tailles de police minimale et maximale, ainsi que vos largeurs de fenêtre minimale et maximale. Dans notre exemple, cela représente 1rem et 3,5rem pour les tailles de police et 360px et 840px pour les largeurs.

Étape 2

Convertissez les largeurs en rem. Étant donné que 1rem sur la plupart des navigateurs est de 16 pixels par défaut (nous en parlerons plus tard), c'est ce que nous allons utiliser. Ainsi, maintenant, les largeurs de fenêtre minimale et maximale seront respectivement de 22,5 et 52,5 rem.

Étape 3

Ici, nous allons nous pencher un peu sur le côté mathématique. Lorsqu'elles sont associées, les largeurs de la fenêtre et les tailles de police forment deux points sur un système de coordonnées X et Y, et ces points forment une ligne.

Un diagramme de coordonnées bidimensionnel avec deux points et une ligne rouge les coupant.
(22.5, 1) et (52.5, 3.5)

Nous avons un peu besoin de cette ligne – ou plutôt de sa pente et de son intersection avec l'axe Y pour être plus spécifique. Voici comment calculer cela:

slope = (maxFontSize - minFontSize) / (maxWidth - minWidth)
yAxisIntersection = -minWidth * slope + minFontSize

Cela nous donne une valeur de 0,0833 pour la pente et de -0,875 pour l'intersection sur l'axe Y.

Étape 4

Maintenant, nous construisons le clamp() fonction. La formule de la valeur préférée est:

preferredValue = yAxisIntersection(rem) + (slope * 100)(vw)

Donc, la fonction se termine comme ceci:

.header {
  font-size: clamp(1rem, -0.875rem + 8.333vw, 3.5rem);
}

Vous pouvez visualiser le résultat dans la démo suivante:

Allez-y et jouez avec. Comme vous pouvez le voir, la taille de la police cesse de croître lorsque la largeur de la fenêtre est de 840 pixels et cesse de se réduire à 360 pixels. Tout entre les changements de mode linéaire.

Que faire si l’utilisateur modifie la taille de la police de la racine?

Vous avez peut-être remarqué un petit défaut dans toute cette approche: cela ne fonctionne que tant que la taille de la police de la racine est celle que vous pensez qu'elle est – qui est de 16 pixels dans l'exemple précédent – et ne change jamais.

Nous convertissons les largeurs, 360px et 840px, en rem unités en les divisant par 16, car c'est ce que nous supposons être la taille de police de la racine. Si l'utilisateur a défini ses préférences sur une autre taille de police racine, par exemple 18 px au lieu de 16 px par défaut, alors ce calcul sera erroné et le texte ne sera pas redimensionné comme prévu.

Il n'y a qu'une seule approche que nous pouvons utiliser ici, et c'est (1) faire les calculs nécessaires dans le code au chargement de la page, (2) écouter les modifications de la taille de la police de la racine, et (3) tout recalculer si des changements ont lieu .

Voici une fonction JavaScript utile pour effectuer les calculs:

// Takes the viewport widths in pixels and the font sizes in rem
function clampBuilder( minWidthPx, maxWidthPx, minFontSize, maxFontSize ) {
  const root = document.querySelector( "html" );
  const pixelsPerRem = Number( getComputedStyle( root ).fontSize.slice( 0,-2 ) );

  const minWidth = minWidthPx / pixelsPerRem;
  const maxWidth = maxWidthPx / pixelsPerRem;

  const slope = ( maxFontSize - minFontSize ) / ( maxWidth - minWidth );
  const yAxisIntersection = -minWidth * slope + minFontSize

  return `clamp( ${ minFontSize }rem, ${ yAxisIntersection }rem + ${ slope * 100 }vw, ${ maxFontSize }rem )`;
}

// clampBuilder( 360, 840, 1, 3.5 ) -> "clamp( 1rem, -0.875rem + 8.333vw, 3.5rem )"

Je laisse délibérément de côté Comment pour injecter la chaîne retournée dans le CSS car il existe une tonne de façons de le faire en fonction de vos besoins et si vous utilisez du CSS vanilla, une bibliothèque CSS-in-JS ou autre chose. De plus, il n'y a pas d'événement natif pour les changements de taille de police, nous devrions donc le vérifier manuellement. Nous pourrions utiliser setInterval à vérifier toutes les secondes, mais cela pourrait avoir un coût de performance.

Il s'agit plus d'un cas de pointe. Très peu de personnes modifient la taille de la police de leur navigateur et encore moins la modifieront précisément en visitant votre site. Mais si vous voulez que votre site soit aussi réactif que possible, alors c'est la voie à suivre.

Pour ceux qui ne craignent pas ce cas de pointe

Vous pensez pouvoir vivre sans que ce soit parfait? Alors j'ai quelque chose pour toi. J'ai fait un petit outil pour rendre les calculs rapides et simples.

Tout ce que vous avez à faire est de brancher les largeurs et les tailles de police dans l'outil, et la fonction est calculée pour vous. Copiez et collez le résultat dans votre CSS. Ce n’est pas sophistiqué et je suis sûr que beaucoup d’entre eux peuvent être améliorés, mais pour les besoins de cet article, c’est plus que suffisant. N'hésitez pas à bifurquer et à modifier à votre guise.

Comment éviter la redistribution du texte

Avoir un contrôle aussi fin sur les dimensions de la typographie nous permet de faire d'autres choses intéressantes – comme empêcher le texte de refluer à différentes largeurs de fenêtre.

C'est ainsi que le texte se comporte normalement.

Il comporte un certain nombre de lignes à une certaine largeur de fenêtre…
… Et enveloppe ses lignes pour les adapter à une autre largeur

Mais maintenant, avec le contrôle que nous avons, nous pouvons faire en sorte que le texte garde le même nombre de lignes, se coupant toujours sur le même mot, quelle que soit la largeur de la fenêtre que nous lui jetons.

Largeur de la fenêtre = 400 px
Largeur de la fenêtre = 740px

Alors, comment faisons-nous cela? Pour commencer, le rapport entre les tailles de police et les largeurs de fenêtre doit rester le même. Dans cet exemple, nous passons de 1rem à 320px à 3rem à 960px.

320 / 1 = 320
960 / 3 = 320

Si nous utilisons le clampBuilder() fonction que nous avons faite plus tôt, cela devient:

const text = document.querySelector( "p" );
text.style.fontSize = clampBuilder( 320, 960, 1, 3 );

Il garde le même rapport largeur / police. La raison pour laquelle nous faisons cela est que nous devons nous assurer que le texte a la bonne taille à chaque largeur afin qu'il puisse conserver le même nombre de lignes. Il sera toujours redistribué à des largeurs différentes, mais cela est nécessaire pour ce que nous allons faire ensuite.

Maintenant, nous devons obtenir de l'aide du caractère CSS (ch) car il ne suffit pas d'avoir la bonne taille de police. Un ch unit équivaut à la largeur du glyphe «0» dans la police d’un élément. Nous voulons que le corps du texte soit aussi large que la fenêtre, pas en définissant width: 100% mais avec width: Xch, où X est le montant de ch unités (ou 0) nécessaires pour remplir la fenêtre horizontalement.

Trouver X, nous devons diviser la largeur minimale de la fenêtre, 320 px, par l'élément ch quelle que soit la taille de la police lorsque la fenêtre a une largeur de 320 pixels. C’est 1rem dans ce cas.

Ne vous inquiétez pas, voici un extrait de code pour calculer un élément ch Taille:

// Returns the width, in pixels, of the "0" glyph of an element at a desired font size
function calculateCh( element, fontSize ) {
  const zero = document.createElement( "span" );
  zero.innerText = "0";
  zero.style.position = "absolute";
  zero.style.fontSize = fontSize;

  element.appendChild( zero );
  const chPixels = zero.getBoundingClientRect().width;
  element.removeChild( zero );

  return chPixels;
}

Nous pouvons maintenant procéder à la définition de la largeur du texte:

function calculateCh( element, fontSize ) { ... }

const text = document.querySelector( "p" );
text.style.fontSize = clampBuilder( 320, 960, 1, 3 );
text.style.width = `${ 320 / calculateCh(text, "1rem" ) }ch`;
Umm, qui a invité tu à la fête, barre de défilement?

Oh, attendez. Quelque chose d'horrible est arrivé. Il y a une barre de défilement horizontale qui gâche les choses!

Quand on parle de 320px, on parle de la largeur de la fenêtre, comprenant la barre de défilement verticale. Ainsi, la largeur du texte est définie sur la largeur de la zone visible, plus la largeur de la barre de défilement, ce qui la fait déborder horizontalement.

Alors pourquoi ne pas utiliser une statistique qui n'inclut pas la largeur de la barre de défilement verticale? Nous ne pouvons pas et c'est à cause du CSS vw unité. Rappelez-vous, nous utilisons vw dans clamp() pour contrôler les tailles de police. Vous voyez, vw inclut la largeur de la barre de défilement verticale qui rend la police à l'échelle le long de la largeur de la fenêtre, y compris la barre de défilement. Si nous voulons éviter toute redistribution, la largeur doit être proportionnelle à la largeur de la fenêtre, y compris la barre de défilement.

Alors que faisons-nous? Lorsque nous faisons cela:

text.style.width = `${ 320 / calculateCh(text, "1rem") }ch`;

… Nous pouvons réduire le résultat en le multipliant par un nombre inférieur à 1. 0,9 fait l'affaire. Cela signifie que la largeur du texte correspondra à 90% de la largeur de la fenêtre, ce qui représentera largement la petite quantité d’espace occupé par la barre de défilement. Nous pouvons le rendre plus étroit en utilisant un nombre encore plus petit, comme 0,6.

function calculateCh( element, fontSize ) { ... }

const text = document.querySelector( "p" );
text.style.fontSize = clampBuilder( 20, 960, 1, 3 );
text.style.width = `${ 320 / calculateCh(text, "1rem" ) * 0.9 }ch`;
Tellement long, barre de défilement!

Vous pourriez être tenté de simplement soustraire quelques pixels de 320 pour ignorer la barre de défilement, comme ceci:

text.style.width = `${ ( 320 - 30 ) / calculateCh( text, "1rem" ) }ch`;

Le problème avec ceci est que cela ramène le problème de refusion! En effet, la soustraction de 320 casse le rapport fenêtre / police.

Largeur de la fenêtre = 650px
Largeur de la fenêtre = 670px

La largeur du texte doit toujours être un pourcentage de la largeur de la fenêtre. Une autre chose à garder à l'esprit est que nous devons nous assurer que nous chargeons la même police sur chaque appareil utilisant le site. Cela semble évident, n'est-ce pas? Eh bien, voici un petit détail qui pourrait perturber votre texte. Faire quelque chose comme font-family: sans-serif ne garantit pas que la même police est utilisée dans tous les navigateurs. sans-serif mettra Arial sur Chrome pour Windows, mais Roboto sur Chrome pour Android. De plus, la géométrie de certaines polices peut provoquer une redistribution même si vous faites tout correctement. Les polices monospaces ont tendance à donner les meilleurs résultats. Assurez-vous donc toujours que vos polices sont à jour.

Consultez cet exemple de non-redistribution dans la démo suivante:

Texte non redistribuable à l'intérieur d'un conteneur

Tout ce que nous avons à faire est maintenant d'appliquer la taille et la largeur de la police au conteneur au lieu des éléments de texte directement. Le texte qu'il contient devra simplement être défini sur width: 100%. Cela n’est pas nécessaire dans le cas des paragraphes et des en-têtes, car ils sont de toute façon des éléments de niveau bloc et remplissent automatiquement la largeur du conteneur.

Un avantage de l'appliquer dans un conteneur parent est que ses enfants réagiront et se redimensionneront automatiquement sans avoir à définir leurs tailles et largeurs de police une par une. De plus, si nous devons modifier la taille de la police d'un seul élément sans affecter les autres, tout ce que nous aurons à faire est de changer sa taille de police en une em montant et il sera naturellement relatif à la taille de la police du conteneur.

Le texte non redistribuable est capricieux, mais c'est un effet subtil qui peut apporter une touche agréable à un design!

Emballer

Pour couronner le tout, j'ai rassemblé une petite démonstration de ce à quoi tout cela pourrait ressembler dans un scénario réel.

Dans ce dernier exemple, vous pouvez également modifier la taille de la police racine et le clamp() La fonction sera recalculée automatiquement afin que le texte puisse avoir la bonne taille dans n'importe quelle situation.

Même si l'objectif de cet article est d'utiliser clamp() avec les tailles de police, cette même technique pourrait être utilisée dans n'importe quelle propriété CSS qui reçoit une unité de longueur. Maintenant, je ne te dis pas devrait utilisez-le partout. Plusieurs fois, un bon vieux font-size: 1rem est tout ce dont vous avez besoin. J'essaye juste de te montrer le contrôle que tu peux avoir quand vous en avez besoin.

Personnellement je crois clamp() est l'une des meilleures choses à arriver en CSS et j'ai hâte de voir quels autres usages les gens inventent à mesure que cela devient de plus en plus répandu!

Laisser un commentaire

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