Mior Agency

Un moyen intelligent de contrôler et de synchroniser les fautes de frappe et les erreurs spatiales

Et si nous pouvions implémenter les tailles de contribution des concepteurs de manière transparente ? Et si nous pouvions définir des points d’ancrage personnalisés pour générer une valeur de réponse parfaite, nous donnant plus d’options à mesure que les approches de dimensionnement fluide ? Et si nous avions une formule magique qui contrôlait et synchronisait tout le projet ?

Il arrive souvent que je reçoive deux templates du designer : un pour mobile et un pour desktop. Dernièrement, je me suis demandé comment je pourrais automatiser le processus et optimiser le résultat. Comment implémenter les tailles spécifiées le plus efficacement ? Comment assurer une vue confortable de 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 de concepteur.
Modèle de bureau et mobile de concepteur. (Grand aperçu)

J’aimerais pouvoir lire les deux valeurs des différentes tailles (police, espaces, etc.) en pixels et les alimenter en 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é FabUnit, je suis tombé sur cet article intéressant d’Adrian. Pendant ce temps, Ruslan et Brecht ont également fait des recherches dans cette direction et ont proposé des idées intéressantes.

Comment puis-je mettre en œuvre des modèles de conception plus efficacement ?

J’en ai assez d’écrire des requêtes multimédias pour chaque valeur et je veux éviter les ruptures 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 concepteur ?

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 Utopia ou Min-Max-Calculator.

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 configuration. La vue de la tablette est souvent trop petite et je ne peux pas réagir aux fenêtres plus grandes ou au rapport d’aspect.

Visualisation des inconvénients de l'approche par taille de fluide avec minimum et maximum fixes.  Une case à cocher pour les mobiles et les ordinateurs de bureau, et une croix rouge pour les tablettes, les écrans plus grands et les formats d'image extrêmes
Inconvénients de l’approche de dimensionnement fluide avec min et max fixes : la taille de la police et les espaces sont trop petits pour les vues de la tablette et ne répondent pas aux écrans plus grands ou aux 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 tracer ma propre ligne de réponse. J’ai donc besoin d’un outil qui crachera la valeur parfaite en fonction de divers points d’ancrage (définitions d’écran) et automatisera 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 de réponse personnalisée basée sur des points d'ancrage
Ligne de réponse personnalisée basée sur des points d’ancrage. (Grand aperçu)

Sur quelles constantes des spécifications de conception nos calculs doivent-ils se baser ?

Reprenons notre exemple précédent :

Entrée d'un concepteur avec des spécifications de taille et d'affichage
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 (plus sur le guide de style complet plus tard). Les tailles de mobile doivent commencer à 375px et s’adapter en permanence à 1024px. Jusqu’à 1440px, les tailles doivent rester statiquement au niveau optimal. Après cela, les valeurs doivent évoluer de manière linéaire jusqu’à 2000pxaprès quoi le max-wrapper prend effet.

Conception réactive pour la mise en page sur mobile (minimum), ordinateur de bureau (facultatif) et écrans plus grands (avec wrapper)
Comportement de l’écran pour la mise en page sur mobile (min), ordinateur de bureau (opt) et écrans plus grands (wrapped). (Grand aperçu)
Les modèles de mise en page affichent le comportement sous la forme d'une ligne réactive basée sur des points d'ancrage
Les modèles de mise en page affichent le comportement sous la forme d’une ligne de réponse 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 dans 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 Oui 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 FabUnit ? ».

Alors commençons !

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

Définir les brides parentes

Tout d’abord, nous devons déterminer quelles pinces parents nous voulons configurer.

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)

Pinces de combinaison et d’emboîtement

Maintenant, nous devons combiner les deux pinces. Cela pourrait être un peu délicat. Nous devons considérer que les deux lignes, slope1 Oui slope2, ils peuvent se remplacer, selon leur degré d’asymétrie. 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 qui se croisent
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)

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

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

Oui slope1 est plus plat que slope2on fait ce calcul :

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

Régler:

steep-slope
  ? clamp(Yf, slope1, clamp(Yb, slope2, Yu))
  : clamp(clamp(Yf, slope1, Ya), slope2, Yu)

Définir le conteneur maximum en option

Que se passe-t-il si nous n’avons pas de conteneur max qui fige la mise en page au-delà d’une certaine largeur ?

Ajustement des opérations si vous ne souhaitez pas définir de valeur maximale
Ajustement des opérations si vous ne souhaitez pas établir de valeur maximale. (Grand aperçu)

Tout d’abord, nous devons déterminer quelles pinces parents nous voulons configurer.

clamp1: clamp(Yf, slope1, Ya)
max: max(Yb, slope2)

Oui slope1 est plus raide que slope2:

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

Oui slope1 est plus plat que slope2:

max(clamp(Yf, slope1, Ya), slope2)

Le calcul non emballé – élastique vers le haut :

steep-slope
  ? clamp(Yf, slope1, max(Yb, slope2))
  : max(clamp(Yf, slope1, Ya), slope2)

Combiné, avec enveloppement max en option (si screen-max Xu Il est établi):

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 = mfa
pente2 = Premier
Yu

Pour compléter la ligne de réponse, deux pentes et un point d'ancrage doivent être automatiquement calculés
Pour compléter la ligne de réponse, deux pentes et un point d’ancrage doivent être automatiquement calculés. (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 calcul de pente Yf → Ya:
Yf + (Ya - Yf) * (100vw - Xf) / (Xa - Xf)
  • Mbu
    Interpolation linéaire entre Yb Oui Yu (pente m = 1):
100vw / Xb * Yb
  • Yu
    Calcul de la position de Yu:
(Xu / Xb) * Yb

mets le tout 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 d’aspect

Puisque 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 appareil mobile, nous souhaitons à nouveau réduire les tailles. C’est plus agréable et plus lisible ainsi.

Visualisation de l'effet négatif des rapports d'aspect extrêmes sur la conception rendue représenté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 format d’image 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. Voyons maintenant toute la formule magique.

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 de réponse correspondante
Un visualiseur FabUnit pratique qui accepte les valeurs définies par l’utilisateur, affiche la taille maximale calculée automatiquement et dessine la ligne de réponse 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 s’assurent que nous produisons correctement les valeurs rem (je n’entrerai pas dans les détails). Nous définissons ensuite 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 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 les appareils de bureau et mobiles
Guide de style d’un designer avec toutes les spécifications de taille pour les appareils de bureau et mobiles. (Grand aperçu)

Nous lisons les valeurs associées dans le guide de style et les transmettons à 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 */
}

Maintenant, nous pouvons tracer la ligne de réponse 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 l’espacement, 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 pouvons également déplacer localement les points d’ancrage prédéfinis.

Adaptation optimale aux différentes tailles de fenêtres et rapports d'aspect avec FabUnit
Adaptation optimale aux différentes tailles de fenêtre et rapports d’aspect avec 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 extensibles. Les arguments avec une grande différence peuvent ne pas se comporter comme souhaité. Pour plus d’informations à ce sujet, vous pouvez consulter l’article « Responsive Type and Zoom » d’Adrian Roselli.

conclusion

Nous avons maintenant créé une fonction qui fait tout le travail pour nous. Il prend une valeur minimale et optimale et renvoie un calcul à notre propriété CSS, en tenant compte de la largeur d’écran, du rapport d’aspect et des points d’ancrage spécifiés, une formule unique qui pilote l’ensemble du projet. Aucune requête multimédia, aucun point d’arrêt, aucune rupture de mise en page.

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 approche différente et que vous 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 package FabUnit npm de cet article pour vos projets.

Merci! ??

Merci Eli, Roman, Patrik, Fidi.

édito fracassant
(Oui oui)

Laissez un commentaire

Derniers Posts
Une Question ? Un Projet ?
Quel que soit votre projet, MIOR AGENCY vous écoute, analyse vos besoins et propose des pistes de travail en conséquence. Vous avancez avec sérénité et confiance.