Memikirkan semula CSS dalam JS

王林
Lepaskan: 2024-09-12 16:18:55
asal
1006 orang telah melayarinya

0. Pengenalan

Dalam dunia pembangunan web, CSS ialah elemen utama dalam menjadikan antara muka pengguna cantik dan berfungsi.

Walau bagaimanapun, apabila kerumitan aplikasi web telah meningkat, Pengurusan CSS telah menjadi tugas yang semakin mencabar. Konflik gaya, kemerosotan prestasi dan kesukaran penyelenggaraan menjadi kebimbangan ramai pembangun.

Adakah isu ini menghalang kemajuan projek anda? (sumber imej)

Rethinking CSS in JS

Artikel ini mengupas secara mendalam pendekatan baharu untuk menyelesaikan masalah ini, terutamanya CSS dalam JS.
Bermula dengan latar belakang sejarah CSS, ia merangkumi pelbagai topik daripada kaedah penggayaan moden kepada sistem reka bentuk masa hadapan.

Struktur artikel adalah seperti berikut:

  1. Takrif dan latar belakang CSS dalam JS
    • 1. Apakah CSS dalam JS?
    • 2. Latar belakang CSS dalam JS
  2. Konteks sejarah CSS dan reka bentuk
    • 3. Latar belakang CSS
    • 4. Latar belakang Reka Bentuk
    • 5. Latar belakang Sistem Reka Bentuk
  3. Analisis kaedah pengurusan gaya dan cadangan baharu
    • 6. Bagaimanakah gaya diuruskan?
    • 7. Bagaimanakah gaya harus diuruskan?
  4. Pelan pelaksanaan khusus untuk CSS dalam JS
    • 8. Mengapa CSS dalam JS?
    • 9. Perkenalkan projek mincho
    • 10. CSS mesra CSS dalam JS
    • 11. CSS boleh skala dalam JS
  5. Integrasi dengan sistem reka bentuk
    • 12. CSS dalam JS untuk Sistem Reka Bentuk

Khususnya, artikel ini memperkenalkan konsep baharu yang dipanggil metodologi CSS SCALE dan StyleStack, dan mencadangkan projek mincho berdasarkan ini. Ia bertujuan untuk melaksanakan CSS dalam JS yang mesra CSS dan berskala.

Tujuan utama artikel ini adalah untuk membentangkan kemungkinan penyelesaian penggayaan yang lebih baik kepada pembangun, pereka bentuk dan pihak berkepentingan projek web yang lain.

Sekarang, mari kita mendalami dunia CSS dalam JS dalam teks utama. Ia akan menjadi perjalanan yang panjang, tetapi saya harap ia memberi anda inspirasi dan peluang baharu untuk cabaran.

1. Apakah CSS dalam JS?

CSS dalam JS ialah teknik yang membolehkan anda menulis gaya CSS terus dalam kod JavaScript(atau TypeScript) anda.
Daripada membuat fail CSS yang berasingan, anda boleh menentukan gaya bersama komponen dalam fail JavaScript anda.

/** @jsxImportSource @emotion/react */
import { css } from "@emotion/react";

const buttonStyles = (primary) => css({
  backgroundColor: primary ? "blue" : "white",
  color: primary ? "white" : "black",
  fontSize: "1em",
  padding: "0.25em 1em",
  border: "2px solid blue",
  borderRadius: "3px",
  cursor: "pointer",
});

function Button({ primary, children }) {
  return (
    <button css={buttonStyles(primary)}>
      {children}
    </button>
  );
}

function App() {
  return (
    <div>
      <Button>Normal Button</Button>
      <Button primary>Primary Button</Button>
    </div>
  );
}
Salin selepas log masuk

Dapat menyepadukannya ke dalam JavaScript pastinya nampak mudah ?

2. Latar belakang CSS dalam JS

CSS dalam JS telah diperkenalkan daripada pembentangan bertajuk 'React: CSS in JS – NationJS' oleh Vjeux, pembangun Facebook.

Masalah yang CSS-in-JS bertujuan untuk diselesaikan adalah seperti berikut:
Rethinking CSS in JS

Apakah masalah yang lebih khusus?
Dan bagaimanakah CSS dalam JS menyelesaikannya?

Saya telah menyusunnya dalam jadual berikut:

Problem Solution
Global namespace Need unique class names that are not duplicated as all styles are declared globally Use Local values as default
- Creating unique class names
- Dynamic styling
Implicit Dependencies The difficulty of managing dependencies between CSS and JS
- Side effect: CSS is applied globally, so it still works if another file is already using that CSS
- Difficulty in call automation: It's not easy to statically analyze and automate CSS file calls, so developers have to manage them directly
Using the module system of JS
Dead Code Elimination Difficulty in removing unnecessary CSS during the process of adding, changing, or deleting features Utilize the optimization features of the bundler
Minification Dependencies should be identified and reduced As dependencies are identified, it becomes easier
to reduce them.
Sharing Constants Unable to share JS code and state values Use JS values as they are, or utilize CSS Variables
Non-deterministic Resolution Style priority varies depending on the CSS loading order - Specificity is automatically calculated and applied
- Compose and use the final value
Breaking Isolation Difficulty in managing external modifications to CSS (encapsulation) - Encapsulation based on components
- Styling based on state
- Prevent styles that break encapsulation, such as .selector > *

But it's not a silverbullet, and it has its drawbacks.

  1. CSS-in-JS adds runtime overhead.
  2. CSS-in-JS increases your bundle size.
  3. CSS-in-JS clutters the React DevTools.
  4. Frequently inserting CSS rules forces the browser to do a lot of extra work.
  5. With CSS-in-JS, there's a lot more that can go wrong, especially when using SSR and/or component libraries.

Aside from the DevTools issue, it appears to be mostly a performance issue.
Of course, there are CSS in JS, which overcomes these issues by extracting the CSS and making it zero runtime, but there are some tradeoffs.
Here are two examples.

  1. Co‑location: To support co-location while removing as much runtime as possible, the module graph and AST should be analyzed and build times will increase. Alternatively, there is a method of abandoning co-location and isolating on a file-by-file basis, similar to Vanilla Extract.
  2. Dynamic styling restrictions: The combination of build issues and the use of CSS Variables forces us to support only some representations, like Styling based on props in Pigment CSS, or learn to do things differently, like Coming from Emotion or styled-components. Dynamicity is also one of the main metrics that can be used to distinguish between CSS in JS.

Therefore, pursuing zero(or near-zero) runtime in CSS-in-JS implementation methods creates a significant difference in terms of expressiveness and API.

3. The background of CSS

3.1 The Beginning of CSS

Where did CSS come from?
Early web pages were composed only of HTML, with very limited styling options.

<p><font color="red">This text is red.</font></p>
<p>This is <strong>emphasized</strong> text.</p>
<p>This is <em>italicized</em> text.</p>
<p>This is <u>underlined</u> text.</p>
<p>This is <strike>strikethrough</strike> text.</p>
<p>This is <big>big</big> text, and this is <small>small</small> text.</p>
<p>H<sub>2</sub>O is the chemical formula for water.</p>
<p>2<sup>3</sup> is 8.</p>
Salin selepas log masuk

For example, the font tag could change color and size, but it couldn't adjust letter spacing, line height, margins, and so on.

You might think, "Why not just extend HTML tags?" However, it's difficult to create tags for all styling options, and when changing designs, you'd have to modify the HTML structure itself.
This deviates from HTML's original purpose as a document markup language and also means that it's hard to style dynamically.

If you want to change an underline to a strikethrough at runtime, you'd have to create a strike element, clone the inner elements, and then replace them.

const strikeElement = document.createElement("strike");
strikeElement.innerHTML = uElement.innerHTML;
uElement.parentNode.replaceChild(strikeElement, uElement);
Salin selepas log masuk

When separated by style, you only need to change the attributes.

element.style.textDecoration = "line-through";
Salin selepas log masuk

If you convert to inline style, it would be as follows:

<p style="color: red;">This text is red.</p>
<p>This is <span style="font-weight: bold;">bold</span> text.</p>
<p>This is <span style="font-style: italic;">italic</span> text.</p>
<p>This is <span style="text-decoration: underline;">underlined</span> text.</p>
<p>This is <span style="text-decoration: line-through;">strikethrough</span> text.</p>
<p>This is <span style="font-size: larger;">large</span> text, and this is <span style="font-size: smaller;">small</span> text.</p>
<p>H<span style="vertical-align: sub; font-size: smaller;">2</span>O is the chemical formula for water.</p>
<p>2<span style="vertical-align: super; font-size: smaller;">3</span> is 8.</p>
Salin selepas log masuk

However, inline style must be written repeatedly every time.
That's why CSS, which styles using selectors and declarations, was introduced.

<p>This is the <strong>important part</strong> of this sentence.</p>
<p>Hello! I want to <strong>emphasize this in red</strong></p>
<p>In a new sentence, there is still an <strong>important part</strong>.</p>

<style>
strong { color: red; text-decoration: underline; }
</style>
Salin selepas log masuk

Since CSS is a method that applies multiple styles collectively, rules are needed to determine which style should take precedence when the target and style of CSS Rulesets overlap.

CSS was created with a feature called Cascade to address this issue. Cascade is a method of layering styles, starting with the simple ones and moving on to the more specific ones later. The idea was that it would be good to create a system where basic styles are first applied to the whole, and then increasingly specific styles are applied, in order to reduce repetitive work.

Therefore, CSS was designed to apply priorities differently according to the inherent specificity of CSS Rules, rather than the order in which they were written.

/* The following four codes produce the same result even if their order is changed. */
#id { color: red; }
.class { color: green; }
h1 { color: blue; }
[href] { color: yellow; }

/* Even if the order is changed, the result is the same as the above code. */
h1 { color: blue; }
#id { color: red; }
[href] { color: yellow; }
.class { color: green; }
Salin selepas log masuk

However, as CSS became more scalable, a problem arose..

3.2 Scalable CSS

Despite the advancements in CSS, issues related to scalability in CSS are still being discussed.
In addition to the issues raised by CSS in JS, several other obstacles exist in CSS.

  1. Code duplication: When writing media queries, pseudo-classes, and pseudo-elements, a lot of duplication occurs if logic is required.
  2. Specificity wars: As a workaround for name collisions and non-deterministic ordering, specificity keeps raising the specificity to override the style. You can have fun reading Specificity Battle!
  3. Lack of type-safety: CSS does not work type-safely with TypeScript or Flow.

These issues can be addressed as follows:

  1. Duplication de code : Utiliser l'imbrication dans les préprocesseurs CSS, etc.
  2. Guerres de spécificités : Le CSS atomique est défini pour chaque propriété séparément, il a donc la même spécificité sauf pour l'ordre de chargement et !important.
  3. Manque de sécurité de type : Utilisez simplement CSS dans JS avec une prise en charge de type sécurisé.

L'expression de la mise en page est un autre obstacle en CSS, rendu plus complexe par les interactions entre diverses propriétés.
Rethinking CSS in JS

Le CSS peut paraître simple en surface, il n'est pas facile à maîtriser. Il est bien connu que de nombreuses personnes ont des difficultés même avec un simple alignement central (1, 2). L'apparente simplicité du CSS peut être trompeuse, car sa profondeur et ses nuances le rendent plus difficile qu'il n'y paraît au départ.

Par exemple, l'affichage en CSS a différents modèles de mise en page : bloc, en ligne, tableau, flex et grille.
Imaginez la complexité lorsque les propriétés suivantes sont utilisées en combinaison : modèle de boîte, conception réactive, flotteurs, positionnement, transformation, mode d'écriture, masque, etc.

À mesure que l'échelle du projet augmente, cela devient encore plus difficile en raison des effets secondaires liés au Positionnement, cascade et spécificité du DOM.

Les problèmes de mise en page doivent être résolus grâce à des frameworks CSS bien conçus et, comme mentionné précédemment, l'utilisation de CSS dans JS pour isoler les styles peut atténuer les effets secondaires.

Cependant, cette approche ne résout pas complètement tous les problèmes. L'isolement des styles peut entraîner de nouveaux effets secondaires, tels qu'une augmentation de la taille des fichiers en raison de déclarations de style en double dans chaque composant, ou de difficultés à maintenir la cohérence des styles communs dans l'application.
Cela entre directement en conflit avec les problèmes d'explosion combinatoire et de cohérence de conception qui seront introduits ensuite.

Pour l'instant, nous pouvons déléguer les soucis de mise en page à des frameworks comme Bootstrap ou Bulma, et nous concentrer davantage sur les aspects de gestion.

4. Le contexte du design

À la base, CSS est un outil puissant pour exprimer et mettre en œuvre le design dans le développement Web.

Il y a de nombreux facteurs à prendre en compte lors de la création d'une UI/UX, et les éléments suivants sont cruciaux à représenter dans votre conception :
Rethinking CSS in JS

  1. Conception visuelle
    • Mise en page : détermine la structure de l'écran et le placement des éléments. Tenez compte de l'espacement, de l'alignement et de la hiérarchie entre les éléments.
    • Couleur : sélectionnez une palette de couleurs qui prend en compte l'identité de la marque et l'expérience utilisateur. La compréhension de la théorie des couleurs est nécessaire.
    • Typographie : choisissez des polices et des styles de texte qui correspondent à la lisibilité et à l'image de la marque.
    • Icônes et éléments graphiques : concevez des icônes et des graphiques intuitifs et cohérents.
  2. Conception d'interactions
    • Concevez le comportement des éléments de l'interface utilisateur tels que les boutons, les curseurs et les barres de défilement.
    • Offrez une expérience utilisateur naturelle grâce à des animations et des effets de transition.
    • Envisagez un design réactif qui s'adapte à différentes tailles d'écran.
  3. Architecture de l'information
    • Concevez la structure et la hiérarchie du contenu pour permettre aux utilisateurs de trouver et de comprendre facilement les informations.
    • Concevez des systèmes de navigation pour permettre aux utilisateurs de se déplacer facilement vers les emplacements souhaités.
  4. Accessibilité et convivialité
    • Poursuivre une conception inclusive qui prend en compte la diversité des utilisateurs. i18n peut également être inclus.
    • Créez des interfaces intuitives et faciles à utiliser pour réduire la charge cognitive des utilisateurs.
  5. Guide de cohérence et de style
    • Créez un guide de style pour maintenir un langage de conception cohérent tout au long de l'application.
    • Développer des composants et des modèles réutilisables pour augmenter l'efficacité.

Exprimer avec précision divers éléments de conception dans diverses conditions présente un défi de taille.
Considérez que vous devez prendre en compte les appareils (téléphones, tablettes, ordinateurs portables, moniteurs, téléviseurs), les périphériques d'entrée (clavier, souris, tactile, voix), les modes paysage/portrait, les thèmes sombres/clairs, le mode contraste élevé, l'internationalisation (langue , LTR/RTL), et plus encore.
De plus, différentes interfaces utilisateur peuvent devoir être affichées en fonction des paramètres utilisateur.

Par conséquent, Explosion combinatoire est inévitable, et il est impossible de les mettre en œuvre une par une manuellement. (source de l'image)

Rethinking CSS in JS

À titre d'exemple représentatif, voir la définition et la compilation de la disposition de la barre d'onglets dans mon thème Firefox.
Bien qu'on ne considère que le système d'exploitation et les options de l'utilisateur, un fichier d'environ 360 lignes produit un résultat de compilation atteignant environ 1 400 lignes.

La conclusion est que la mise en œuvre efficace d'une conception doit être intrinsèquement évolutive, généralement gérée soit par programme, soit par le biais d'ensembles de règles bien définis.
Le résultat est un système de conception pour une gestion cohérente à grande échelle.

5. Le contexte du système de conception

Les systèmes de conception servent de source unique de vérité, couvrant tous les aspects de la conception et du développement, des styles visuels aux modèles d'interface utilisateur et à la mise en œuvre du code.

Rethinking CSS in JS

Selon Nielsen Norman Group, un système de conception comprend les éléments suivants :

  • Guides de style : Documentation qui fournit des conseils de style sur des besoins de style spécifiques, notamment la marque, le contenu et la conception visuelle.
  • Bibliothèque de composants : Ceux-ci spécifient des éléments d'interface utilisateur individuels réutilisables, par exemple des boutons. Pour chaque élément de l'interface utilisateur, des détails spécifiques de conception et de mise en œuvre sont fournis, y compris des informations telles que des attributs pouvant être personnalisés (taille, copie, etc.), différents états (activé, survol, focus, désactivé) et le code réutilisable, propre et serré pour chacun. élément.
  • Bibliothèque de modèles : Ceux-ci spécifient des modèles réutilisables ou des groupes d'éléments d'interface utilisateur individuels extraits de la bibliothèque de composants. Par exemple, vous pouvez voir un modèle pour un en-tête de page, qui peut être composé d'un titre, d'un fil d'Ariane, d'une recherche et d'un bouton principal et secondaire.
  • Ressources de conception : Pour que les concepteurs puissent réellement utiliser et concevoir avec les composants et les bibliothèques, un fichier de conception est requis (généralement dans Figma). Des ressources telles que des logos, des polices de caractères et des icônes sont généralement également incluses pour que les concepteurs et les développeurs puissent les utiliser.

Les systèmes de conception doivent fonctionner comme un carrefour pour les concepteurs et les développeurs, prenant en charge la fonctionnalité, la forme, l'accessibilité et la personnalisation.
Mais les concepteurs et les développeurs pensent différemment et ont des perspectives différentes.

Utilisons les composants comme une lentille pour reconnaître les différences entre les perspectives des concepteurs et des développeurs !!

5.1 Structure des composants

Le concepteur doit également décider quelle icône sera utilisée pour le contrôle de la case à cocher.
Rethinking CSS in JS

Les concepteurs ont tendance à se concentrer sur la forme, tandis que les développeurs ont tendance à se concentrer sur la fonction.
Pour les concepteurs, un bouton est un bouton s'il semble invitant à appuyer, tandis que pour les développeurs, c'est un bouton tant qu'il peut être enfoncé.

Si le composant est plus complexe, l'écart entre les concepteurs et les développeurs pourrait encore se creuser.

Rethinking CSS in JS

5.2 Considérations du concepteur

  • Options visuelles : L'apparence change en fonction des options définies telles que Primaire, Accent, Décrit, Texte uniquement, etc.
    Rethinking CSS in JS

  • Options d'état : L'apparence change en fonction de l'état et du contexte
    Rethinking CSS in JS

  • Décision de conception : Détermination des valeurs avec la structure des composants, les options visuelles/d'état, les attributs visuels (couleur, typographie, icône, etc.) et plus encore.

5.3 Considérations pour les développeurs

  • Option : Valeurs initiales configurables. Des options visuelles sont également incluses. Ex) Décrit, Taille
  • État : Modifications basées sur l'interaction de l'utilisateur. Ex) Survol, Appuyé, Focalisé, Sélectionné (Coché)
  • Événement : Actions qui déclenchent un changement d'état. Ex) HoverEvent, PressEvent, FocusEvent, ClickEvent
  • Contexte : Conditions injectées à partir du code qui affectent le comportement. Ex) Lecture seule, Désactivé

La forme finale est une combinaison d'option, d'état et de contexte, ce qui aboutit à l'explosion combinatoire mentionnée ci-dessus.

Parmi ceux-ci, Option s'aligne sur le point de vue du concepteur, contrairement à State et Context.
Effectuez une compression d'état en tenant compte des états parallèles, des hiérarchies, des gardes, etc. pour revenir à la perspective du concepteur.
Rethinking CSS in JS

  • Activé : Désactivé OFF, Appuyé sur OFF, Survolé OFF, Focalisé OFF
  • Survolé : Désactivé OFF, Appuyé sur OFF, Survolé ON
  • Concentré : Désactivé OFF, enfoncé OFF, concentré ON
  • Appuyé : Désactivé OFF, Appuyé sur ON
  • Désactivé : Désactivé ON

6. Comment les styles étaient-ils gérés ?

Comme vous l'avez peut-être déjà réalisé, créer et maintenir une interface utilisateur de haute qualité est un travail difficile.

Les différents états sont donc couverts par la bibliothèque de gestion des états, mais comment les styles étaient-ils gérés ?
Alors que des méthodologies, des bibliothèques et des cadres continuent d'émerger parce que la solution n'a pas encore été établie, il existe trois paradigmes principaux.
Rethinking CSS in JS

  1. CSS sémantique : Attribuez une classe en fonction du but ou de la signification de l'élément.
  2. Atomic CSS : Créez une classe pour chaque attribut de style (visuel).
  3. CSS en JS : Écrivez en JavaScript et isolez le CSS pour chaque unité de composant.

Parmi ceux-ci, CSS dans JS ressemble à un paradigme qui utilise une approche fondamentalement différente pour exprimer et gérer les styles.
En effet, le CSS dans JS est comme des mécanismes, tandis que le CSS sémantique et le CSS atomique sont comme des politiques.
En raison de cette différence, CSS dans JS doit être expliqué séparément des deux autres approches. (source de l'image)

Rethinking CSS in JS

Lorsque l'on discute du mécanisme CSS dans JS, les pré/post-processeurs CSS peuvent venir à l'esprit.
De même, lorsqu'on parle de politiques, les « méthodologies CSS » peuvent venir à l'esprit.

Par conséquent, je présenterai les méthodes de gestion de style dans l'ordre suivant : CSS en JS, processeurs, CSS sémantique et CSS atomique, et autres méthodologies de style.

6.1 CSS dans JS

Alors, quelle est la véritable identité du CSS dans JS ?
La réponse réside dans la définition ci-dessus.

Écrivez en JavaScript et isolez le CSS pour chaque unité de composant.

  1. CSS écrit en JavaScript
  2. Isolement CSS au niveau des composants

Parmi ceux-ci, l'isolation CSS peut être suffisamment appliquée aux CSS existants pour résoudre les problèmes d'espace de noms global et de rupture d'isolation.
Il s'agit de modules CSS.

Rethinking CSS in JS

Sur la base du lien vers l'article d'analyse CSS dans JS mentionné ci-dessus, j'ai classé les fonctionnalités comme suit.
Chaque fonctionnalité comporte des compromis, et ce sont des facteurs importants lors de la création de CSS dans JS.

6.1.1 Intégration

Le contenu particulièrement remarquable serait SSR (Server Side Rendering) et RSC (React Server Component).
Ce sont les orientations que visent React et NEXT, qui représentent le frontend, et elles sont importantes car elles ont un impact significatif sur la mise en œuvre.

  • IDE : Mise en évidence de la syntaxe et complétion du code
  • TypeScript : Que ce soit typesafe
  • Cadre
    • Agnostique : Indépendantes du framework, les bibliothèques comme StyledComponent sont conçues spécifiquement pour React.
    • SSR : Extraire les styles sous forme de chaînes lors du rendu sur le serveur et prendre en charge l'hydratation
    • RSC : RSC s'exécute uniquement sur le serveur, il ne peut donc pas utiliser d'API côté client.

Le rendu côté serveur crée du HTML sur le serveur et l'envoie au client, il doit donc être extrait sous forme de chaîne, et une réponse au streaming est nécessaire. Comme dans l'exemple du composant stylisé, des paramètres supplémentaires peuvent être requis. (source de l'image)

Rethinking CSS in JS

  1. Server-side style extraction
    • Should be able to extract styles as strings when rendering on the server
    • Insert extracted styles inline into HTML or create separate stylesheets
  2. Unique class name generation
    • Need a mechanism to generate unique class names to prevent class name conflicts between server and client
  3. Hydration support
    • The client should be able to recognize and reuse styles generated on the server
  4. Asynchronous rendering support
    • Should be able to apply accurate styles even in asynchronous rendering situations due to data fetching, etc.

Server components have more limitations. [1, 2]
Server and client components are separated, and dynamic styling based on props, state, and context is not possible in server components.
It should be able to extract .css files as mentioned below.

Rethinking CSS in JS

  1. Static CSS generation
    • Should be able to generate static CSS at build time
    • Should be able to apply styles without executing JavaScript at runtime
  2. Server component compatibility
    • Should be able to define styles within server components
    • Should not depend on client-side APIs
  3. Style synchronization between client and server
    • Styles generated on the server must be accurately transmitted to the client

6.1.2 Style Writing

As these are widely known issues, I will not make any further mention of them.

  • Co-location: Styles within the same file as the component?
  • Theming: Design token feature supports
  • Definition: Plain CSS string vs Style Objects
  • Nesting
    • Contextual: Utilize parent selectors using &
    • Abitrary: Whether arbitrary deep nesting is possible

6.1.3 Style Output and Apply

The notable point in the CSS output is Atomic CSS.
Styles are split and output according to each CSS property.

Rethinking CSS in JS

  • Style Ouput
    • .css file: Extraction as CSS files