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)
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:
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.
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> ); }
Dapat menyepadukannya ke dalam JavaScript pastinya nampak mudah ?
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:
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.
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.
Therefore, pursuing zero(or near-zero) runtime in CSS-in-JS implementation methods creates a significant difference in terms of expressiveness and API.
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>
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);
When separated by style, you only need to change the attributes.
element.style.textDecoration = "line-through";
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>
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>
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; }
However, as CSS became more scalable, a problem arose..
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.
These issues can be addressed as follows:
L'expression de la mise en page est un autre obstacle en CSS, rendu plus complexe par les interactions entre diverses propriétés.
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.
À 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 :
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)
À 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.
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.
Selon Nielsen Norman Group, un système de conception comprend les éléments suivants :
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 !!
Le concepteur doit également décider quelle icône sera utilisée pour le contrôle de la case à cocher.
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.
Options visuelles : L'apparence change en fonction des options définies telles que Primaire, Accent, Décrit, Texte uniquement, etc.
Options d'état : L'apparence change en fonction de l'état et du contexte
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.
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.
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.
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)
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.
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.
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.
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.
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.
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)
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.
As these are widely known issues, I will not make any further mention of them.
The notable point in the CSS output is Atomic CSS.
Styles are split and output according to each CSS property.