Typographie réactive fluide avec CSS Poly Fluid Sizing
Publié: 2022-03-10Dans 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:
- Le
h1
à la petite mise en page pourrait être22px
- Le
h1
à la disposition moyenne pourrait être24px
- Le
h1
à la grande mise en page pourrait être34px
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.
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 = 22 ⁄ 576 *100 = 24px
Taille 24px @ 768px
large = 24 ⁄ 768 *100 = 34px
Taille 34px @ 992px
large = 34 ⁄ 992 *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 :
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 :
- 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 :
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.
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.
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 :
Maintenant , c'est plus comme ça! Beaucoup plus précis que notre ligne droite. Une équation de régression polynomiale de base ressemble à ceci :
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:
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 ?
Puisque nous ne parlons que de 2 points maintenant, trouver la pente (m) et l'ordonnée à l'origine (b) est trivial :
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.
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.