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

Alex Rudenko
Alex Rudenko

Cet article traite de la prise en charge du code CSS-in-JS dans les Outils de développement, qui est disponible depuis Chrome 85. Il décrit, d'une manière générale, ce que nous entendons par CSS-in-JS et en quoi il est différent du CSS standard, compatible depuis longtemps par les outils de développement.

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

La définition de CSS-in-JS est assez vague. Globalement, il s'agit d'une approche de gestion du 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, le code CSS-in-JS signifie que le contenu CSS est injecté dans la page à l'aide des API CSSOM. Le CSS standard est injecté à l'aide d'é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 souvent pas de source statique. Ici, un cas particulier est que le contenu d'un élément <style> peut être mis à jour à l'aide de l'API CSSOM, ce qui entraîne un décalage entre la source et la feuille de style CSS.

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

Examinons quelques exemples qui vous montrent 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 gérer le CSS est le volet Styles. Dans le volet Styles, vous pouvez voir quelles règles s'appliquent à un élément particulier. Vous pouvez également les modifier et voir les changements sur la page en temps réel.

Avant l'année dernière, la prise en charge des règles CSS modifiées à l'aide des API CSSOM était plutôt limitée: vous ne pouviez que voir les règles appliquées, mais pas les modifier. L'an dernier, notre principal objectif était de permettre la modification des règles CSS-in-JS à l'aide du volet "Styles". Parfois, nous appelons également les styles CSS-in-JS "constructed" pour indiquer qu'ils ont été créés à l'aide d'API Web.

Voyons 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, ou "Chrome DevTools Protocol", est une API qui permet à l'interface DevTools 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 il est nécessaire d'analyser à nouveau le CSS. Le problème est que, pour des raisons de performances, le navigateur lui-même n'est pas concerné par les positions source des règles CSS et ne les stocke donc pas. Toutefois, les outils de développement ont besoin des positions de la source pour permettre la modification 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 emplacements des 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 qui correspondent à 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 concernant les règles CSS. Les outils de développement savent ainsi quelle partie de la règle correspond au sélecteur ou aux propriétés. Elle permet aux outils de développement de modifier le sélecteur et les propriétés indépendamment.

Passons maintenant au montage. Rappelez-vous que CSS.getMatchedStylesForNode renvoie les positions source 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 à 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 feuille de style qu'elle gère et les positions sources 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 du 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: aucune source réelle n'est stockée dans CSS-in-JS et les règles CSS sont stockées dans la mémoire du navigateur dans les structures de données CSSOM.

Comment nous avons ajouté la compatibilité avec CSS-in-JS

Ainsi, pour faciliter la modification des règles CSS-in-JS, nous avons décidé de créer une source pour les feuilles de style créées, modifiables à 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 se présente comme suit:

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();
}

Elle parcourt les règles d'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 des 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, nous voyons que CSSOMStyleSheetText appelle CollectStyleSheetRules en interne. La méthode CSSOMStyleSheetText est appelée si la feuille de style n'est pas intégrée ou qu'il s'agit d'une feuille de style de ressource. Ces deux extraits 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 à une balise <style> qui ont été transformé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 figurent pas dans la source. Pour gérer ce cas de figure, nous introduisons une méthode permettant de fusionner ces règles supplémentaires dans le texte source. Dans ce cas, 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, de nouvelles règles ont été insérées sur la page à l'aide de l'API JavaScript, dans l'ordre suivant : .rule0, .rule1, .rule2, .rule3, .rule4. Le texte source obtenu après l'opération de fusion doit se présenter comme suit:

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

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

Autre particularité 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é une vérification qui permet au navigateur d'avertir 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 de l'appel suivant à CSS.getMatchedStylesForNode.

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

Feuille de style constructible

Conclusions

Pour récapituler notre témoignage, nous avons passé en revue les cas d'utilisation pertinents de CSS-in-JS non pris en charge par les outils de développement. Nous avons également examiné les solutions qui leur sont utiles. Ce qui est intéressant de cette implémentation, c'est que nous avons pu exploiter les fonctionnalités existantes en faisant en sorte que les règles CSS CSS comportent un texte source standard, ce qui nous évite de repenser complètement l'architecture de la modification des styles dans les outils de développement.

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

Télécharger les canaux de prévisualisation

Nous vous conseillons d'utiliser Chrome Canary, Dev ou Beta 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 pointe de plates-formes Web et de détecter les problèmes sur votre site avant même que vos utilisateurs ne le fassent.

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

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

  • Envoyez-nous une suggestion ou des commentaires via crbug.com.
  • Signalez un problème lié aux outils de développement en accédant à Plus d'options   More > Aide > Signaler un problème dans les outils de développement.
  • Envoyez un tweet à @ChromeDevTools.
  • Laissez des commentaires sur les nouveautés des outils de développement vidéos YouTube ou les vidéos YouTube de nos conseils sur les outils de développement.