Typographie réactive fluide avec CSS Poly Fluid Sizing

Publié: 2022-03-10
Résumé rapide ↬ Les mises en page fluides font partie intégrante du développement front-end depuis des années. L'idée de la typographie fluide, cependant, est relativement nouvelle et n'a pas encore été pleinement explorée. Jusqu'à présent, l'idée de la typographie fluide de la plupart des développeurs consistait simplement à utiliser des unités Viewport, peut-être avec des tailles minimales et maximales. Dans cet article, nous allons passer à un autre niveau. Nous allons examiner comment créer une typographie fluide et évolutive sur plusieurs points d'arrêt et tailles de police prédéfinies à l'aide de fonctionnalités de navigateur bien prises en charge et d'une algèbre de base. La meilleure partie est que vous pouvez tout automatiser en utilisant Sass.

Dans cet article, nous allons passer à un autre niveau. Nous allons examiner comment créer une typographie fluide et évolutive sur plusieurs points d'arrêt et tailles de police prédéfinies à l'aide de fonctionnalités de navigateur bien prises en charge et d'une algèbre de base. La meilleure partie est que vous pouvez tout automatiser en utilisant Sass.

Lectures complémentaires sur SmashingMag :

  • Typographie vraiment fluide avec les unités vh et vw
  • Modèles typographiques dans la conception de newsletters par e-mail HTML
  • Le bon, le mauvais et les grands exemples de typographie Web
  • Outils et ressources pour une typographie Web plus significative

Lorsque vous travaillez avec des concepteurs créatifs sur des conceptions de pages Web, il est assez courant de recevoir plusieurs plans de travail/mises en page Sketch ou Photoshop, un pour chaque point d'arrêt. Dans cette conception, les éléments (comme un en-tête h1 ) auront généralement des tailles différentes à chaque point d'arrêt. Par exemple:

Plus après saut! Continuez à lire ci-dessous ↓
  1. Le h1 à la petite mise en page pourrait être 22px
  2. Le h1 à la disposition moyenne pourrait être 24px
  3. Le h1 à la grande mise en page pourrait être 34px

Le CSS minimum pour cela utilise des requêtes multimédia :

 h1 { font-size: 22px; } @media (min-width:576px) { h1 { font-size: 22px; } } @media (min-width:768px) { h1 { font-size: 24px; } } @media (min-width:992px) { h1 { font-size: 34px; } }

C'est une bonne première étape, mais vous limitez la font-size à ce qui a été spécifié par le concepteur aux points d'arrêt fournis. Que dirait le concepteur si vous lui demandiez : « Quelle devrait être la font-size dans une fenêtre d'affichage de 850 pixels de large ? La réponse dans la plupart des cas est que ce serait quelque part entre 24px et 34px. Mais pour le moment, c'est juste 24px selon votre CSS, ce qui n'est probablement pas ce que le concepteur envisageait.

Votre option à ce stade est de calculer quelle devrait être cette taille et d'ajouter un autre point d'arrêt. C'est assez facile. Mais qu'en est-il de toutes les autres résolutions ? Quelle devrait être la font-size à 800 pixels de large ? Qu'en est-il du 900 px ? Qu'en est-il de 935px ? De toute évidence, le concepteur ne vous fournira pas une mise en page complète pour chaque résolution possible. Même s'ils le faisaient, devriez-vous ajouter des dizaines (ou des centaines) de points d'arrêt pour toutes les différentes font-sizes souhaitées par le concepteur ? Bien sûr que non.

Votre mise en page est déjà mise à l'échelle de manière fluide avec la largeur de votre fenêtre d'affichage. Ne serait-il pas agréable que votre typographie s'adapte de manière prévisible à votre mise en page fluide ? Que pouvons-nous faire d'autre pour améliorer cela ?

Les unités de fenêtre à la rescousse ?

Les unités Viewport sont un autre pas dans la bonne direction. Ils permettent à votre texte de se redimensionner de manière fluide avec vos mises en page. Et la prise en charge du navigateur est excellente de nos jours.

Puis-je utiliser la fenêtre ?
(Voir la grande version)

Mais la viabilité des unités Viewport dépend fortement des conceptions créatives originales d'une page Web. Ce serait formidable de simplement définir votre font-size aide de vw et d'avoir terminé :

 h1 { font-size: 2vw; } 

Mais cela ne fonctionne que si vos tableaux d'art créatifs en tiennent compte. Le designer a-t-il choisi une taille de texte correspondant exactement à 2 % de la largeur de chacun de ses plans de travail ? Bien sûr que non. Calculons ce que la valeur vw devrait être pour chacun de nos points d'arrêt :

Taille 22px @ 576px large = 22576 *100 = 24px Taille 24px @ 768px large = 24768 *100 = 34px Taille 34px @ 992px large = 34992 *100 = 3.43vw

Ils sont proches mais ils ne sont pas tous pareils. Vous auriez donc toujours besoin d'utiliser des requêtes multimédias pour passer d'une taille de texte à l'autre et il y aurait toujours des sauts. Et considérez cet effet secondaire étrange :

@ 767px, 3,82% de la largeur de la fenêtre est de 29px. Si la fenêtre d'affichage est plus large d'un pixel, la font-size redescend soudainement à 24px . Cette animation d'une fenêtre en cours de redimensionnement illustre cet effet secondaire indésirable :

Ce changement spectaculaire de la taille de la police n'est certainement pas ce que le concepteur envisageait. Alors, comment résolvons-nous ce problème?

Régression linéaire statistique ?

Attendez. Quoi? Oui, il s'agit d'un article sur CSS, mais quelques mathématiques de base peuvent grandement contribuer à une solution élégante à notre problème.

Tout d'abord, traçons nos résolutions et les tailles de texte correspondantes sur un graphique :

Nuage de points de la taille de la police et de la largeur de la fenêtre d'affichage correspondante
Nuage de points de font-size et de la largeur de la fenêtre d'affichage correspondante (Google Spreadsheets) (Voir la grande version)

Ici, vous pouvez voir un nuage de points des tailles de texte spécifiées par le concepteur aux largeurs de fenêtre définies. L'axe des x est la largeur de la fenêtre et l'axe des y est la font-size . Vous voyez cette ligne ? C'est ce qu'on appelle une ligne de tendance . C'est un moyen de trouver une valeur de font-size interpolée pour n'importe quelle largeur de fenêtre, en fonction des données fournies.

La ligne de tendance est la clé de tout cela

Si vous pouviez définir votre font-size fonction de cette ligne de tendance, vous auriez un h1 qui évolue en douceur sur toutes les résolutions qui se rapprocheraient de ce que le concepteur avait prévu. Tout d'abord, regardons les mathématiques. La droite est définie par cette équation :

Définition de l'équation linéaire
Définition de l'équation linéaire
  • m = pente
  • b = l'ordonnée à l'origine
  • x = la largeur actuelle de la fenêtre
  • y = la font-size résultante

Il existe plusieurs méthodes pour déterminer la pente et l'ordonnée à l'origine. Lorsque plusieurs valeurs sont impliquées, une méthode courante est l'ajustement des moindres carrés :

Moindres carrés

Une fois que vous avez exécuté ces calculs, vous avez votre équation de ligne de tendance.

Comment puis-je l'utiliser en CSS ?

D'accord, cela devient assez lourd sur les maths. Comment utilisons-nous réellement ces éléments dans le développement Web frontal ? La réponse est CSS calc() ! Encore une fois, une technologie CSS assez nouvelle qui est très bien supportée.

Puis-je utiliser Calc ?
(Voir la grande version)

Vous pouvez utiliser l'équation de la courbe de tendance comme ceci :

 h1 { font-size: calc({slope}*100vw + {y-intercept}px); }

Une fois que vous avez trouvé votre pente et votre ordonnée à l'origine, il vous suffit de les brancher !

Remarque : Vous devez multiplier la pente par 100 puisque vous l'utilisez comme une unité vw qui correspond à 1/100e de la largeur de la fenêtre.

Cela peut-il être automatisé ?

J'ai porté la méthode d'ajustement des moindres carrés dans une fonction Sass facile à utiliser :

 /// least-squares-fit /// Calculate the least square fit linear regression of provided values /// @param {map} $map - A Sass map of viewport width and size value combinations /// @return Linear equation as a calc() function /// @example /// font-size: least-squares-fit((576px: 24px, 768px: 24px, 992px: 34px)); /// @author Jake Wilson <[email protected]> @function least-squares-fit($map) { // Get the number of provided breakpoints $length: length(map-keys($map)); // Error if the number of breakpoints is < 2 @if ($length < 2) { @error "leastSquaresFit() $map must be at least 2 values" } // Calculate the Means $resTotal: 0; $valueTotal: 0; @each $res, $value in $map { $resTotal: $resTotal + $res; $valueTotal: $valueTotal + $value; } $resMean: $resTotal/$length; $valueMean: $valueTotal/$length; // Calculate some other stuff $multipliedDiff: 0; $squaredDiff: 0; @each $res, $value in $map { // Differences from means $resDiff: $res - $resMean; $valueDiff: $value - $valueMean; // Sum of multiplied differences $multipliedDiff: $multipliedDiff + ($resDiff * $valueDiff); // Sum of squared resolution differences $squaredDiff: $squaredDiff + ($resDiff * $resDiff); } // Calculate the Slope $m: $multipliedDiff / $squaredDiff; // Calculate the Y-Intercept $b: $valueMean - ($m * $resMean); // Return the CSS calc equation @return calc(#{$m*100}vw + #{$b}); }

Cela fonctionne-t-il vraiment ? Ouvrez ce CodePen et redimensionnez la fenêtre de votre navigateur. Ça marche! Les tailles de police sont assez proches de ce que la conception originale demandait et elles s'adaptent en douceur à votre mise en page.

Test SCSS d'ajustement des moindres carrés user="jakobud"]Voir le test SCSS d'ajustement des moindres carrés du stylo par Jake Wilson (@jakobud) sur CodePen.

Least Squares Fit SCSS test user=“jakobud”]Voir le test Pen Least Squares Fit SCSS par Jake Wilson (@jakobud) sur CodePen.

Maintenant, certes, ce n'est pas parfait. Les valeurs sont proches de la conception originale mais elles ne correspondent pas tout à fait. En effet, une courbe de tendance linéaire est une approximation de tailles de police spécifiques à des largeurs de fenêtre spécifiques. C'est l'héritage de la régression linéaire. Il y a toujours une erreur dans vos résultats. C'est un compromis entre simplicité et précision. De plus, gardez à l'esprit que plus les tailles de texte sont variées, plus il y aura d'erreurs dans votre ligne de tendance.

Pouvons-nous faire mieux que cela ?

Ajustement polynomial des moindres carrés

Afin d'obtenir une courbe de tendance plus précise, vous devez examiner des sujets plus avancés, comme une courbe de tendance de régression polynomiale qui pourrait ressembler à ceci :

Courbe de tendance de régression polynomiale
Courbe de tendance de régression polynomiale (Google Spreadsheets) (Voir la grande version)

Maintenant , c'est plus comme ça! Beaucoup plus précis que notre ligne droite. Une équation de régression polynomiale de base ressemble à ceci :

Une équation polynomiale du 3ème degré
Une équation polynomiale du 3ème degré

Plus vous voulez que votre courbe soit précise, plus l'équation devient compliquée. Malheureusement, vous ne pouvez pas le faire en CSS . calc() ne peut tout simplement pas faire ce type de calcul avancé. Plus précisément, vous ne pouvez pas calculer les exposants :

 font-size: calc(3vw * 3vw); /* This doesn't work in CSS */

Ainsi, tant que calc() ne prend pas en charge ce type de calculs non linéaires, nous sommes bloqués avec des équations linéaires uniquement . Pouvons-nous faire autre chose pour améliorer cela ?

Points de rupture et équations linéaires multiples

Et si nous ne calculions qu'une ligne droite entre chaque paire de points d'arrêt ? Quelque chose comme ça:

Courbes de tendance de régression linéaire entre plusieurs paires de valeurs
Courbes de tendance de régression linéaire entre plusieurs paires de valeurs (Google Spreadsheets) (Voir la version agrandie)

Ainsi, dans cet exemple, nous calculerions la ligne droite entre 22px et 24px , puis une autre entre 24px et 34px . Le Sass ressemblerait à ceci :

 // SCSS h1 { @media (min-width:576px) { font-size: calc(???); } @media (min-width:768px) { font-size: calc(???); } }

Nous pourrions utiliser la méthode d'ajustement des moindres carrés pour ces valeurs calc() , mais comme il ne s'agit que d'une ligne droite entre 2 points, les calculs pourraient être grandement simplifiés. Vous souvenez-vous de l'équation d'une ligne droite ?

Définition de l'équation linéaire
Définition de l'équation linéaire

Puisque nous ne parlons que de 2 points maintenant, trouver la pente (m) et l'ordonnée à l'origine (b) est trivial :

Trouver la pente et l'ordonnée à l'origine d'une équation linéaire
Trouver la pente et l'ordonnée à l'origine d'une équation linéaire

Voici une fonction Sass pour cela :

 /// linear-interpolation /// Calculate the definition of a line between two points /// @param $map - A Sass map of viewport widths and size value pairs /// @returns A linear equation as a calc() function /// @example /// font-size: linear-interpolation((320px: 18px, 768px: 26px)); /// @author Jake Wilson <[email protected]> @function linear-interpolation($map) { $keys: map-keys($map); @if (length($keys) != 2) { @error "linear-interpolation() $map must be exactly 2 values"; } // The slope $m: (map-get($map, nth($keys, 2)) - map-get($map, nth($keys, 1)))/(nth($keys, 2) - nth($keys,1)); // The y-intercept $b: map-get($map, nth($keys, 1)) - $m * nth($keys, 1); // Determine if the sign should be positive or negative $sign: "+"; @if ($b < 0) { $sign: "-"; $b: abs($b); } @return calc(#{$m*100}vw #{$sign} #{$b}); }

Maintenant, utilisez simplement la fonction d'interpolation linéaire sur plusieurs points d'arrêt dans votre Sass. Ajoutons également quelques font-sizes min et max :

 // SCSS h1 { // Minimum font-size font-size: 22px; // Font-size between 576 - 768 @media (min-width:576px) { $map: (576px: 22px, 768px: 24px); font-size: linear-interpolation($map); } // Font-size between 768 - 992 @media (min-width:768px) { $map: (768px: 24px, 992px: 34px); font-size: linear-interpolation($map); } // Maximum font-size @media (min-width:992px) { font-size: 34px; } }

Et il génère ce CSS :

 h1 { font-size: 22px; } @media (min-width: 576px) { h1 { font-size: calc(1.04166667vw + 16px); } } @media (min-width: 768px) { h1 { font-size: calc(4.46428571vw - 10.28571429px); } } @media (min-width: 992px) { h1 { font-size: 34px; } } 

Le Saint Graal du dimensionnement CSS ?

Enveloppons tout cela dans un joli mixin Sass (pour les paresseux et les efficaces !). J'invente cette méthode Poly Fluid Sizing :

 /// poly-fluid-sizing /// Generate linear interpolated size values through multiple break points /// @param $property - A string CSS property name /// @param $map - A Sass map of viewport unit and size value pairs /// @requires function linear-interpolation /// @requires function map-sort /// @example /// @include poly-fluid-sizing('font-size', (576px: 22px, 768px: 24px, 992px: 34px)); /// @author Jake Wilson <[email protected]> @mixin poly-fluid-sizing($property, $map) { // Get the number of provided breakpoints $length: length(map-keys($map)); // Error if the number of breakpoints is < 2 @if ($length < 2) { @error "poly-fluid-sizing() $map requires at least values" } // Sort the map by viewport width (key) $map: map-sort($map); $keys: map-keys($map); // Minimum size #{$property}: map-get($map, nth($keys,1)); // Interpolated size through breakpoints @for $i from 1 through ($length - 1) { @media (min-width:nth($keys,$i)) { $value1: map-get($map, nth($keys,$i)); $value2: map-get($map, nth($keys,($i + 1))); // If values are not equal, perform linear interpolation @if ($value1 != $value2) { #{$property}: linear-interpolation((nth($keys,$i): $value1, nth($keys,($i+1)): $value2)); } @else { #{$property}: $value1; } } } // Maxmimum size @media (min-width:nth($keys,$length)) { #{$property}: map-get($map, nth($keys,$length)); } }

Ce mixin Sass nécessite quelques fonctions Sass dans les Github suivants :

  • interpolation linéaire
  • tri par carte
  • tri de liste
  • suppression de liste

Le mixin poly-fluid-sizing() effectuera une interpolation linéaire sur chaque paire de largeurs de fenêtre et définira une taille minimale et maximale. Vous pouvez l'importer dans n'importe quel projet Sass et l'utiliser facilement sans avoir besoin de connaître les mathématiques sous-jacentes. Voici le CodePen final qui utilise cette méthode.

Dimensionnement de poly fluide à l'aide d'équations linéaires, d'unités de fenêtre et de calc() user="jakobud"]Voir le stylo Dimensionnement de poly fluide à l'aide d'équations linéaires, d'unités de fenêtre et de calc()"] Dimensionnement de poly fluide à l'aide d'équations linéaires, d'unités de fenêtre et de calc() par Jake Wilson (@jakobud) sur CodePen.

Dimensionnement de poly fluide à l'aide d'équations linéaires, d'unités de fenêtre et de calc() user=“jakobud”]Voir le stylet Dimensionnement de poly fluide à l'aide d'équations linéaires, d'unités de fenêtre et de calc()“] Dimensionnement de poly fluide à l'aide d'équations linéaires, d'unités de fenêtre et de calc() par Jake Wilson (@jakobud) sur CodePen.

Quelques remarques

  • Évidemment, cette méthode s'applique non seulement à font-size mais à toute propriété d'unité/longueur ( margin , padding , etc.). Vous transmettez le nom de la propriété souhaitée dans le mixin sous forme de chaîne.
  • La carte Sass des paires largeur de fenêtre + valeur de taille peut être passée dans n'importe quel ordre dans le mixin poly-fluid-sizing() . Il triera automatiquement la carte en fonction de la largeur de la fenêtre, du plus bas au plus haut . Vous pourriez donc passer une carte comme celle-ci et cela fonctionnerait très bien:
 $map: (576px: 22px, 320px: 18px, 992px: 34px, 768px: 24px); @include poly-fluid-sizing('font-size', $map);
  • Une limitation de cette méthode est que vous ne pouvez pas passer d'unités mixtes dans le mixin. Par exemple, 3em @ 576px largeur. Sass ne saura tout simplement pas quoi faire mathématiquement là-bas.

Conclusion

Est-ce le mieux que nous puissions faire ? Le Poly Fluid Sizing est-il le Saint Graal du dimensionnement des unités fluides en CSS ? Peut-être. CSS prend actuellement en charge les fonctions d'animation non linéaire et de synchronisation des transitions, il est donc possible que calc() le prenne également en charge un jour. Si cela se produit, la régression polynomiale non linéaire pourrait valoir la peine d'être examinée à nouveau. Mais peut-être pas… La mise à l'échelle linéaire pourrait être supérieure de toute façon.

J'ai commencé à explorer cette idée au début de 2017 et j'ai finalement développé la solution ci-dessus. Depuis lors, j'ai vu quelques développeurs proposer des idées similaires et différentes pièces de ce puzzle. J'ai pensé qu'il était temps pour moi de partager ma méthode et comment j'y suis arrivé. Unités de fenêtre. Calc(). Toupet. Points d'arrêt. Aucune de ces choses n'est nouvelle. Ce sont toutes des fonctionnalités de navigateur qui existent depuis des années (avec différents degrés de prise en charge). Je ne les ai utilisés ensemble que d'une manière qui n'avait pas encore été complètement explorée. N'ayez jamais peur de regarder les outils que vous utilisez tous les jours et de penser à la façon dont vous pouvez mieux les utiliser et développer vos compétences.