Ein eigenes Paint Worklet für ein wiederverwendbares Hintergrundmuster
Ein Paint Worklet zeichnet einen Hintergrund nicht als fertige Bilddatei, sondern als kleines JavaScript-Programm, das der Browser bei jedem Repaint direkt in den Element-Hintergrund rendert. Wer einmal ein parametrisierbares Muster als Worklet registriert, kann es über CSS Custom Properties in beliebig vielen Komponenten wiederverwenden, ohne für jede Farbvariante eine neue SVG-Datei zu exportieren.
Inhaltsverzeichnis
- 1. Was die CSS Paint API ist und wie sie sich in die Cascade einklinkt
- 2. Ein erstes Worklet registrieren: registerPaint und addModule
- 3. Das Worklet in CSS einbinden: paint() als Hintergrundwert
- 4. Werte an das Worklet übergeben: inputProperties und @property
- 5. Wiederverwendbarkeit: ein Muster für mehrere Komponenten parametrisieren
- 6. Performance-Vergleich: Paint Worklet gegen SVG-Background und Bilddatei
- 7. Wann sich ein Paint Worklet nicht lohnt
- 8. Browser-Support und eine robuste Fallback-Strategie
- 9. Debugging und Entwicklungsworkflow für Paint Worklets
- 10. Zusammenfassung
- 11. FAQ
1. Was die CSS Paint API ist und wie sie sich in die Cascade einklinkt
Die CSS Paint API ist Teil der Houdini-Initiative und erlaubt es, eine JavaScript-Funktion als gültigen Wert für background-image und ähnliche Bild-Eigenschaften zu registrieren. Statt einer URL oder eines Gradienten steht dort paint(mustername), und der Browser ruft bei jedem Layout- oder Style-Wechsel die registrierte paint()-Methode des Worklets auf, die auf einer Canvas-ähnlichen Zeichenfläche direkt in den Element-Hintergrund malt.
Der entscheidende Unterschied zu einem klassischen background: url(...) ist, dass ein Paint Worklet keine fertige Datei lädt, sondern bei jedem Aufruf neu rechnet, basierend auf der aktuellen Elementgröße und den übergebenen Custom Properties. Das macht Worklets besonders für Muster interessant, die sich responsiv an die Boxgröße anpassen sollen, etwa ein Punktraster, das bei jeder Breakpoint-Änderung neu und scharf gezeichnet wird, statt wie ein skaliertes Bild zu verschwimmen.
2. Ein erstes Worklet registrieren: registerPaint und addModule
Ein Paint Worklet lebt in einer eigenen JavaScript-Datei, die in einem separaten Thread ausgeführt wird und keinen Zugriff auf das DOM hat. Innerhalb dieser Datei ruft man registerPaint(name, klasse) auf und übergibt eine Klasse mit einer paint(ctx, size, properties)-Methode, die auf dem übergebenen 2D-Kontext zeichnet. Der Hauptdokument-Code lädt dieses Modul asynchron über CSS.paintWorklet.addModule(pfad), bevor der erste Paint-Aufruf im Stylesheet greifen kann.
Wichtig ist, dass addModule ein Promise zurückgibt und der Browser das Muster erst zeichnet, sobald das Modul geladen ist. Bei einem sehr frühen ersten Render kann deshalb kurzzeitig kein Hintergrund sichtbar sein. In der Praxis registriert man das Worklet deshalb so früh wie möglich im Kopf des Dokuments und plant einen unauffälligen Fallback-Hintergrund über CSS ein, der greift, solange das Worklet noch nicht bereitsteht.
// dot-pattern-worklet.js -- registered as a CSS Paint Worklet
class DotPatternPainter {
static get inputProperties() {
return ['--dot-color', '--dot-size', '--dot-gap'];
}
paint(ctx, size, properties) {
const color = properties.get('--dot-color').toString().trim() || '#7c3aed';
const dotSize = parseFloat(properties.get('--dot-size')) || 2;
const gap = parseFloat(properties.get('--dot-gap')) || 18;
ctx.fillStyle = color;
for (let y = gap / 2; y < size.height; y += gap) {
for (let x = gap / 2; x < size.width; x += gap) {
ctx.beginPath();
ctx.arc(x, y, dotSize, 0, Math.PI * 2);
ctx.fill();
}
}
}
}
registerPaint('dot-pattern', DotPatternPainter);
3. Das Worklet in CSS einbinden: paint() als Hintergrundwert
Sobald das Modul geladen ist, verhält sich paint(dot-pattern) in CSS wie jeder andere background-image-Wert: Es lässt sich mit background-size, mehreren Layern über Kommas und sogar mit background-blend-mode kombinieren. Für die Karte einer Produktseite reicht damit ein einziger CSS-Block, um ein Punktraster als dekorativen Hintergrund zu setzen, ganz ohne zusätzliche HTTP-Anfrage für eine Bilddatei.
Das Laden des Moduls geschieht im Hauptdokument mit wenigen Zeilen JavaScript und muss nur einmal pro Seite passieren, unabhängig davon, wie viele Elemente das Muster später verwenden. Dieses einmalige Setup ist einer der praktischen Vorteile gegenüber mehreren einzelnen SVG-Dateien, die jede für sich geladen und vom Browser gecacht werden müssten.
.product-card {
--dot-color: #7c3aed;
--dot-size: 1.5px;
--dot-gap: 16px;
background-image: paint(dot-pattern);
background-color: #faf5ff;
}
.product-card--dark {
--dot-color: #c4b5fd;
background-color: #1e1033;
}
4. Werte an das Worklet übergeben: inputProperties und @property
Ein Worklet erhält Werte aus CSS ausschließlich über die in inputProperties aufgelisteten Custom Properties, alle anderen CSS-Eigenschaften des Elements sind für das Worklet unsichtbar. Deshalb müssen Farbe, Punktgröße und Abstand des Musters explizit als eigene Custom Properties definiert und in der statischen Getter-Liste der Painter-Klasse angemeldet werden, sonst liefert properties.get(...) im Worklet einen leeren Wert zurück.
Registriert man diese Custom Properties zusätzlich mit @property, bekommt das Worklet garantiert typisierte, geparste Werte statt roher Strings, und der Browser kann zwischen zwei Werten sogar animieren, etwa den Punktabstand beim Hover sanft zu vergrößern. Ohne Registrierung bleibt jede Custom Property ein reiner Text-Token, den das Worklet selbst parsen muss, was Tippfehler bei Einheiten erst zur Laufzeit sichtbar macht.
@property --dot-gap {
syntax: '<length>';
inherits: false;
initial-value: 16px;
}
.product-card:hover {
--dot-gap: 24px; /* animatable because --dot-gap is typed as <length> */
transition: --dot-gap 0.4s ease;
}
5. Wiederverwendbarkeit: ein Muster für mehrere Komponenten parametrisieren
Der eigentliche Gewinn eines Paint Worklets zeigt sich erst, wenn ein einziges registriertes Muster in einem ganzen Design-System für Karten, Hero-Sektionen und Badges gleichzeitig genutzt wird, jeweils mit anderen Custom-Property-Werten für Farbe und Dichte. Statt fünf separater SVG-Dateien für fünf Farbvarianten pflegt man eine JavaScript-Datei und variiert das Ergebnis komplett über CSS, was die Wartung deutlich vereinfacht, sobald sich die Markenfarbe ändert.
Diese Parametrisierung funktioniert auch über Media Queries und Container Queries hinweg, weil das Worklet bei jeder relevanten Style-Änderung erneut aufgerufen wird. Ein Punktraster kann so auf kleinen Viewports automatisch enger werden, indem --dot-gap innerhalb einer Media Query einfach überschrieben wird, ohne dass eine zweite, für mobile Geräte optimierte Bilddatei ausgeliefert werden muss.
6. Performance-Vergleich: Paint Worklet gegen SVG-Background und Bilddatei
Ein statisches SVG oder PNG als Hintergrund wird einmal vom Browser dekodiert und danach aus dem Bild-Cache gezeichnet, was pro Frame extrem günstig ist, aber jede Größenänderung als Skalierung eines bereits fertigen Bitmaps behandelt. Ein Paint Worklet zeichnet dagegen bei jedem Repaint neu, was für ein einfaches Punktraster auf modernen Geräten kaum messbar ist, bei komplexen, rechenintensiven Mustern mit vielen Pfaden aber echte Repaint-Kosten verursacht, die ein Profiling im Performance-Panel sichtbar macht.
Der Vorteil liegt nicht in der reinen Zeichengeschwindigkeit, sondern darin, dass ein Worklet keine zusätzliche Datei über das Netzwerk lädt und beliebig viele Farbvarianten ohne neuen Request erzeugt, während für dieselbe Flexibilität mit SVG entweder mehrere Dateien oder eine per JavaScript generierte Data-URI nötig wären. Für seltene Repaints, etwa einen statischen Kartenhintergrund, ist ein optimiertes SVG oft weiterhin die günstigere Wahl, für stark parametrisierte, häufig wechselnde Muster gewinnt in der Praxis meist das Worklet.
7. Wann sich ein Paint Worklet nicht lohnt
Für einfache geometrische Muster, die sich mit CSS-Gradienten allein abbilden lassen, etwa Streifen, Schachbrettmuster oder weiche Farbverläufe, ist ein Paint Worklet reiner Overhead: Ein repeating-linear-gradient erreicht dasselbe visuelle Ergebnis ohne zusätzliche JavaScript-Datei, ohne Ladezeit für addModule und mit vollem Support auch in Safari, wo Paint Worklets bis heute fehlen.
Auch wenn ein Muster nur einmal auf der gesamten Seite vorkommt und sich nie ändert, überwiegt der Aufwand für Registrierung und Modul-Ladezeit den Nutzen gegenüber einer einzelnen, sauber optimierten SVG-Datei. Ein Paint Worklet lohnt sich am meisten dort, wo echte Parametrisierbarkeit über viele Komponenten hinweg gebraucht wird, nicht als Ersatz für jedes dekorative Hintergrundbild.
8. Browser-Support und eine robuste Fallback-Strategie
Chrome und Edge unterstützen die CSS Paint API seit mehreren Jahren vollständig, Firefox bietet sie nur hinter einem experimentellen Flag an, und Safari implementiert bislang keinen der Houdini-Paint-Bausteine. Damit ist die API für produktive Projekte klar ein Progressive-Enhancement-Feature: Sie darf niemals die einzige Quelle für einen inhaltlich wichtigen Hintergrund sein, sondern immer eine Verbesserung on top of einem funktionierenden CSS-Fallback.
Die Feature-Detection erfolgt am saubersten über @supports (background-image: paint(x)), weil das die tatsächliche Fähigkeit des Browsers prüft, statt sich auf eine JavaScript-Prüfung von CSS.paintWorklet zu verlassen, die in älteren Browsern selbst schon einen Fehler werfen kann. Innerhalb des @supports-Blocks lädt man dann optional zusätzlich das Worklet-Modul per JavaScript, außerhalb bleibt ein einfacher Gradient oder ein statisches Bild als solide Basis bestehen.
.product-card {
/* Fallback for every browser without CSS Paint API support */
background-image: radial-gradient(circle, #c4b5fd 1.5px, transparent 1.5px);
background-size: 16px 16px;
}
@supports (background-image: paint(dot-pattern)) {
.product-card {
background-image: paint(dot-pattern);
}
}
9. Debugging und Entwicklungsworkflow für Paint Worklets
Fehler innerhalb eines Worklets landen nicht automatisch in der normalen Konsole des Hauptdokuments, weil der Code in einem eigenen, isolierten Worklet-Kontext läuft. In Chrome DevTools lässt sich dieser Kontext über das Dropdown-Menü oben in der Konsole gezielt auswählen ("Paint Worklet"), um Log-Ausgaben und Exceptions aus paint() direkt zu sehen, statt im Dunkeln zu debuggen, warum ein Hintergrund einfach leer bleibt.
Ein typischer Debugging-Fehler ist, eine Custom Property zu ändern, ohne sie in inputProperties aufgelistet zu haben: Das Worklet erhält dann stillschweigend keinen aktualisierten Wert, und es entsteht der Eindruck, CSS-Änderungen würden ignoriert. Wer neue Parameter einführt, sollte deshalb immer zuerst die statische Getter-Liste erweitern und erst danach die zugehörige CSS Custom Property im Stylesheet nutzen.
| Ansatz | Netzwerk-Request | Responsive Schärfe | Parametrisierbarkeit |
|---|---|---|---|
paint() Worklet |
Ein Modul, einmalig pro Seite | Immer scharf, neu berechnet | Beliebig viele Varianten per Custom Property |
| SVG als Background | Eine Datei pro Variante | Skalierbar, aber statisch | Nur über mehrere Dateien oder Data-URI |
| PNG/JPEG als Background | Eine Datei pro Variante | Verliert Schärfe bei Skalierung | Keine, feste Pixelwerte |
| CSS-Gradient | Kein Request | Immer scharf | Über Custom Properties möglich, aber geometrisch begrenzt |
Mironsoft
Modernes CSS, Layout-Architektur und Rendering-Performance
CSS, das wartbar bleibt statt mit jeder Änderung zu brechen?
Wir prüfen bestehende Stylesheets auf Spezifitäts-Chaos und Layout-Thrashing und bauen daraus eine CSS-Architektur mit Cascade Layers, Custom Properties und modernen Layout-Primitiven, die auch nach dem zehnten Feature noch verständlich ist.
CSS-Audit
Spezifität, Cascade-Konflikte und ungenutzte Selektoren systematisch aufdecken.
Architektur-Refactoring
Cascade Layers, Custom Properties und Design Tokens sauber einführen.
Performance-Tuning
Layout-Thrashing, teure Selektoren und Rendering-Engpässe gezielt beheben.
10. Zusammenfassung
CSS Paint Worklets: Das Wichtigste auf einen Blick
Grundprinzip
Ein Paint Worklet registriert eine JavaScript-Klasse mit paint()-Methode, die bei jedem Repaint direkt in den Hintergrund zeichnet.
Werte übergeben
inputProperties listet die Custom Properties, die das Worklet lesen darf. @property macht sie typisiert und animierbar.
Performance
Kein zusätzlicher Netzwerk-Request pro Variante, aber echte Rechenkosten bei jedem Repaint für komplexe Muster.
Fallback-Pflicht
@supports (background-image: paint(x)) prüfen und immer einen CSS-Fallback für Safari und ältere Firefox-Versionen bereithalten.