Im Grunde ist das Prinzip einfach: Anstelle von Stylesheets verpackt ihr euer CSS in Komponenten. Die Wahl der richtigen CSS-in-JS-Library kann bei fast 50 Optionen zwar durchaus schwierig ausfallen, und am Ende kommt es auf euren jeweiligen Use-Case an. Einige Eigenschaften haben jedoch alle Libraries gemeinsam:
Gescoptes CSS ✔️
Alle CSS-in-JS-Bibliotheken generieren einzigartige Namen für eure CSS-Klassen, eine Idee, die mit CSS Modules populär wurde. Alle Styles wirken sich nur auf ihre jeweilige Komponente aus. Styling außerhalb der Komponente bleibt von ihnen völlig unberührt und vice versa. Dass die Namen eurer CSS-Klassen kollidieren, es Probleme mit der Spezifität gibt oder der Struggle mit einer sinnvollen Benennung von drölfzigtausend CSS-Klassen gehört damit der Vergangenheit an. Das ist auch der Grund, warum Entwickler, die auf einen komponentenbasierten Ansatz – also beispielsweise React – setzen, CSS-in-JS-Libraries so schätzen:
import React, {Component} from 'react';
import styled from 'styled-components';
const Background = styled.div`
background: red
const Paragraph = styled.p`
color: white
`
class App extends Component {
render() {
>return (
<Background>
<Paragraph>t3n - digital pioneers</Paragraph>
</Background>
export default App;
`
Im Beispiel, hier mit der beliebten CSS-in-JS-Library StyledComponents für React und React Native, sind die Styles quasi von der Komponente umschlossen und wirken sich ausschließlich darauf aus.
Keine Inline-Styles ✔️
Styles, die innerhalb des HTML vergeben werden, nennt man Inline-Styles. Sogenannte Pseudo-Klassen, Pseudo-Elemente und Media-Queries sind bei dieser Schreibweise allerdings ausgeschlossen. Responsive Designs sind unter Verwendung von Inline-Styles ebenfalls nicht möglich. Zudem gelten Inline-Styles ganz generell als weniger performant als Klassennamen. Zwei gute Gründe, weshalb die meisten neueren CSS-in-JS-Libraries zugunsten von CSS-Klassennamen von Inline-Styles Abstand nehmen, um Pseudo-Klassen und -Elemente, Media-Queries und Keyframe-Animationen zu ermöglichen.
Automatische Vendor-Prefixes ✔️
Bis neue CSS-Features in allen Browsern verfügbar sind, dauert es aufgrund des aufwendigen CSS-Standardisierungsprozess oft Jahre. Um neue, noch nicht standardisierte Features vorab schon nutzbar zu machen, wird nicht-standardisierte CSS-Syntax unter einem sogenannten Vendor-Prefix ausgeliefert. In älteren Browsern werden neuere Features oft nicht unterstützt, dann kommen die Prefixe zum Einsatz. Auswendig zu wissen, welche Features für welche Browser eines benötigen, ist vielleicht etwas hochgegriffen, zumal sich das über die Zeit mit der Veröffentlichung von Updates und neuen Versionen ständig ändern kann. Ressourcen wie caniuse.com können hier wertvolle Quellen sein.
Die meisten CSS in JS-Libraries nehmen Entwickler:innen diese Aufgabe dankenswerterweise ab. Das heißt, ihr könnt einfach die Standard-Syntax nutzen und die Bibliothek generiert diese Properties inklusive Vendor-Prefix automatisch.
Server-Side-Rendering ✔️
Single-Page-Apps oder SPA kommen in der Regel ohne Server-Side-Rendering aus. Sie werden traditionell im Browser gerendert. Vom http-Server kommt lediglich eine initiale, leere HTML-Page. Websites und Apps, die von Suchmaschinen geparsed und indexiert werden sollen, sollten aus einer Reihe von Gründen aber besser über serverseitig gerenderte Pages verfügen. Das Gleiche gilt für sogenannte Static-Site-Generators, wo Inhalte als statische HTML-Files zusammen mit dem zugehörigen CSS während der Build-Time generiert werden. Die meisten CSS-in-JS-Bibliotheken unterstützen SSR, sodass ihr sie für alle möglichen Arten von Webprojekten nutzen könnt.
Unterschiede
Soviel zu den Gemeinsamkeiten. Im Einzelnen unterscheiden sich die verschiedenen JS-in-CSS-Libraries in mehreren Punkten. Einige, wie Styled Components oder Stitches, wurden spezifisch für React entwickelt, andere – JSS, Treat, Emotion – sind Framework-agnostisch. Die meisten unterstützen ein Format, in dem HTML, CSS und JS in der selben Datei stehen können, was sich in vielen Fällen als Vorteil erweist. So wird auf einen Blick ersichtlich, welches Styling zu welcher Komponente gehört. Für den Fall, dass die Styles ein Datei zu unübersichtlich werden lassen, gibt es immer noch die Möglichkeit, sie in eine separate CSS-Datei auszulagern. Definiert werden können die Styles je nach Library entweder als String innerhalb von ES-Template-Literalen oder in der Object-Styles-Syntax als reguläre JS-Objekte. Einige Bibliotheken – Styled Components, Compiled, Emotion, Goober – unterstützen beides.
Auch für die Anwendung der so definierten Styles gibt es mehrere Optionen.
Die class-attribute- oder className-Property-Variante
Für CSS-kundige Entwickler der wohl eingängigste Weg: Styles einfach als Klassennamen zu vergeben. Via API geben diese CSS-in-JS-Libraries die Klassennamen als String aus, der dann an HTML-Elemente vergeben werden kann.
// "css" is the API of a generic CSS-in-JS library here
const heading_style = css({
color: "red"
});
const heading = `<h2 class="${heading_style}">Title</h2>`;
function Heading() { return <h2 className={heading_style}>Title</h2>; }
Diese Methode ähnelt wohl am ehesten traditionellem CSS: Zuerst wird ein Style definiert und anschließend auf die so zu gestaltenden Elemente angewandt. Unterstützt wird sie unter anderem von Treat, Emotion, Goober, Fela, JSS, Stitches, TypeStyle und Styled JSX.
Die <styled>-Variante
Eine weitere beliebte Gangart ist die <styled>-Variante. Eingeführt wurde sie erstmals in der Styled-Components-Library. Statt die Styles separat zu definieren, werden bei diesem Ansatz Element und zugehöriges Styling zusammen definiert. Die Styled-Components-API gibt dann eine neue Komponente zurück, die bereits über das gewünschte Styling verfügt. Unterstützt wird sie von Stitches, Emotion, Styled Components und Compiled.
Die CSS-Property-Methode
Ein neuerer Ansatz, der durch eine Library namens Emotion populär wurde, funktioniert über die Style-Vergabe an spezielle Properties. Die API ist allerdings nur mit JSX verwendbar. Dieser Ansatz hat den Vorteil, dass der Import einer API wegfällt, Styles lassen sich einfach an die CSS-Property weitergeben, ähnlich wie bei der Verwendung von Inline-Styles. Weil es sich bei der .css-Property nicht um ein Standard-HTML-Attribut handelt, ist die Installation eines separaten, von der jeweiligen Bibliothek bereitgestellten Plugins nötig.
2 Methoden, die Styles im Browser auszuliefern
Es gibt zwei Arten, auf die die Styles im Browser ausgeliefert werden.
DOM-Injection zur Laufzeit
Die meisten CSS-in-JS Libraries fügen die Styles zur Laufzeit dem DOM hinzu. Sie nutzen entweder <style>-Tags, die während SSR der HTML-Page angehängt werden, oder eine API namens CSSStyleSheet, um die Styles direkt innerhalb des CSS-Object-Models zu verwalten. Die Styles während SSR der HTML-Page hinzuzufügen, wirkt sich positiv auf die Performance aus, weil so keine separate CSS-Datei, die erst vom Server abgerufen werden muss, den Rendering-Prozess blockiert. Dieser Ansatz bietet sogenannte Critical-CSS-Extraktion out of the Box; das bedeutet, während des SSR werden nur die Styles dem HTML-Dokument hinzugefügt, die nötig sind, um die initiale Ansicht zu rendern. Dynamische Styles werden entfernt, die Menge an Code, der initial heruntergeladen muss, wird so noch einmal kleiner und Ladezeiten entsprechend reduziert.
Allerdings ist die Handhabung dynamischer Stile im Browser bei diesem Ansatz nicht ohne eine zusätzliche Runtime-Library möglich. Auch werden die inline eingefügten SSR-Styles nicht gecached und müssen bei jedem Request erneut an den Browser ausgeliefert werden. Während der Rehydration werden sie in Form von JS erneut an den Browser ausgeliefert.
Statische Extraktion von CSS-Dateien
Die meisten CSS-Libraries fügen die Styles in das DOM ein. Nur einige wenige, darunter Treat, style9 und Linaria, unterstützen die statische Extraktion von CSS-Dateien.
Atomic CSS
Einige Bibliotheken verfolgen mit Atomic CSS einen dritten Ansatz. Inspiriert ist er von CSS-Frameworks wie Tailwind oder Tachyons. Anstelle einer CSS-Klasse, die alle Properties enthält, die für ein bestimmtes Element definiert wurden, generieren diese Bibliotheken eine CSS-Klasse für jede Property-Value-Kombination, die dann in der gesamten Codebase wiederverwendet werden kann.
In der Theorie ist dieser Ansatz gerade für große Anwendungen geeignet, bei denen die Klassen oft wiederverwendet werden. Der einzige Haken: Die Klassennamen müssen auf jedes der HTML-Elemente angewandt werden, die HTML-Datei wird also größer. Trotzdem dürfte bei diesem Ansatz die Menge der Bytes, die an den Browser ausgeliefert werden, im Schnitt kleiner sein als bei den vorher genannten.
Die passende Bibliothek finden
Wer bis hierher gelesen hat, stellt fest, dass die Entscheidung für die passende Library alles andere als einfach ist. Eine Reihe von Fragen zu beantworten, kann bei der Entscheidung für die richtige Bibliothek allerdings helfen:
- Nutzt das Projekt React oder nicht? Wenn ja, haben Entwickler die Qual der Wahl zwischen einer breiten Palette an CSS-in-JS-Bibliotheken. Für Anwendungen, die ein anderes Framework oder Vanilla JS nutzen, muss hingegen auf eine Framework-agnostische Bibliothek zurückgegriffen werden.
- Ist die App sehr interaktiv und setzt auf client-seitiges Rendering oder handelt es sich um eine dynamische Website mit SSR? In diesem Fall ist es wahrscheinlich schlauer, statische CSS-Dateien zu extrahieren. So profitiert die Anwendung von Caching.
- Ob bereits CSS vorhanden ist, das migriert werden muss, beeinflusst die Wahl ebenfalls. In diesem Fall empfiehlt es sich, eine CSS-in-JS-Bibliothek zu nutzen, die Tagged Templates unterstützt. So wird die Migration einfacher.
- Auch, ob die Anwendung für Erstbesucher oder wiederkehrende Nutzer:innen optimiert werden soll, spielt eine Rolle. Statische .css-Dateien bieten für wiederkehrende User die beste UX, allerdings ist beim ersten Visit dann ein zusätzlicher http-Request nötig, der das Rendern der Seite kurzzeitig blockiert. Wer allerdings vorhat, seiner Anwendung häufig einen neuen Look zu verpassen, kann den Faktor Caching bei der Wahl natürlich außen vorlassen.
- Nutzen Entwickler:innen Styles und Komponenten vielfach mehrmals in einer Anwendung, sind Frameworks, die auf Atomic CSS bauen, wohl die beste Option.