Comment nous avons fait des amis SCSS avec les variables CSS en utilisant le thème du kit d'interface utilisateur


Bonjour à tous, je m'appelle Vitalik, je suis développeur front-end senior chez Skyeng. Notre équipe crée une plateforme Vimbox en ligne pour apprendre l'anglais. Il y a environ un an, le concepteur et moi avons terminé un petit kit d'interface utilisateur qui a déraciné le chaos dans l'interface et la base de code.


Il s'est avéré que nous n'étions pas les seuls dans l'entreprise à vouloir le kit d'interface utilisateur, et d'autres équipes ont commencé à nous demander conseil «comment écrire le vôtre». Nous avons réussi à les dissuader de cette entreprise, en promettant de tempérer la nôtre - cela a permis à l'entreprise de gagner des centaines d'heures de développement. En choisissant une solution, nous avons examiné le matériau angulaire, les assemblages personnalisés et les variables CSS et nous nous sommes finalement installés sur ces derniers, malgré leur faible compatibilité avec SCSS, fondement du kit d'interface utilisateur existant. Sous la coupe - les détails de ce que nous avons fait.



Le problème


Le premier kit d'interface utilisateur était composé de polices, d'une palette, d'un ensemble d'éléments pour créer des formulaires (saisie, bouton, etc.), d'un système de gestion des icônes svg. Des fenêtres contextuelles et des info-bulles basées sur des matériaux angulaires ont également été implémentées. Il a été emprisonné pour avoir travaillé uniquement avec la Vimbox "classique": beaucoup de choses étaient délibérément bien cousues et ne permettaient pas de changements de l'extérieur. Et Skyeng a commencé à proposer de nouveaux produits sur la même plateforme, par exemple pour les enfants.


Les développeurs de nouvelles directions, sachant que nous avons quelque chose, sont venus pour des conseils. De plus, à notre grande surprise, ils sont déjà venus avec les dispositions de leurs kits d'interface utilisateur: ils allaient développer leurs solutions à partir de zéro, car ils avaient besoin d'un look différent pour les composants. Il était clair que quelque chose n'allait pas, et nous avons proposé d'affiner notre bibliothèque, en ajoutant la possibilité de son thème.


L'argument était simple: la conception de notre kit d'interface utilisateur a nécessité 200 heures de conception UX et plus de 500 heures de développement. C'est le temps requis pour créer un système de polices, de couleurs et d'environ 10 composants de base. Par conséquent, si vous écrivez une bibliothèque distincte pour chaque produit, l'entreprise passera N * 500 heures de temps de développement. Nous pensions que l'amélioration de notre kit d'interface utilisateur serait moins chère, et cette étape ne devrait pas être répétée pour chaque produit.


Nos arguments ont été acceptés, les domaines connexes ont accepté d'attendre et nous sommes partis à la recherche d'une solution technique.


Données source


Nos outils: Angulaire, SCSS.


Nous ne prenons en charge que les navigateurs modernes et, avec certaines limitations, IE11. Ce qui rend la vie plus facile.


Tous nos composants de kit d'interface utilisateur ont été imprégnés de styles communs que nous avons empilés dans UI kit.var.scss tant que constantes UI kit.var.scss :


 @mixin fontSizeXl { @include fontSize(18px, 26px); } $colorSkillListening: #9679e0; $colorSkillListeningText: #7754d1; $colorSkillListeningBackground: mix($colorSkillListening, #ffffff, 16%); $colorSkillListeningBackgroundHover: mix($colorSkillListening, #ffffff, 8%); 

Défi


  • Tous les nouveaux produits sont assemblés à partir d'éléments existants qui sont présents dans la Vimbox "adulte" - salles de classe, comptes personnels, etc.
  • Les concepteurs devraient avoir une grande liberté pour mettre en œuvre des idées créatives, des caractéristiques distinctives et des exigences spécifiques de nouveaux produits.
  • Dans le même temps, la continuité demeure, c'est-à-dire quelles que soient les couleurs acides et les polices folles inventées par le créateur, l'appartenance du résultat de son travail à l'écosystème Skyeng reste évidente.
  • Tout cela est ajouté au kit d'interface utilisateur existant, tout en conservant tous ses avantages.

C'est parti!


Donc, le résultat est attendu de nous hier, il faut faire rapidement des revues techniques et discuter des options. Lors des premières rencontres, nous avons identifié un cercle de solutions possibles:


Matériau angulaire


Nous n'aimons pas écrire des vélos, nous nous sommes donc tournés d'abord vers le matériel angulaire. Dans les composants, les styles dynamiques sont déplacés vers un fichier {component}-theme.scss . Ces styles sont liés au sélecteur global du composant.


avantagescontre
pas besoin d'ajouter de nouvelles technologies au projetbesoin d'apporter des modifications à chaque composant
la plus grande quantité de travail
avoir à sacrifier l'encapsulation de style

Variables CSS


Nous avons une bonne raison d'essayer les variables CSS à la mode. Le plan consiste à transplanter les parties personnalisées du kit d'interface utilisateur dans les variables CSS. Les composants utilisent les mêmes constantes SCSS, mais au lieu de valeurs spécifiques, les variables CSS y sont écrites.


avantagescontre
possède la portéedevez ajouter polyfill pour IE
technologie de navigateur native (sauf IE)nous ne pourrons pas utiliser les fonctions SCSS
ouvre la possibilité d'utiliser des vars CSS dans d'autres tâches

Constructions personnalisées


Nous aimons les solutions simples, pourquoi ne pas essayer de changer l'assemblage? Chaque équipe crée son propre fichier avec les paramètres du thème. Lors de l'assemblage pour tous les thèmes personnalisés, des ensembles séparés avec leur propre thème sont créés.


avantagescontre
pas besoin de changer le code de la bibliothèqueconfiguration d'assemblage de bibliothèque plus complexe
complication de déploiement de bibliothèque
apporter des modifications, vous devez réassembler les artefacts pour tous les projets
il n'y a aucune possibilité de changement de thème "dynamique". Il sera difficile d'ajouter le mode nuit

Solution


Pendant une semaine, nous avons étudié chaque option, discuté, reporté la décision et étudié à nouveau.


Nous aimons les nouvelles technologies et les surveillons, mais nous ne les mettons en œuvre que si elles nous donnent de vrais bonus. Nous connaissions les variables CSS, nous voulions les essayer, mais le manque de fonctions SCSS a causé beaucoup de tristesse. Néanmoins, les avantages de cette option étaient évidents, nous avons décidé de comprendre comment et quelles fonctions SCSS nous utilisons, s'il est possible de se lier d'amitié avec les vars CSS.


Comprendre CSS vs SCSS


Après avoir expérimenté, nous avons réalisé que le problème principal est le manque de support pour #hex en CSS: dans SCSS, nous écrivons rgba(#ffffff, 0.4) , et en CSS, la même chose nécessite un ensemble de paramètres différent - rgba(255, 255, 255, 0.4) . Tout fonctionne pour nous avec #hex, et nous ne voulons vraiment, vraiment pas changer cela. Nous avons trouvé des solutions, je dirai dans l'ordre d'admission.


Éclaircir et assombrir


Notre designer a imaginé une palette composée d'un petit nombre de couleurs de base, s'étendant grâce aux fonctions SCSS d' lighten et d' darken :


 // $color –   base: $color, background: mix($color, #ffffff, 16%), backgroundHover: mix($color, #ffffff, 8%), hover: lighten($color, 5), focused: darken($color, 5), ...more transformations... 

Nous avons essayé de trouver un analogue d' lighten et d' darken en CSS, mais nous n'avons rien trouvé. Nous avons réfléchi pendant plusieurs jours jusqu'à ce que nous réalisions que pour la personnalisation, nous devons nous débarrasser de ces fonctions à l'intérieur de la bibliothèque, en les supprimant. Après tout, chaque équipe peut vouloir trouver sa propre formule pour changer la couleur lors du changement de focus - par exemple, les collègues des enfants ont besoin de plus de contraste.


La solution simple était de transférer nos transformations du côté de la plateforme qui initialisera le sujet. Et pour la plateforme, nous écrivons une fonction qui crée automatiquement les valeurs nécessaires:


 @function getMainColors($color, $colorText) { $colors: ( text: $colorText, base: $color, background: mix($color, #ffffff, 16%), backgroundHover: mix($color, #ffffff, 8%), lightenLess: lighten($color, 5), darkenLess: darken($color, 5), lightenMore: lighten($color, 20), ); @return $colors; } 

La plateforme l'utilise lors de l'initialisation des couleurs:


 //platform $colorValues: ( brand: getMainColors(#5d9cec, #4287df), positive: getMainColors(#8cc152, #55a900), accent: getMainColors(#ff3d6f, #ff255d), wrong: getMainColors(#ff6666, #fe4f44), ) 

RGBA


Dans notre kit d'interface utilisateur, nous utilisons la fonction rgba . Avec lui, nous ajustons la transparence des couleurs de base. Mais si rgba fonctionne avec #hex dans SCSS, alors CSS ne peut pas. J'ai dû écrire une fonction qui décompose la valeur #hex en r / g / b:


 // returns `r, g, b` from `#hex` for `rgba(var(--smth))` usage @function rgbValuesFromHex($hex) { @return red($hex), green($hex), blue($hex); } 

Eh bien, comme nous ne voulons pas générer de valeurs RVB avec des poignées pour toute la palette, nous créons une fonction distincte qui le fait de manière récursive pour chaque couleur de la collection:


 // adds `fieldRgb: r, g, b` fields to map for each `field: #hex` for `rgba(var(--smth-rgb))` usage @function withRgbValues($map) { $rgbValues: (); @each $name, $value in $map { $formattedValue: (); @if type-of($value) == 'map' { $rgbValues: map-merge($rgbValues, (#{$name}: withRgbValues($value))); } @else { //   , rgb    Rgb   $rgbValues: map-merge($rgbValues, (#{$name}Rgb: rgbValuesFromHex($value))); } } @return map-merge($map, $rgbValues); } 

Par conséquent, l'initialisation de la palette avec les valeurs RVB générées ressemble à ceci:


 $colorValues: withRgbValues( ( text: ( base: #242d34, secondary: #50575c, label: #73797d, placeholder: #969b9e, inversed: #ffffff, inversedSecondary: #dadada, ), brand: getMainColors(#5d9cec, #4287df), positive: getMainColors(#8cc152, #55a900), accent: getMainColors(#ff3d6f, #ff255d), wrong: getMainColors(#ff6666, #fe4f44), //...etc 

La sortie est une carte de couleurs SCSS, qui peut ensuite être transformée en une méthode qui la transforme en variables CSS. Afin d'obtenir la valeur du thème RVB, nous avons écrit une fonction:


 @function getUiKitRgbVar($path...) { $path: set-nth($path, -1, #{nth($path, -1)}Rgb); //       @return getFromMap($uiKitBaseVars, $path...); } //  border-color: rgba(getUiKitRgbVar(color, brand, base), $opacity64); 

Transformez const SCSS en vars CSS


La première étape consiste à créer une structure miroir (similaire à SCSS) qui stocke les noms des variables CSS:


 $colorCssVars: withRgbCssVars( ( text: ( base: getColorCssVar(text, base), secondary: getColorCssVar(text, secondary), label: getColorCssVar(text, label), placeholder: getColorCssVar(text, placeholder), inversed: getColorCssVar(text, inversed), //   

getColorCssVar - une méthode qui ajoute des préfixes aux noms de variables. Ajoutez le préfixe --sky pour éviter les collisions avec des bibliothèques externes. Ajoutez également le préfixe de bibliothèque de - UI kit à --sky pour éviter les conflits avec les bibliothèques internes. Il s'est avéré --sky- UI kit :


 @function getColorCssVar($parts...) { @return getUiKitCssVar(color, $parts...); } @function getUiKitCssVar($parts...) { $uiKitCssVarPrefix: '--sky- UI kit'; $cssVar: $uiKitCssVarPrefix; @each $part in $parts { $cssVar: $cssVar + '-' + $part; } @return $cssVar; } 

Par exemple, pour getColorCssVar(text, base) nous obtenons --sky- UI kit-color-text-base sur la sortie.


La touche finale est un mixin récursif qui initialise les valeurs de la structure SCSS en variables avec des noms de la structure CSS Var:


 //       :root { @include uiKitThemeCssVars($uiKitDefaultTheme); //uiKitDefaultTheme – SCSS       } @mixin uiKitThemeCssVars($theme) { $cssVarsList: createVarsList($theme, $uiKitBaseCssVars); //18+,    Map  List, $uiKitBaseCssVars   css  @each $cssVar, $value in $cssVarsList { #{$cssVar}: $value; } } 

Un exemple d'utilisation d'un thème sur la plateforme:


 .popup { font-family: getUiKitVar(font, family); background-color: getUiKitVar(color, background, base); ... } 

Quel est le résultat


Nous avons pu utiliser des variables CSS, tout en conservant la possibilité d'utiliser des fonctions SCSS. Création de la possibilité de personnaliser l'apparence des composants. Nous avons écrit quelques méthodes récursives pour automatiser l'extension du sujet. Eh bien et surtout - passé 30 heures de développement au lieu de N * 500.


Profit!

Source: https://habr.com/ru/post/fr459194/


All Articles