Fermer

décembre 1, 2022

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 ?

Modèle de bureau et mobile du concepteur
Modèle de bureau et mobile du concepteur. (Grand aperçu)

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 ?

Requêtes multimédias avec points d'arrêt
Requêtes multimédias avec points d’arrêt. (Grand aperçu)

Qu’en est-il des tailles de fluides ? Il existe des calculatrices pratiques comme utopie ou Min-Max-Calculatrice.

Taille du fluide avec minimum et maximum
Taille du fluide avec minimum et maximum. (Grand aperçu)

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.

Visualisation des inconvénients de l'approche par taille de fluide avec minimum et maximum fixes.  Une coche dans une case pour mobile et ordinateur de bureau, et une croix rouge pour une tablette, des écrans plus grands et des rapports d'aspect extrêmes
Inconvénients de l’approche de taille fluide avec un minimum et un maximum fixes : la taille et les espacements de la police sont trop petits pour les vues de la tablette et aucune réactivité pour les écrans plus grands ou les rapports d’aspect extrêmes. (Grand aperçu)

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.

Ligne réactive personnalisée basée sur des points d'ancrage
Ligne réactive personnalisée basée sur des points d’ancrage. (Grand aperçu)

Sur quelles constantes des spécifications de conception nos calculs doivent-ils être basés ?

Reprenons notre exemple précédent :

Contribution d'un concepteur avec spécifications de taille et d'écran
Contribution d’un concepteur avec spécifications de taille et d’écran. (Grand aperçu)

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’à 2000pxaprès quoi le max-wrapper prend effet.

Conception réactive pour la conception sur mobile (min), ordinateur de bureau (opt) et écrans plus grands (avec wrapper)
Comportement d’écran pour la conception sur mobile (min), ordinateur de bureau (opt) et écrans plus grands (avec wrapper). (Grand aperçu)
Comportement d'écran des modèles de conception en tant que ligne réactive basée sur des points d'ancrage
Comportement d’écran des modèles de conception en tant que ligne réactive basée sur des points d’ancrage. (Grand aperçu)

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 16pxidé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!

Plus après saut! Continuez à lire ci-dessous ↓

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)
Définition des pinces principales en fonction des points d'ancrage spécifiés
Définition des pinces principales en fonction des points d’ancrage spécifiés. (Grand aperçu)

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.

Visualisation de deux lignes intersectées
Selon la différence entre les deux tailles, les lignes de pente peuvent se croiser – les deux pinces peuvent entrer en conflit. (Grand aperçu)

Si slope1 est plus raide que slope2nous combinons les pinces comme ceci :

clamp(Yf, slope1, clamp(Yb, slope2, Yu))

Si slope1 est plus plat que slope2on 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 ?

Ajustement des opérations si aucune valeur maximale n'est à définir
Ajustement des opérations si aucune valeur maximale n’est à définir. (Grand aperçu)

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

Pour compléter la ligne réactive, deux pentes et un point d'ancrage doivent être calculés automatiquement
Pour compléter la ligne réactive, deux pentes et un point d’ancrage doivent être calculés automatiquement. (Grand aperçu)
  • steep-slope
    Vérifiez si la pente Yf → Ya est au-dessus de la pente Yb → Yu (m = 1) :
((Ya - Yf) / (Xa - Xf)) * 100 > 1
  • Mfa
    Interpolation linéaire, y compris le calcul de la pente Yf → Ya:
Yf + (Ya - Yf) * (100vw - Xf) / (Xa - Xf)
  • Mbu
    Interpolation linéaire entre Yb et Yu (pente m = 1):
100vw / Xb * Yb
  • Yu
    Calcul de la position de Yu:
(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.

Visualisation de l'effet négatif des rapports d'aspect extrêmes sur la conception rendue illustrée par une croix dans une boîte rouge
Les rapports d’aspect extrêmes peuvent avoir un effet négatif sur la conception rendue. (Grand aperçu)

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)
FabUnit Visualizer qui accepte les valeurs définies par l'utilisateur, affiche la taille maximale calculée automatiquement et dessine la ligne réactive correspondante
Un pratique Visualiseur FabUnit qui accepte les valeurs définies par l’utilisateur, affiche la taille maximale calculée automatiquement et dessine la ligne réactive correspondante. (Grand aperçu)

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 :

Guide de style d'un designer avec toutes les spécifications de taille pour mobile et ordinateur de bureau
Guide de style d’un designer avec toutes les spécifications de taille pour mobile et ordinateur de bureau. (Grand aperçu)

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.

Adaptation optimale aux différentes tailles de fenêtres et rapports d'aspect avec le FabUnit
Adaptation optimale aux différentes tailles de fenêtres et rapports d’aspect avec le FabUnit. (Grand aperçu)

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! 🙏🏻

Merci Eli, Roman, Patrik, Fidi.

Éditorial fracassant
(yk, il)




Source link

décembre 1, 2022