Compatibilité CSS-in-JS dans les outils de développement

Alex Rudenko
Alex Rudenko

Cet article traite de la compatibilité du CSS-in-JS dans les outils de développement depuis Chrome 85. Il explique en général ce que nous entendons par CSS-in-JS et en quoi il diffère du CSS standard pris en charge par les outils de développement depuis longtemps.

Qu'est-ce que CSS-in-JS ?

La définition de CSS-in-JS est assez vague. Au sens large, il s'agit d'une approche permettant de gérer le code CSS à l'aide de JavaScript. Par exemple, cela peut signifier que le contenu CSS est défini à l'aide de JavaScript et que la sortie CSS finale est générée instantanément par l'application.

Dans le contexte des outils de développement, CSS-in-JS signifie que le contenu CSS est injecté dans la page à l'aide des API CSSOM. Le code CSS standard est injecté à l'aide des éléments <style> ou <link> et possède une source statique (par exemple, un nœud DOM ou une ressource réseau). En revanche, CSS-in-JS n'a généralement pas de source statique. Il s'agit d'un cas particulier : le contenu d'un élément <style> peut être mis à jour à l'aide de l'API CSSOM, ce qui entraîne une désynchronisation de la source avec la feuille de style CSS réelle.

Si vous utilisez une bibliothèque CSS-in-JS (par exemple, styled-component, Emotion ou JSS), il est possible que la bibliothèque injecte des styles à l'aide des API CSSOM en arrière-plan, en fonction du mode de développement et du navigateur.

Examinons quelques exemples illustrant comment injecter une feuille de style à l'aide de l'API CSSOM de la même manière que les bibliothèques CSS-in-JS.

// Insert new rule to an existing CSS stylesheet
const element = document.querySelector('style');
const stylesheet = element.sheet;
stylesheet.replaceSync('.some { color: blue; }');
stylesheet.insertRule('.some { color: green; }');

Vous pouvez également créer une toute nouvelle feuille de style:

// Create a completely new stylesheet
const stylesheet = new CSSStyleSheet();
stylesheet.replaceSync('.some { color: blue; }');
stylesheet.insertRule('.some { color: green; }');

// Apply constructed stylesheet to the document
document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];

Compatibilité des CSS dans les outils de développement

Dans les outils de développement, la fonctionnalité la plus couramment utilisée pour les CSS est le volet Styles. Dans le volet Styles, vous pouvez afficher les règles qui s'appliquent à un élément particulier. Vous pouvez également modifier les règles et voir les changements sur la page en temps réel.

Avant l'année dernière, la compatibilité des règles CSS modifiées à l'aide des API CSSOM était plutôt limitée: vous ne pouviez voir que les règles appliquées, mais vous ne pouviez pas les modifier. Notre objectif principal l'année dernière était d'autoriser la modification des règles CSS-in-JS à l'aide du volet "Styles". Parfois, nous appelons également les styles CSS-in-JS "construits" pour indiquer qu'ils ont été construits à l'aide d'API Web.

Examinons en détail comment modifier les styles dans les outils de développement.

Mécanisme de modification du style dans les outils de développement

Mécanisme de modification du style dans les outils de développement

Lorsque vous sélectionnez un élément dans les outils de développement, le volet Styles s'affiche. Le volet Styles émet une commande CDP appelée CSS.getMatchedStylesForNode pour obtenir les règles CSS qui s'appliquent à l'élément. CDP est l'abréviation de Chrome DevTools Protocol. Il s'agit d'une API qui permet à l'interface des outils de développement d'obtenir des informations supplémentaires sur la page inspectée.

Lorsqu'elle est appelée, CSS.getMatchedStylesForNode identifie toutes les feuilles de style du document et les analyse à l'aide de l'analyseur CSS du navigateur. Il crée ensuite un index qui associe chaque règle CSS à une position dans la source de la feuille de style.

Vous vous demandez peut-être : pourquoi a-t-il besoin d'analyser à nouveau le CSS ? Le problème est que, pour des raisons de performances, le navigateur lui-même ne se préoccupe pas des positions sources des règles CSS et qu'il ne les stocke donc pas. Toutefois, les outils de développement ont besoin des positions sources pour permettre la modification des CSS. Nous ne voulons pas que les utilisateurs standards de Chrome paient la pénalité liée aux performances, mais nous souhaitons que les utilisateurs des outils de développement aient accès aux positions sources. Cette approche de réanalyse répond aux deux cas d'utilisation avec un minimum d'inconvénients.

Ensuite, l'implémentation de CSS.getMatchedStylesForNode demande au moteur de style du navigateur de fournir des règles CSS correspondant à l'élément donné. Enfin, la méthode associe les règles renvoyées par le moteur de style au code source et fournit une réponse structurée aux règles CSS afin que les outils de développement sachent quelle partie de la règle correspond au sélecteur ou aux propriétés. Il permet aux outils de développement de modifier le sélecteur et les propriétés indépendamment.

Passons maintenant à l'édition. Souvenez-vous que CSS.getMatchedStylesForNode renvoie les positions sources pour chaque règle ? C'est crucial pour le montage. Lorsque vous modifiez une règle, les outils de développement génèrent une autre commande CDP qui met réellement à jour la page. La commande inclut la position d'origine du fragment de la règle en cours de mise à jour et le nouveau texte avec lequel le fragment doit être mis à jour.

Sur le backend, lors de la gestion de l'appel de modification, les outils de développement mettent à jour la feuille de style cible. Elle met également à jour la copie de la source de la feuille de style gérée et les positions de la source pour la règle mise à jour. En réponse à l'appel de modification, l'interface des outils de développement récupère les positions mises à jour pour le fragment de texte qui vient d'être mis à jour.

Cela explique pourquoi la modification du code CSS-in-JS dans les outils de développement n'a pas fonctionné immédiatement: CSS-in-JS n'a pas de source réelle stockée nulle part, et les règles CSS sont stockées dans la mémoire du navigateur dans les structures de données CSSOM.

Compatibilité avec CSS-in-JS

Par conséquent, pour permettre la modification des règles CSS-in-JS, nous avons décidé de créer une source pour les feuilles de style construites, que vous pourrez modifier à l'aide du mécanisme existant décrit ci-dessus.

La première étape consiste à créer le texte source. Le moteur de style du navigateur stocke les règles CSS dans la classe CSSStyleSheet. Cette classe est celle dont vous pouvez créer les instances à partir de JavaScript, comme nous l'avons vu précédemment. Le code permettant de créer le texte source est le suivant:

String InspectorStyleSheet::CollectStyleSheetRules() {
  StringBuilder builder;
  for (unsigned i = 0; i < page_style_sheet_->length(); i++) {
    builder.Append(page_style_sheet_->item(i)->cssText());
    builder.Append('\n');
  }
  return builder.ToString();
}

Il effectue une itération sur les règles trouvées dans une instance CSSStyleSheet et en crée une seule chaîne. Cette méthode est appelée lorsqu'une instance de la classe InspectorStyleSheet est créée. La classe InspectorStyleSheet encapsule une instance CSSStyleSheet et extrait les métadonnées supplémentaires requises par les outils de développement:

void InspectorStyleSheet::UpdateText() {
  String text;
  bool success = InspectorStyleSheetText(&text);
  if (!success)
    success = InlineStyleSheetText(&text);
  if (!success)
    success = ResourceStyleSheetText(&text);
  if (!success)
    success = CSSOMStyleSheetText(&text);
  if (success)
    InnerSetText(text, false);
}

Dans cet extrait, CSSOMStyleSheetText appelle CollectStyleSheetRules en interne. CSSOMStyleSheetText est appelé si la feuille de style n'est pas intégrée ou n'est pas une feuille de style de ressource. En gros, ces deux extraits de code permettent déjà de modifier de base les feuilles de style créées à l'aide du constructeur new CSSStyleSheet().

Les feuilles de style associées à un tag <style> qui ont été modifiées à l'aide de l'API CSSOM constituent un cas particulier. Dans ce cas, la feuille de style contient le texte source et des règles supplémentaires qui ne sont pas présentes dans la source. Pour gérer ce cas, nous introduisons une méthode permettant de fusionner ces règles supplémentaires dans le texte source. Ici, l'ordre est important, car les règles CSS peuvent être insérées au milieu du texte source d'origine. Par exemple, imaginez que l'élément <style> d'origine contenait le texte suivant:

/* comment */
.rule1 {}
.rule3 {}

Ensuite, la page a inséré de nouvelles règles à l'aide de l'API JavaScript, en produisant l'ordre suivant: .règle0, .règle1, .règle2, .règle3 et .règle4. Après l'opération de fusion, le texte source obtenu doit se présenter comme suit:

.rule0 {}
/* comment */
.rule1 {}
.rule2 {}
.rule3 {}
.rule4 {}

La conservation des commentaires et des retraits d'origine est importante pour le processus de modification, car les positions du texte source des règles doivent être précises.

Autre aspect spécial des feuilles de style CSS-in-JS : elles peuvent être modifiées par la page à tout moment. Si les règles CSSOM réelles ne sont pas synchronisées avec la version texte, la modification ne fonctionnera pas. Pour cela, nous avons lancé un test, qui permet au navigateur d'informer la partie backend des outils de développement lorsqu'une feuille de style est modifiée. Les feuilles de style modifiées sont ensuite synchronisées lors du prochain appel de CSS.getMatchedStylesForNode.

Une fois tous ces éléments en place, l'édition CSS-in-JS fonctionne déjà, mais nous souhaitions améliorer l'interface utilisateur pour indiquer si une feuille de style a été construite. Nous avons ajouté un nouvel attribut appelé isConstructed à l'en-tête CSS.CSSStyleSheetHeader de la CDP que l'interface utilise pour afficher correctement la source d'une règle CSS:

Feuille de style constructible

Conclusions

Pour récapituler, nous avons passé en revue les cas d'utilisation pertinents liés à CSS-in-JS que les outils de développement ne prenaient pas en charge, ainsi que la solution adaptée à ces cas d'utilisation. Ce qui est intéressant dans cette implémentation, c'est que nous avons pu exploiter les fonctionnalités existantes en faisant en sorte que les règles CSS CSSOM disposent d'un texte source standard, ce qui nous évite d'avoir à modifier complètement l'architecture des modifications de style dans les outils de développement.

Pour en savoir plus, consultez notre proposition de conception ou le bug de suivi Chromium qui fait référence à tous les correctifs associés.

Télécharger les canaux de prévisualisation

Vous pouvez utiliser Chrome Canary, Dev ou Bêta comme navigateur de développement par défaut. Ces versions preview vous permettent d'accéder aux dernières fonctionnalités des outils de développement, de tester des API de plates-formes Web de pointe et de détecter les problèmes sur votre site avant vos utilisateurs.

Contacter l'équipe des outils pour les développeurs Chrome

Utilisez les options suivantes pour discuter des nouvelles fonctionnalités et des modifications dans l'article, ou de tout autre sujet lié aux outils de développement.

  • Envoyez-nous vos suggestions ou vos commentaires via crbug.com.
  • Signalez un problème lié aux outils de développement en cliquant sur Autres options   Plus > Aide > Signalez un problème dans les outils de développement.
  • Tweetez à l'adresse @ChromeDevTools.
  • Faites-nous part de vos commentaires sur les vidéos YouTube sur les nouveautés des outils de développement ou sur les vidéos YouTube de conseils pour les outils de développement.