webinale Blog

Houdini: Hausgemachte CSS-Magie

Eigene CSS-Funktionen definieren

May 26, 2021

CSS wirkt manchmal fast schon magisch. Aber auch die besten Zaubertricks stoßen an ihre Grenzen, wenn man etwas erreichen möchte, das noch in keiner Formel beschrieben wurde. Im Fall von CSS gibt es aber Abhilfe: Houdini erlaubt die Definition eigener Funktionen.

Wie fügt man eine neue Funktion zu CSS hinzu? Man könnte sich auf Twitter darüber beschweren und einfach darauf hoffen, dass jemand vom Standardisierungsgremium sich des Anliegens annimmt – und voilà, in ein paar Jahren ist das benötigte Feature vielleicht verfügbar. Oder: Man schreibt selbst einen JavaScript-Polyfill, der aber nicht sonderlich leistungsfähig ist.

CSS ist halt irgendwie auch „magisch“. Wird ein einziger Style verändert, kann das die gesamte Webseite beeinflussen oder den Code beschädigen, sodass Vorsicht angeraten ist. Mit CSS Houdini können Entwickler nun aber ihre eigene „Magie“ erschaffen. Denn durch das Einklinken in die Renderingphasen des Browsers können neue Funktionen zu CSS hinzugefügt und dem Browser so einige neue Tricks beigebracht werden.

Wie funktioniert das Rendering?

Browser haben normalerweise vier Hauptphasen in ihrer CSS-Rendering-Pipeline (Abb. 1). Um die CSS-Stufen zu verstehen, muss mit den Styles begonnen werden. Hier sammelt der Browser alle Stylesheets, die sich im Dokument befinden, und findet dann heraus, welches Element von welchem dieser Styles betroffen ist. Sobald der Browser weiß, wie groß die Elemente sind und an welcher Position sie gerendert werden sollen, beginnt er, die leeren und transparenten Felder auf der Seite anzuordnen. Im nächsten Schritt kann der Browser einfach das Layout der Seite übernehmen und die Pixel „malen“. Dabei handelt es sich in der Regel um Text, Farben, Bilder, Rahmen, Hintergründe usw. Das Malen erfolgt auf mehreren Oberflächen, die als Ebenen bezeichnet werden. Sobald dieser Malprozess fertig ist, werden alle Ebenen an den Compositor übergeben. Der Compositor fügt sie auf der Seite, die wir sehen, zusammen. Wenn sich etwas auf einer eigenen Ebene befindet, können diese Ebenen verschoben und so Animationen erstellt werden.

Abb. 1: Renderingschritte im Browser

NEWSLETTER

Alle News zu Web Design, UX und Digital Marketing

Was ist Houdini?

Harry Houdini war ein berühmter Illusionskünstler. Er ist die Inspiration für die Namensgebung dieses CSS API. Houdini ist ein Standard in der CSS-Arbeitsgruppe im W3C (World Wide Web Consortium), um besseren Zugang zur CSS Engine des Browsers zu erhalten und mehr Kontrolle darüber zu haben, wie eine Seite aussieht oder gerendert wird. Mit Houdini kann dem Browser beigebracht werden, Farben oder Layout zu verarbeiten. Das erlaubt es Entwicklern, Layouteffekte, Rahmen, Hintergründe, Bilder oder andere Dinge zu erstellen, die derzeit in CSS nicht möglich sind. Außerdem müssen weniger Polyfills und JavaScript-Abhängigkeiten geschrieben werden und es können reine CSS-Polyfills verfasst und so neue CSS-Funktionen geschaffen werden, ohne darauf warten zu müssen, dass diese nativ im Browser implementiert werden. Damit sind kontinuierliche Verbesserungen möglich.

Houdini ist noch in der Entwicklungsphase, aber die Beschäftigung damit erlaubt einen Einblick in die Entwicklung von CSS und was die Zukunft dahingehend noch bereithält. Zum Zeitpunkt der Erstellung dieses Artikels wurden drei der Houdini APIs in Chromium-basierten Browsern ausgeliefert. Ein aktueller Status findet sich auf der Webseite https://ishoudinireadyyet.com/ (Abb. 2). Ein detaillierter Blick auf diese drei APIs lohnt sich, da sich die anderen APIs möglicherweise noch massiv verändern werden. Alle in diesem Artikel erwähnten Beispiele sind ausführbar, wenn das Chrome-Flag-Experimental-Web-Platform-Feature aktiviert ist oder der Polyfill für CSS Paint hinzugefügt wurde.

Abb. 2: Übersicht auf ishoudinireadyyet.com

Properties and Values API

Um das Properties and Values API zu verstehen, wird im Folgenden eine Schaltfläche mit linearen Farbverläufen animiert, die mit CSS-Variablen definiert wurden.

Abb. 3: Button mit animiertem Farbverlauf

Wenn das in Abbildung 3 gezeigte Stück Code ausgeführt wird, kann man sehen, dass die Schaltfläche nicht fließend in die einzelnen Farben übergeht, sondern dass es einen abrupten Farbwechsel gibt. CSS-Variablen sind praktisch und dynamisch, aber sie können nicht animiert werden. Warum ist das so? Der Browser parst CSS-Variablen immer als Strings. So weiß man nicht, ob eine gegebene Variable eine Farbe oder eine Größe in Pixeln ist. Es gibt also keine semantische Bedeutung für die Variablen. Das Properties and Values API von Houdini bietet die Möglichkeit, einen Typ für CSS-Variablen zu definieren (Abb. 4), und ermöglicht eine direkte Integration mit dem Parser.

Abb. 4: Custom Property mit Houdini

Damit die Animation funktioniert, muss die CSS-Variable mit dem registerProperty API registriert werden. Auf diese Weise wird sie mit Daten angereichert, typisiert und hat nun eine semantische Bedeutung.

Typed Object Model (Typed OM) API

Von allen Houdini APIs hat das Typed OM API den größten Einfluss auf das Tagesgeschäft des Entwicklers. In den guten alten Zeiten wurde getComputedProperty verwendet, um den Stil eines beliebigen DOM Node zu erhalten, wie Abbildung 5 zeigt.

Abb. 5:getComputedProperty

Typed OM gibt ausführliche Informationen über eine Eigenschaft in typisierter JavaScript-Darstellung – mit Wert und Einheit. Das ermöglicht Verbesserungen bei der Performance, da der Browser CSS auf eine strukturiertere Weise versteht und nicht mehr alles von Grund auf tokenisieren muss. So sind berechnete Styles einfach zu lesen und zu analysieren. Typed OM kümmert sich auch um das Fehlerhandling, da die Typen der Elementstyles definiert sind: Wenn ein Entwickler versucht, einem längenbezogenen Attribut einen Farbwert zuzuweisen, wird vom Typed OM ein Fehler angezeigt. Das Rätselraten, ob es sich bei den Namen um Camel Case oder Strings handelt (z. B. el.style.backgroundColor vs el.style[‘background-color’]), hat damit ein Ende. CSS-Eigenschaftsnamen in Typed OM sind immer Strings und entsprechen dem, was auch tatsächlich in CSS geschrieben wird (Abb. 6).

Abb. 6: Typed OM

NEWSLETTER

Alle News zu Web Design, UX und Digital Marketing

Außerdem kann typisiertes CSS mit Type-APIs erstellt werden – und Styles können erstellt, gelesen, aktualisiert oder von einem Element gelöscht werden. Es fügt auch weitere funktionale Programmierkonzepte hinzu, wie beispielsweise das Aufrufen von Funktionen innerhalb anderer Funktionen.

Paint API

Geht man einen Schritt weiter und betrachtet ein API auf höherer Ebene, beinhaltet dieses Paint API ein Worklet. Ein Worklet ist von einem Worker zu unterscheiden: Alle Worklets sind Workers, aber nicht alle Worker sind Worklets. Worklets sind vielmehr spezifische Worker. Der Browser verfügt über eine Ereignisschleife, die Ereignisse verarbeitet. Immer wenn ein Ereignis eintritt, prüft die JavaScript Engine, ob es einen Handler für dieses Ereignis in der Codebasis gibt. Sie nimmt den Code für diesen Handler und stellt ihn in eine Warteschlange. Einfach ausgedrückt nimmt die Ereignisschleife bei jeder Umdrehung etwas aus der Warteschlange und führt es aus. Ein Worker ist eine separate Ereignisschleife mit isoliertem Bereich, eigenen Ereignissen und Handlern. Der Aufwand, eine Ereignisschleife zu erstellen und zu pflegen, ist allerdings groß, weshalb nicht einfach Hunderte von Workern zur gleichen Zeit erstellt werden können.

Worklets sind ebenfalls isolierter JavaScript-Code mit eigenem Gültigkeitsbereich, aber sie haben keine Ereignisschleife, sondern hängen sich an eine bereits vorhandene Ereignisschleife. Das macht sie billiger in der Erstellung und Wartung – und es können mehrere Worklets an eine Ereignisschleife angehängt werden. Da die meisten Worklets zustandslos sind, können sie während ihrer Lebensdauer in eine andere Ereignisschleife migriert werden. Ein Worklet ist ein in sich geschlossenes Modul, das eine fertige Arbeitsoberfläche herstellt und neben dem Haupt-Thread laufen kann. Es ist sozusagen eine Klasse, die eine bestimmte Schnittstelle implementiert, damit sie ein Element anpassen kann. Es gibt also einen formalen API Handshake zwischen dem Browser und dem Worklet.

Dieses API ermöglicht es Entwicklern, benutzerdefinierte Funktionen zu bestimmen, die in CSS direkt als Hintergründe, Rahmen, Masken und mehr verwendet werden können. Der Browser leitet die Anfrage, die er von CSS erhält, an das entsprechende Houdini Worklet weiter – eine Erweiterung für den Paint-Rendering-Schritt des Browsers, in dem die visuellen Eigenschaften (Farbe, Hintergrund, Rahmen etc.) bestimmt werden. Das Worklet läuft auf einem eigenen Thread, der den Haupt-Thread entlastet und ihn für andere Dinge freihält.

Im Folgenden wird die in Abbildung 7 gezeigte Wellenlinie, die im nativem CSS nicht existiert, mit dem Houdini Paint API erstellt.

Abb. 7: Wellenlinie

Der erste Schritt ist das Schreiben des Worklet. Um ein Paint Worklet zu schreiben, muss man wissen, wie das Canvas API verwendet werden kann, dank dem man etwas auf der Seite zeichnen kann. Das Paint Worklet ist eine Klasse, die eine Paint-Funktion enthält. Es wird eine Datei namens curved-line.js erstellt (Abb. 8). Dieses Codestück demonstriert, wie man die Wellenlinie zeichnet. Zu beachten ist dabei, dass am Ende der Datei die Paint-Klasse mit der Funktion registerPaint registriert wird. Sie akzeptiert nicht nur die Paint-Klasse, sondern auch eine Zeichenkette. Innerhalb einer CSS-Datei kann dieser String verwendet werden, um das Worklet aufzurufen (Abb. 9). Ein Paint-Worklet-Modul muss im Skript enthalten sein (Abb. 10), damit das CSS weiß, wo dieses hinterlegt ist.

Abb. 8: Paint Worklet

Abb. 9: Paint Worklet aufrufen

Abb. 10: Worklet im Skript

NOCH MEHR ZU WEB DESIGN?

Der Web Design & Development Track

Mit diesem Worklet wird überall dort, wo ein <hr />-Element verwendet wird, eine gebogene Linie gezeichnet. Ein Paint Worklet kann durch die Verwendung von CSS-Variablen noch leistungsfähiger und dynamischer gemacht werden. Das kann über das dritte Argument der Paint-Funktion des Worklet geschehen, das in Abbildung 11 zu sehen ist. Mit lineHeight, lineWidth, lineColor und lineSpread (Abb. 12) können geschwungene Linien in jedem Element des DOM dynamisch gestylt werden.

Abb. 11: Argumente der Paint-Funktion

Abb. 12: Code für individuelle Linien

Layout and Animation APIs

Die Layout and Animation APIs befinden sich noch im experimentellen Stadium. Das Layout API ermöglicht es Entwicklern, ihre eigenen Algorithmen zu schreiben. Im Grunde könnte ein Entwickler damit einen eigenen Layoutalgorithmus wie Flexbox oder Grid erstellen. Er wird dann als Teil des Layoutschrittes im Rendering-Flow ausgeführt.

In ähnlicher Weise bietet das Animation API eine Möglichkeit, die Timing-Funktionen einer Animation zu manipulieren. Dieses API hilft Entwicklern im Wesentlichen beim Schreiben von Parallaxenanimationen oder beliebigen Keyframe-Animationen, die außerhalb des Haupt-Threads arbeiten sollen. Es wird als Teil des Composite-Schritts im Rendering-Flow ausgeführt.

Fazit

Houdini ist so leistungsstark und effizient, sodass es nicht übertrieben ist, es als die „Zukunft von CSS“ zu bezeichnen. Houdini bietet nützliche APIs, um die CSS-Logik von der JavaScript-Logik zu trennen, was eins der kniffligsten Probleme im Web angeht: die Separation of Concerns. Durch die Ermöglichung von Worklets, die nichts anderes als kleine, unabhängige Module sind, wird der gesamte Prozess des Schreibens von CSS-Funktionen leicht wiederverwend- und teilbar.

Houdini eröffnet eine ganze Welt von Möglichkeiten im Web. CSS-Funktionen, die bisher als extrem komplex und nicht performant galten, scheinen mit nur einer Handvoll CSS möglich zu sein. Es ist bemerkenswert, zu sehen, wie Entwickler auf der ganzen Welt mit Houdini experimentieren. Hier einige erstaunliche Beispiele von Houdini-API-Experimenten aus der Community: https://css-houdini.rocks/, https://houdini.how/, https://github.com/GoogleChromeLabs/houdini-samples

MEHR INFOS ZUR WEBINALE?

JETZT NEWSLETTER ABONNIEREN