In der Welt der Webentwicklung ist CSS ein Schlüsselelement, um Benutzeroberflächen schön und funktional zu gestalten.
Da jedoch die Komplexität von Webanwendungen zunimmt, ist die CSS-Verwaltung zu einer immer anspruchsvolleren Aufgabe geworden. Stilkonflikte, Leistungseinbußen und Wartungsschwierigkeiten bereiten vielen Entwicklern Sorgen.
Behindern diese Probleme den Fortschritt Ihrer Projekte? (Bildquelle)
Dieser Artikel befasst sich eingehend mit neuen Ansätzen zur Lösung dieser Probleme,
insbesondere CSS in JS.
Ausgehend vom historischen Hintergrund von CSS deckt es ein
breites Themenspektrum ab, von modernen Styling-Methoden bis hin zu zukünftigen Designsystemen.
Mincho-Projekt vorgeschlagen. Ziel ist es, CSS in JS zu implementieren, das CSS-freundlich und skalierbar ist.
Der ultimative Zweck dieses Artikels besteht darin, Entwicklern, Designern und anderen Webprojektbeteiligten dieMöglichkeit besserer Styling-Lösungen vorzustellen.
Lassen Sie uns nun im Haupttext tiefer in die Welt von CSS in JS eintauchen. Es wird eine lange Reise sein, aber ich hoffe, dass sie Ihnen neueInspirationen und Möglichkeiten zur Herausforderung bietet.
1. Was ist CSS in JS?
Anstatt separate CSS-Dateien zu erstellen, können Sie Stile neben Komponenten in Ihren JavaScript-Dateien definieren.
/** @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> ); }
2. Der Hintergrund von CSS in JS
Die Probleme, die CSS-in-JS lösen wollte, waren wie folgt:
Und wie löst CSS in JS das Problem?
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:
Layout auszudrücken ist eine weitere Hürde in CSS, die durch die Interaktionen zwischen verschiedenen Eigenschaften noch komplexer wird.
CSS mag oberflächlich betrachtet einfach erscheinen, es ist jedoch nicht leicht zu beherrschen. Es ist bekannt, dass viele Menschen bereits mit der einfachen Mittelausrichtung Schwierigkeiten haben (1, 2). Die scheinbare Einfachheit von CSS kann trügen, da seine Tiefe und Nuancen es anspruchsvoller machen, als es zunächst scheint.
Zum Beispiel hat die Anzeige in CSS verschiedene Layoutmodelle: Block, Inline, Tabelle, Flex und Raster.
Stellen Sie sich die Komplexität vor, wenn die folgenden Eigenschaften in Kombination verwendet werden: Boxmodell, Responsive Design, Floats, Positionierung, Transformation, Schreibmodus, Maske usw.
Mit zunehmender Projektgröße wird es aufgrund von Nebenwirkungen im Zusammenhang mit DOM-Positionierung, Kaskadierung und Spezifität noch schwieriger.
Layoutprobleme sollten durch gut gestaltete CSS-Frameworks behoben werden, und wie bereits erwähnt, kann die Verwendung von CSS in JS zur Isolierung von Stilen Nebenwirkungen abmildern.
Dieser Ansatz löst jedoch nicht alle Probleme vollständig. Stilisolation kann zu neuen Nebenwirkungen führen, wie z. B. zu erhöhten Dateigrößen aufgrund von doppelten Stildeklarationen in jeder Komponente oder Schwierigkeiten bei der Aufrechterhaltung der Konsistenz allgemeiner Stile in der gesamten Anwendung.
Dies steht in direktem Konflikt mit den Design-kombinatorischen Explosions- und Konsistenzproblemen, die als Nächstes eingeführt werden.
Im Moment können wir Layout-Anliegen an Frameworks wie Bootstrap oder Bulma delegieren und sich mehr auf Managementaspekte konzentrieren.
Im Kern ist CSS ein leistungsstarkes Werkzeug zum Ausdruck und Implementieren von Design in der Webentwicklung.
Beim Erstellen einer UI/UX sind viele Faktoren zu berücksichtigen, und die folgenden Elemente müssen in Ihrem Design unbedingt berücksichtigt werden:
Die genaue Darstellung verschiedener Designelemente unter verschiedenen Bedingungen stellt eine große Herausforderung dar.
Bedenken Sie, dass Sie Geräte (Telefone, Tablets, Laptops, Monitore, Fernseher), Eingabegeräte (Tastatur, Maus, Touch, Stimme), Quer-/Hochformatmodi, dunkle/helle Themen, hohen Kontrastmodus und Internationalisierung (Sprache) berücksichtigen müssen , LTR/RTL) und mehr.
Darüber hinaus müssen je nach Benutzereinstellungen möglicherweise unterschiedliche Benutzeroberflächen angezeigt werden.
Daher ist eine kombinatorische Explosion unvermeidlich und es ist unmöglich, sie einzeln manuell zu implementieren. (Bildquelle)
Als repräsentatives Beispiel sehen Sie sich die Definition und Zusammenstellung des Tab-Leisten-Layouts in meinem Firefox-Theme an.
Obwohl nur das Betriebssystem und die Benutzeroptionen berücksichtigt werden, führt eine Datei mit etwa 360 Zeilen zu einem Kompilierungsergebnis von etwa 1400 Zeilen.
Die Schlussfolgerung ist, dass eine effektive Designimplementierung von Natur aus skalierbar sein muss und typischerweise entweder programmgesteuert oder durch klar definierte Regelsätze verwaltet werden muss.
Das Ergebnis ist ein Designsystem für konsistentes Management im großen Maßstab.
Designsysteme dienen als einzige Quelle der Wahrheit und decken alle Aspekte des Designs und der Entwicklung ab, von visuellen Stilen bis hin zu UI-Mustern und Code-Implementierung.
Laut der Nielsen Norman Group umfasst ein Designsystem Folgendes:
Designsysteme sollten als Schnittstelle für Designer und Entwickler fungieren und Funktionalität, Form, Zugänglichkeit und Anpassung unterstützen.
Aber Designer und Entwickler denken anders und haben unterschiedliche Perspektiven.
Lassen Sie uns Komponenten als Linse nutzen, um die Unterschiede zwischen den Perspektiven von Designern und Entwicklern zu erkennen!!
Der Designer sollte auch entscheiden, welches Symbol für das Kontrollkästchen-Steuerelement verwendet wird.
Designer neigen dazu, sich auf die Form zu konzentrieren, während sich Entwickler eher auf die Funktion konzentrieren.
Für Designer ist ein Knopf ein Knopf, wenn er zum Drücken einlädt, während er für Entwickler ein Knopf ist, solange er gedrückt werden kann.
Wenn die Komponente komplexer ist, könnte die Kluft zwischen Designern und Entwicklern noch größer werden.
Visuelle Optionen: Das Erscheinungsbild ändert sich entsprechend den eingestellten Optionen wie Primär, Akzent, Umrissen, Nur Text usw.
Statusoptionen: Das Erscheinungsbild ändert sich je nach Status und Kontext
Entwurfsentscheidung:Bestimmen von Werten mit Komponentenstruktur, visuellen/Zustandsoptionen, visuellen Attributen (Farbe, Typografie, Symbol usw.) und mehr.
Die endgültige Form ist eine Kombination aus Option, Zustand und Kontext, was zu der oben erwähnten kombinatorischen Explosion führt.
Von diesen stimmt Option mit der Perspektive des Designers überein, während Status und Kontext dies nicht tun.
Führen Sie eine Zustandskomprimierung unter Berücksichtigung paralleler Zustände, Hierarchien, Wachen usw. durch, um zur Designer-Perspektive zurückzukehren.
Wie Sie vielleicht inzwischen erkannt haben, ist die Erstellung und Pflege einer hochwertigen Benutzeroberfläche harte Arbeit.
Die verschiedenen Bundesstaaten werden also von der State-Management-Bibliothek abgedeckt, aber wie wurden Stile verwaltet?
Während Methoden, Bibliotheken und Frameworks weiterhin entstehen, weil die Lösung noch nicht etabliert ist, gibt es drei Hauptparadigmen.
Unter diesen fühlt sich CSS in JS wie ein Paradigma an, das einen grundlegend anderen Ansatz zum Ausdrücken und Verwalten von Stilen verwendet.
Dies liegt daran, dass CSS in JS wie Mechanismen ist, während semantisches CSS und atomares CSS wie Richtlinien sind.
Aufgrund dieses Unterschieds muss CSS in JS getrennt von den beiden anderen Ansätzen erklärt werden. (Bildquelle)
Wenn man über den CSS-in-JS-Mechanismus spricht, fallen einem vielleicht CSS-Prä-/Postprozessoren ein.
Wenn man über Richtlinien spricht, fallen einem möglicherweise auch „CSS-Methoden“ ein.
Daher werde ich Stilverwaltungsmethoden in der folgenden Reihenfolge vorstellen: CSS in JS, Prozessoren, semantisches CSS und Atomic CSS sowie andere Stilmethoden.
Was ist dann die wahre Identität von CSS in JS?
Die Antwort liegt in der obigen Definition.
Schreiben Sie in JavaScript und isolieren Sie CSS für jede Komponenteneinheit.
Unter anderem kann die CSS-Isolation ausreichend auf vorhandenes CSS angewendet werden, um globale Namespace- und Breaking Isolation-Probleme zu lösen.
Das sind CSS-Module.
Basierend auf dem Link zum oben erwähnten Artikel zur CSS-in-JS-Analyse habe ich die Funktionen wie folgt kategorisiert.
Jede Funktion hat Kompromisse, und diese sind wichtige Faktoren beim Erstellen von CSS in JS.
Besonders erwähnenswerte Inhalte wären SSR (Server Side Rendering) und RSC (React Server Component).
Das sind die Richtungen, die React und NEXT, die das Frontend darstellen, anstreben, und sie sind wichtig, weil sie einen erheblichen Einfluss auf die Umsetzung haben.
Serverseitiges Rendering erstellt HTML auf dem Server und sendet es an den Client, daher muss es als Zeichenfolge extrahiert werden und eine Antwort auf Streaming ist erforderlich. Wie im Beispiel von Styled Component können zusätzliche Einstellungen erforderlich sein. (Bildquelle)
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.