Optimiser notre CSS chez Glean

0
minutes de lecture
Optimiser notre CSS chez Glean

Table des matières

Vous avez des questions ou souhaitez une démo ?

Nous sommes là pour vous aider ! Cliquez sur le bouton ci-dessous et nous vous recontacterons.

Demander une démo
Partager cet article :

À l’époque où Glean a démarré, nous avions choisi d’utiliser des styles inline. Cette approche s’est mal adaptée à notre croissance  nous avons donc décidé d’évaluer les options CSS-in-JS. vanilla-extract, la solution que nous avons retenue, optimise les performances (zéro surcoût à l’exécution) et l’expérience développeur, au prix d’une courbe d’apprentissage légèrement plus exigeante.

Lisez ceci si vous :

  • Voulez comprendre les compromis entre performance et expérience développeur des frameworks CSS modernes les plus courants
  • Souhaitez tester des solutions CSS-in-JS zéro-runtime

{{richtext-banner-component}}

Notre évaluation des options de frameworks CSS

Nous avons commencé notre recherche d’une solution CSS en consultant des tendances et des enquêtes comme State of CSS (2021). D’après les chiffres d’usage ci-dessous, Styled Components et CSS Modules occupaient des positions #1 et #2 assez proches. À noter que l’usage de vanilla-extract se situait près du bas, à 1 %. Cela paraît logique, étant donné que sa première version stable est sortie en mai 2021.

Illustration produit

En revanche, côté satisfaction des développeurs, vanilla-extract arrivait en tête, et CSS Modules était une seconde place très proche.

Illustration produit

Pour départager nos 3 options de tête (Styled Components, CSS Modules et vanilla-extract), nous avons lu des articles de blogs de développeurs afin d’en comprendre les avantages et les inconvénients.

Pourquoi nous avons écarté Styled Components

Styled Components est un mécanisme très orienté opinion qui encapsule des propriétés de style (sans classes) dans un composant React. Notre principale inquiétude avec Styled Components concernait son surcoût à l’exécution. Nous n’avons pas réalisé de prototype avec Styled Components, mais les retours d’autres développeurs nous ont convaincus. En bref, Styled Components entraîne un surcoût de taille de bundle à l’exécution via son loader. Son rendu, comme les styles inline, est également bloqué jusqu’à l’exécution du JS.

CSS Modules semblait prometteur

CSS Modules est un mécanisme qui obfusque les classes CSS afin de simuler un scope local en CSS. Nous apprécions sa séparation philosophique des responsabilités entre les fichiers CSS et les fichiers de composants React. De plus, CSS Modules est transpilé en assets CSS « classiques », qui sont :

  • Plus parallélisables : le CSS bloque uniquement le rendu (pas le parsing), tandis que le JS bloque le parsing du HTML.
  • Plus rapides à appliquer pour le navigateur : voir le surcoût de styling en JS dans ce benchmark.

Cependant, CSS Modules n’est pas une solution CSS-in-JS, et nécessiterait de migrer de TS vers du CSS ou un préprocesseur.

Pourquoi nous avons choisi vanilla-extract

La promesse de vanilla-extract comme solution « le meilleur des deux mondes » (CSS-in-JS et CSS Modules) était suffisamment intéressante pour la prototyper. Rappel : vanilla-extract est un préprocesseur build-time qui transforme du TS en modules CSS. Il est également utile de noter que l’auteur de vanilla-extract est le co-créateur de CSS Modules, qui se place juste derrière Styled Components en usage aujourd’hui.

Nous pensons que Styled Components a un usage élevé mais une satisfaction relativement plus faible car il est facile à adopter sur des projets petits à moyens (pas de préprocesseur CSS). À l’inverse, CSS Modules et vanilla-extract conviennent mieux aux projets plus importants grâce à leurs avantages en matière de performance, d’organisation des fichiers et de débogage.

vanilla-extract vs. CSS Modules

CSS Modules et vanilla-extract proposent tous deux un surcoût zéro-runtime. Autrement dit, un CSS natif transpilé permet au navigateur de faire ce qu’il fait le mieux en code natif, plutôt que d’exécuter du JS en « espace utilisateur », qui parse beaucoup plus lentement et se retrouve bloqué au chargement.

Cependant, nous pensons que l’approche CSS-in-JS de vanilla-extract est meilleure que la transpilation CSS Modules, car l’intégration TS offre l’intellisense de l’IDE, le bundle splitting Webpack, le tree shaking (élimination du code mort), etc. En bref : un refactoring plus simple, et de meilleures performances web grâce à une taille d’assets minimale.

Cela dit, nos réserves quant à l’adoption de vanilla-extract restent valables. Nous avons adopté le projet assez tôt. Par exemple, la solution CSS-in-JS de Facebook, Stylex, pourrait être open sourcée et gagner en popularité. Cette inquiétude est en partie atténuée par la similarité de Stylex. Les deux transpilent du TS en CSS Modules. Si jamais il faut changer, passer d’une solution similaire à une autre ne devrait pas être catastrophique. Enfin, d’après l’article lié, Stylex aurait dû sortir fin 2021, mais n’est toujours pas disponible en septembre 2022. Cela peut être un signal que Stylex concède du terrain à la traction open source de vanilla-extract. Nous mesurons cette traction par : une adoption en hausse via les téléchargements npm, et les commits Github.

Migration des styles inline vers vanilla-extract

Même si nous étions passés à vanilla-extract pour les nouvelles fonctionnalités, il nous restait encore à migrer les styles inline existants dans la base de code. Cela s’est également avéré être une refonte assez conséquente, car nous avions plus de ~300 fichiers (10 000 lignes de code) à convertir vers vanilla-extract.

En migrant manuellement certaines feuilles de style, nous avons remarqué que l’effort était assez mécanique, puisqu’il s’agissait de convertir un style de code vers un autre  nous avons donc décidé d’écrire un codemod pour faire une grande partie du travail.

Refactorisation à l’aide de codemods

Un codemod est un ensemble de transformations qui automatise des changements constituant essentiellement une refactorisation. L’idée est de traiter le code comme des données pour un autre programme, capable d’en comprendre la structure et d’appliquer des transformations spécifiques. Avec la bonne configuration, les codemods peuvent faire gagner énormément de temps et d’efforts lors de refactorisations à grande échelle.

Côté implémentation, nos scripts de codemod effectuaient les actions suivantes pour chaque feuille de style :

Étape 1. Convertir les règles de style inline en styles vanilla-extract

Pour chaque fichier, nous devions convertir les règles de style en déclarations style() vanilla-extract correspondantes. Nous utilisons une déclaration personnalisée Stylesheet() pour nos styles inline existants, ce qui constitue un bon point de départ pour le script de codemod. À haut niveau, nous effectuons quatre transformations distinctes :

  1. Trouver toutes les déclarations Stylesheet() dans le fichier.
  2. Pour chaque Stylesheet(), extraire chaque propriété et expression pour créer une nouvelle déclaration de variable.
    Par ex. : `container : { …styleRules }` → `export const container = style({...styleRules })`
  3. Ajouter un import de `style` depuis vanilla-extract.
  4. Supprimer la déclaration et l’import Stylesheet().


Illustration du produit

Étape 2. Enfin, mettre à jour les props « style » en « className » dans les composants JSX

Une fois les styles transformés, nous devions aussi convertir les composants qui utilisaient ces styles inline.
Les localiser était simple, car la plupart de nos composants partagent une convention de nommage avec leurs styles.

Une fois les fichiers localisés, nous avions encore quelques transformations à effectuer :

  • Convertir les JSXAttributes style en className et référencer les styles transformés.
  • Mettre à jour l’import de styles par défaut en imports nommés, puisque nous nous sommes éloignés de l’export par défaut Stylesheet ci-dessus.


Étape 3. Ajouter.css.ts au nom de fichier

À ce stade, la feuille de style et le composant ont tous deux été transformés  il ne reste donc plus qu’à renommer le fichier et à ajouter.css.ts au nom de fichier.

Outils

Nous avons utilisé ASTExplorer pour explorer et visualiser l’arbre de syntaxe abstraite (AST) du code et déterminer où appliquer les transformations. Nous avons également utilisé JSCodeshift, qui fournit une API pratique pour appliquer des transformations au code.

Vanilla extract en pratique

Après un prototype et une adoption à grande échelle, nous avons réalisé que les classNames pour le debugging constituaient notre plus grand gain de productivité. L’arbre des composants React et l’arbre DOM diffèrent souvent fortement. Un className=$fileName_$elementClass_$moduleHash (ex : ProfilePage_mainPersonCard__1bmv6no1) est bien plus facile à déboguer que de bricoler avec l’absence de sourcemaps des styles inline. Par exemple, auparavant, nous hotloadions des textNodes traceurs à chaque fois que nous devions mapper les styles rendus à la source TS :

< ;div style={{...inlineStylesToDebug}}> ; {/* pas de sourcemaps */}

  ; ;  ; ;  ;Test : est-ce bien la div que je débogue ? {/* textNode traceur */}

  ; ;  ; ;  ; ;< ;SomeComponent /> ;

  ; ;  ; ;  ; ;< ; ;/div> ;

Les coûts d’adoption de vanilla-extract étaient ponctuels :

// Quasi-équivalent des mixins SCSS

const deepMergedClassName = style([baseStyle, otherBase, componentOverrides])

  • Des bugs de migration initiaux. Passer de styles inline à des classNames casse lorsque des sélecteurs sont à égalité et lorsque des propriétés !important, auparavant courantes, sont présentes. Détecter manuellement les bugs était fastidieux, mais ils ont motivé des tests de diff de captures d’écran Storybook plus automatisés, qui évitent de futures régressions

À retenir

Voici certains des avantages à travailler dans une startup en forte croissance :

  • Il ne manque pas de travail à fort impact lorsqu’il y a beaucoup de gains faciles à saisir.
  • Si vous repérez une amélioration, vous pouvez souvent prendre en charge la solution de bout en bout.

Si construire ou utiliser un produit de recherche best-in-class vous intéresse, contactez-nous !

Qu'est-ce que la Retrieval Augmented Generation (RAG) ?

Qu'est-ce que la Retrieval Augmented Generation (RAG) ?

La Retrieval Augmented Generation est un framework de pipeline qui récupère des informations via un système de découverte externe, améliorant le processus de récupération des connaissances pour les grands modèles de langage. L'IA générative et les grands modèles de langage (LLMs) émergent comme certaines des technologies de travail les plus transformatrices d'aujourd'hui — cependant, de nombreuses solutions peinent à récupérer les connaissances. La Retrieval Augmented Generation répond à ce problème en séparant la récupération des connaissances du processus de génération. Découvrez ce qu'il faut pour faire fonctionner RAG et comment cela libère tout le potentiel de l'IA générative dans ce document gratuit de 2 pages.

Qu'est-ce que la Retrieval Augmented Generation (RAG) ?
L’IA au service de tous.
Demander une démo
CTA Section Background Shape

Work AI qui fonctionne.

CTA Background Gradient 3CTA Background Gradient 3CTA Background Mobile