Un moyen intelligent de contrôler et de synchroniser la typo et l’espace
Et si nous pouvions mettre en œuvre les tailles de contribution du designer sans tracas ? Et si nous pouvions définir des points d’ancrage personnalisés pour générer une valeur parfaitement réactive, nous donnant plus d’options à mesure que l’approche de la taille fluide ? Et si nous avions une formule magique qui contrôlait et synchronisait tout le projet ?
Il arrive souvent que je reçoive deux modèles du concepteur : un pour le mobile et un pour le bureau. Dernièrement, je me suis demandé comment automatiser le processus et optimiser le résultat. Comment implémenter les tailles spécifiées le plus efficacement ? Comment puis-je garantir une vue confortable sur la tablette ? Comment puis-je optimiser la sortie pour les grands écrans ? Comment puis-je réagir à des rapports hauteur/largeur extrêmes ?
J’aimerais pouvoir lire les deux valeurs des différentes tailles (police, espaces, etc.) en pixel et les entrer comme arguments dans une fonction qui fait tout le travail pour moi. Je veux créer ma propre formule magique responsive, ma FabUnit.
Lorsque j’ai commencé à travailler sur ce sujet au printemps et que j’ai lancé la FabUnit, je suis tombé sur cet article intéressant de Adrien. En attendant, Rouslan et Brecht ont également fait des recherches dans ce sens et présenté des réflexions intéressantes.
Comment puis-je mettre en œuvre les modèles de conception le plus efficacement ?
Je suis fatigué d’écrire des requêtes multimédias pour chaque valeur et je veux éviter les sauts de conception. L’entretien et le résultat ne sont pas satisfaisants. Alors quelle est la meilleure façon de mettre en œuvre la contribution du designer ?
Qu’en est-il des tailles de fluides ? Il existe des calculatrices pratiques comme utopie ou Min-Max-Calculatrice.
Mais pour mes projets, j’ai généralement besoin de plus d’options de réglage. La vue de la tablette s’avère souvent trop petite et je ne peux ni réagir à des fenêtres plus grandes ni au rapport d’aspect.
Et ce serait bien d’avoir une synchronisation proportionnelle sur l’ensemble du projet. Je peux définir des variables globales avec les valeurs calculées, mais je veux aussi pouvoir générer une valeur interpolée localement dans les composants sans aucun effort. Je voudrais dessiner ma propre ligne réactive. J’ai donc besoin d’un outil qui crache la valeur parfaite en fonction de plusieurs points d’ancrage (définitions d’écran) et automatise les processus pour la plupart de mes projets. Mon outil doit être rapide et facile à utiliser, et le code doit être lisible et maintenable.
Sur quelles constantes des spécifications de conception nos calculs doivent-ils être basés ?
Reprenons notre exemple précédent :
La taille de la police du corps doit être 16px
sur mobile et 22px
sur le bureau (nous traiterons du guide de style complet plus tard). Les tailles mobiles doivent commencer à 375px
et s’adapter en permanence à 1024px
. Jusqu’à 1440px
, les tailles doivent rester statiquement à l’optimum. Après cela, les valeurs doivent évoluer linéairement jusqu’à 2000px
après quoi le max-wrapper prend effet.
Cela nous donne les constantes suivantes qui s’appliquent à l’ensemble du projet :
Xf 375px global screen-min
Xa 1024px global screen-opt-start
Xb 1440px global screen-opt-end
Xu 2000px global screen-max
La taille de la police du corps doit être d’au moins 16px
idéalement 22px
. La taille de police maximale à 2000px
doit être calculé automatiquement :
Yf 16px local size-min
Ya 22px
Yb 22px local size-opt
Yu auto
Donc, à la fin de la journée, ma fonction devrait pouvoir prendre deux arguments – dans ce cas, 16
et 22
.
fab-unit(16, 22);
Le calcul
Si vous n’êtes pas intéressé par la dérivation mathématique de la formule, n’hésitez pas à passer directement à la section « Comment utiliser la FabUnit ?”.
Alors, commençons!
Définir les pinces principales
Tout d’abord, nous devons déterminer quelles pinces principales nous voulons régler.
clamp1: clamp(Yf, slope1, Ya)
clamp2: clamp(Yb, slope2, Yu)
Combinez et emboîtez les pinces
Maintenant, nous devons combiner les deux pinces. Cela pourrait devenir un peu délicat. Nous devons considérer que les deux lignes, slope1
et slope2
, peuvent s’écraser, selon leur pente. Puisque nous savons que slope2
doit être de 45 degrés ou 100 % (m = 1), nous pouvons nous demander si slope1
est supérieur à 1. De cette façon, nous pouvons définir une pince différente en fonction de la façon dont les lignes se croisent.
Si slope1
est plus raide que slope2
nous combinons les pinces comme ceci :
clamp(Yf, slope1, clamp(Yb, slope2, Yu))
Si slope1
est plus plat que slope2
on fait ce calcul :
clamp(clamp(Yf, slope1, Ya), slope2, Yu)
Combiné:
steep-slope
? clamp(Yf, slope1, clamp(Yb, slope2, Yu))
: clamp(clamp(Yf, slope1, Ya), slope2, Yu)
Définir l’enveloppe maximale en option
Que se passe-t-il si nous n’avons pas d’enveloppe maximale qui fige le dessin au-delà d’une certaine largeur ?
Tout d’abord, nous devons déterminer quelles pinces principales nous voulons régler.
clamp1: clamp(Yf, slope1, Ya)
max: max(Yb, slope2)
Si slope1
est plus raide que slope2
:
clamp(Yf, slope1, max(Yb, slope2))
Si slope1
est plus plat que slope2
:
max(clamp(Yf, slope1, Ya), slope2)
Le calcul sans enveloppe — élastique vers le haut :
steep-slope
? clamp(Yf, slope1, max(Yb, slope2))
: max(clamp(Yf, slope1, Ya), slope2)
Combiné, avec wrapper max en option (si screen-max Xu
est défini) :
Xu
? steep-slope
? clamp(Yf, slope1, clamp(Yb, slope2, Yu))
: max(clamp(Yf, slope1, Ya), Yu)
: steep-slope
? clamp(Yf, slope1, max(Yb, slope2))
: max(clamp(Yf, slope1, Ya), slope2)
Ainsi, nous avons construit la structure de base de la formule. Maintenant, nous plongeons un peu plus profondément.
Calculer les valeurs manquantes
Voyons quelles valeurs nous obtenons comme argument et lesquelles nous devons calculer maintenant :
steep-slope
? clamp(Yf, slope1, clamp(Yb, slope2, Yu))
: max(clamp(Yf, slope1, Ya), Yu)
pente raide
Ya = Yb = 22px
Yf = 16px
pente1 = AMF
pente2 = Première
Yu
steep-slope
Vérifiez si la penteYf → Ya
est au-dessus de la penteYb → Yu
(m = 1) :
((Ya - Yf) / (Xa - Xf)) * 100 > 1
Mfa
Interpolation linéaire, y compris le calcul de la penteYf → Ya
:
Yf + (Ya - Yf) * (100vw - Xf) / (Xa - Xf)
Mbu
Interpolation linéaire entreYb
etYu
(pente m = 1):
100vw / Xb * Yb
Yu
Calcul de la position deYu
:
(Xu / Xb) * Yb
Tout mettre ensemble
Xu
? ((Ya - Yf) / (Xa - Xf)) * 100 > 1
? clamp(Yf, Yf + (Ya - Yf) * (100vw - Xf) / (Xa - Xf), clamp(Yb, 100vw / Xb * Yb, (Xu / Xb) * Yb))
: max(clamp(Yf, Yf + (Ya - Yf) * (100vw - Xf) / (Xa - Xf), Ya), (Xu / Xb) * Yb)
: ((Ya - Yf) / (Xa - Xf)) * 100 > 1
? clamp(Yf, Yf + (Ya - Yf) * (100vw - Xf) / (Xa - Xf), max(Yb, 100vw / Xb * Yb))
: max(clamp(Yf, Yf + (Ya - Yf) * (100vw - Xf) / (Xa - Xf), Ya), 100vw / Xb * Yb)
Nous ferions mieux de stocker certains calculs dans des variables :
steep-slope = ((Ya - Yf) / (Xa - Xf)) * 100 > 1
slope1 = Yf + (Ya - Yf) * (100vw - Xf) / (Xa - Xf)
slope2 = 100vw / Xb * Yb
Yu = (Xu / Xb) * Yb
Xu
? steep-slope
? clamp(Yf, slope1, clamp(Yb, slope2, Yu))
: max(clamp(Yf, slope1, Ya), Yu)
: steep-slope
? clamp(Yf, slope1, max(Yb, slope2))
: max(clamp(Yf, slope1, Ya), slope2)
Inclure le rapport hauteur/largeur
Parce que nous voyons maintenant comment le chat saute, nous nous régalons d’un autre cookie. Dans le cas d’un format extrêmement large, par exemple un paysage d’appareils mobiles, nous souhaitons à nouveau réduire les tailles. C’est plus agréable et lisible ainsi.
Et si nous pouvions inclure le rapport d’aspect dans nos calculs ? Dans cet exemple, nous voulons réduire les tailles lorsque l’écran est plus large que le rapport hauteur/largeur de 16:9.
aspect-ratio = 16 / 9
screen-factor = min(100vw, 100vh * aspect-ratio)
Dans les deux interpolations de pente, nous remplaçons simplement 100vw
avec le nouveau facteur d’écran.
slope1 = Yf + (Ya - Yf) * (screen-factor - Xf) / (Xa - Xf)
slope2 = screen-factor / Xb * Yb
Alors, finalement, ça y est. Regardons toute la formule magique maintenant.
Formule
screen-factor = min(100vw, 100vh * aspect-ratio)
steep-slope = ((Ya - Yf) / (Xa - Xf)) * 100 > 1
slope1 = Yf + (Ya - Yf) * (screen-factor - Xf) / (Xa - Xf)
slope2 = screen-factor / Xb * Yb
Yu = (Xu / Xb) * Yb
Xu
? steep-slope
? clamp(Yf, slope1, clamp(Yb, slope2, Yu))
: max(clamp(Yf, slope1, Ya), Yu)
: steep-slope
? clamp(Yf, slope1, max(Yb, slope2))
: max(clamp(Yf, slope1, Ya), slope2)
Fonction
Nous pouvons maintenant intégrer la formule dans notre configuration. Dans cet article, nous verrons comment l’implémenter dans Sass. Les deux fonctions d’assistance garantissent que nous produisons correctement les valeurs rem (je n’entrerai pas dans les détails). Ensuite, nous définissons les points d’ancrage et le rapport d’aspect comme des constantes (respectivement, des variables Sass). Enfin, nous remplaçons les points de coordonnées de notre formule par des noms de variables, et la FabUnit est prête à l’emploi.
_fab-unit.scss
@use "sass:math";
/* Helper functions */
$rem-base: 10px;
@function strip-units($number) {
@if (math.is-unitless($number)) {
@return $number;
} @else {
@return math.div($number, $number * 0 + 1);
}
}
@function rem($size){
@if (math.compatible($size, 1rem) and not math.is-unitless($size)) {
@return $size;
} @else {
@return math.div(strip-units($size), strip-units($rem-base)) * 1rem;
}
}
/* Default values fab-unit 🪄 */
$screen-min: 375;
$screen-opt-start: 1024;
$screen-opt-end: 1440;
$screen-max: 2000; // $screen-opt-end | int > $screen-opt-end | false
$aspect-ratio: math.div(16, 9); // smaller values for larger aspect ratios
/* Magic function fab-unit 🪄 */
@function fab-unit(
$size-min,
$size-opt,
$screen-min: $screen-min,
$screen-opt-start: $screen-opt-start,
$screen-opt-end: $screen-opt-end,
$screen-max: $screen-max,
$aspect-ratio: $aspect-ratio
) {
$screen-factor: min(100vw, 100vh * $aspect-ratio);
$steep-slope: math.div(($size-opt - $size-min), ($screen-opt-start - $screen-min)) * 100 > 1;
$slope1: calc(rem($size-min) + ($size-opt - $size-min) * ($screen-factor - rem($screen-min)) / ($screen-opt-start - $screen-min));
$slope2: calc($screen-factor / $screen-opt-end * $size-opt);
@if $screen-max {
$size-max: math.div(rem($screen-max), $screen-opt-end) * $size-opt;
@if $steep-slope {
@return clamp(rem($size-min), $slope1, clamp(rem($size-opt), $slope2, $size-max));
} @else {
@return clamp(clamp(rem($size-min), $slope1, rem($size-opt)), $slope2, $size-max);
}
} @else {
@if $steep-slope {
@return clamp(rem($size-min), $slope1, max(rem($size-opt), $slope2));
} @else {
@return max(clamp(rem($size-min), $slope1, rem($size-opt)), $slope2);
}
}
}
Comment utiliser la FabUnit ?
Le travail est fait, maintenant c’est simple. Le guide de style de notre exemple peut être mis en œuvre en un rien de temps :
Nous lisons les valeurs associées dans le guide de style et les transmettons au FabUnit en tant qu’arguments : fab-unit(16, 22)
.
style.scss
@import "fab-unit";
/* overwrite default values 🪄 */
$screen-max: 1800;
/* Style guide variables fab-unit 🪄 */
$fab-font-size-body: fab-unit(16, 22);
$fab-font-size-body-small: fab-unit(14, 16);
$fab-font-size-h1: fab-unit(60, 160);
$fab-font-size-h2: fab-unit(42, 110);
$fab-font-size-h3: fab-unit(28, 60);
$fab-space-s: fab-unit(20, 30);
$fab-space-m: fab-unit(40, 80);
$fab-space-l: fab-unit(60, 120);
$fab-space-xl: fab-unit(80, 180);
/* fab-unit in action 🪄 */
html {
font-size: 100% * math.div(strip-units($rem-base), 16);
}
body {
font-size: $fab-font-size-body;
}
.wrapper {
max-width: rem($screen-max);
margin-inline: auto;
padding: $fab-space-m;
}
h1 {
font-size: $fab-font-size-h1;
border-block-end: fab-unit(2, 10) solid plum;
}
…
p {
margin-block: $fab-space-s;
}
…
/* other use cases for calling fab-unit 🪄 */
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(fab-unit(200, 500), 1fr));
gap: $fab-space-m;
}
.thing {
flex: 0 0 fab-unit(20, 30);
height: fab-unit(20, 36, 660, 800, 1600, 1800); /* min, opt, … custom anchor points */
}
Nous sommes maintenant en mesure de tracer la ligne responsive en appelant fab-unit()
et en spécifiant seulement deux tailles, la minimale et l’optimale. Nous pouvons contrôler les tailles de police, les rembourrages, les marges et les espaces, les hauteurs et les largeurs, et même – si nous le voulons – définir des colonnes de grille et des mises en page flexibles avec. Nous sommes également en mesure de déplacer localement les points d’ancrage prédéfinis.
Jetons un coup d’œil à la sortie compilée :
…
font-size: clamp(clamp(1.3rem, 1.3rem + 2 * (min(100vw, 177.7777777778vh) - 37.5rem) / 649, 1.5rem), min(100vw, 177.7777777778vh) / 1440 * 15, 2.0833333333rem);
…
Et la sortie calculée :
font-size: 17.3542px
Problèmes d’accessibilité
Pour assurer une bonne accessibilité, je recommande de tester dans chaque cas si toutes les tailles sont suffisamment zoomables. Les arguments avec une grande différence peuvent ne pas se comporter comme souhaité. Pour plus d’informations sur ce sujet, vous pouvez consulter l’article « Type et zoom réactifs» d’Adrian Roselli.
Conclusion
Maintenant, nous avons créé une fonction qui fait tout le travail pour nous. Il prend une valeur minimale et optimale et crache un calcul sur notre propriété CSS, en tenant compte de la largeur de l’écran, du rapport d’aspect et des points d’ancrage spécifiés – une formule unique qui pilote l’ensemble du projet. Pas de requêtes multimédias, pas de points d’arrêt, pas de sauts de conception.
La FabUnit présentée ici est basée sur ma propre expérience et est optimisée pour la plupart de mes projets. Je gagne beaucoup de temps et je suis satisfait du résultat. Il se peut que vous et votre équipe ayez une autre approche et ayez donc d’autres exigences pour une FabUnit. Ce serait bien si vous pouviez maintenant créer votre propre FabUnit en fonction de vos besoins.
Je serais heureux si mon approche vous inspirait de nouvelles idées. Je serais honoré si vous utilisiez directement le paquet npm de la FabUnit de cet article pour vos projets.
Merci! 🙏🏻
LIENS FAB-UNITÉS
Merci Eli, Roman, Patrik, Fidi.
(yk, il)
Source link