</>
tw
Tailwind CSS · Safelist · Build-Optimierung · JIT
Tailwind CSS Safelist:
Dynamische Klassen korrekt absichern

Tailwind entfernt beim Build jede Klasse, die es im Quellcode nicht als vollständigen String findet. Dynamisch zusammengesetzte Klassennamen verschwinden spurlos – die Safelist ist die chirurgisch präzise Lösung, die genau die richtigen Klassen im Bundle behält.

12 Min. Lesezeit Safelist · Regex-Patterns · Varianten · tailwind.config.js Tailwind CSS v3 · v4 · Vite · PostCSS

1. Das Purge-Problem: Warum dynamische Klassen verschwinden

Tailwind CSS ist auf einen schlanken CSS-Output ausgelegt. Im Produktionsbuild scannt das Framework alle konfigurierten Quelldateien nach Tailwind-Klassen und entfernt alle Klassen, die es dort nicht als vollständigen Klassenstring findet. Dieses Verfahren – intern als Content-Scanning bezeichnet – reduziert typische Tailwind-CSS-Dateien von mehreren Megabyte auf wenige Kilobyte. Was dabei übersehen wird, sind dynamisch zusammengesetzte Klassen, bei denen der vollständige Klassenname zur Laufzeit erst zusammengebaut wird.

Ein typisches Beispiel: In einem Vue- oder React-Komponenten wird eine Farbe aus einer Prop oder aus einem API-Response gelesen und zu einem Tailwind-Klassenstring zusammengesetzt – etwa 'bg-' + color + '-500'. Zur Compile-Zeit sieht Tailwind nur den String 'bg-' und '-500', aber nie die vollständige Klasse bg-red-500 oder bg-blue-500. Das Ergebnis: Im Produktionsbuild fehlen genau die Klassen, die zur Laufzeit gebraucht werden. Die Komponente funktioniert in der Entwicklungsumgebung mit JIT-Watch problemlos, im Deploy nicht. Die Tailwind CSS Safelist ist die präzise Lösung für dieses Problem.

2. Wie Tailwind CSS den Quellcode analysiert

Tailwind analysiert Quelldateien mit einem einfachen, aber effektiven Mechanismus: Es sucht nach vollständigen Klassenstrings, die einem Tailwind-Klassenformat entsprechen. Der Scanner ist kein echter HTML- oder JavaScript-Parser. Er zerlegt den Quelltext in Token – Zeichenketten, die durch Whitespace oder Sonderzeichen begrenzt sind – und prüft jeden Token gegen die bekannten Tailwind-Klassen. Das funktioniert gut für statische Klassen in HTML-Attributen, Klassennamen in Strings und Template-Literale, die vollständige Klassen enthalten.

Was der Scanner nicht leisten kann: Er folgt keiner Variablenzuweisung. const cls = prefix + '-red-500' erzeugt nie einen vollständigen Match für bg-red-500. Dasselbe gilt für Klassen, die aus JSON-Konfigurationsdateien kommen, aus einer Datenbank geladen werden oder von einem CMS-Backend generiert werden. In allen diesen Szenarien ist die Tailwind CSS Safelist das richtige Werkzeug – sie registriert Klassen explizit, ohne dass sie im Quellcode als vollständiger String vorhanden sein müssen.

3. Die Safelist: Grundkonfiguration und einfache Einträge

Die Safelist wird in der tailwind.config.js im Root-Objekt konfiguriert. Sie nimmt ein Array entgegen, das aus einfachen Strings (einzelne Klassen) oder aus Objekten mit Regex-Pattern und optionalen Varianten besteht. Einfache String-Einträge sind die direkteste Form: Jeder String in der Safelist wird unabhängig vom Content-Scanning immer in den Build eingeschlossen. Das ist die richtige Wahl, wenn die Menge der dynamischen Klassen überschaubar und stabil ist.

Einfache Safelist-Einträge haben einen klaren Nachteil: Sie skalieren schlecht. Wer alle Farb-Utilities für alle Hintergrundfarben absichern will, müsste hunderte von Strings einpflegen. Hier kommen Regex-Patterns ins Spiel. Bevor auf Regex umgestiegen wird, lohnt es sich aber, zu prüfen, ob die dynamischen Klassen nicht doch als vollständige Strings in einer Lookup-Tabelle im Quellcode stehen können – das ist die sauberste Lösung und erfordert keine Safelist.


// tailwind.config.js — Basic safelist configuration
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    './src/**/*.{html,js,ts,vue,jsx,tsx}',
    './resources/views/**/*.blade.php',
  ],
  safelist: [
    // Simple string entries — always included regardless of content scan
    'bg-red-500',
    'bg-blue-500',
    'bg-green-500',
    'text-white',
    'font-bold',

    // Useful for status indicators from API responses
    'border-red-400',
    'border-yellow-400',
    'border-green-400',

    // Complete animation classes often missed by scanner
    'animate-spin',
    'animate-pulse',
    'animate-bounce',
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}

Eine häufige Falle beim Einstieg in die Tailwind CSS Safelist: Klassen mit Slash-Syntax für Opacity werden anders geparst. bg-black/50 ist ein valider Tailwind-Klassenstring, muss aber als einzelner String in der Safelist stehen. Genauso verhält es sich mit beliebigen Werten in eckigen Klammern – bg-[#1a1a1a] ist eine separate Klasse, die nicht durch ein einfaches Regex-Pattern abgedeckt wird. Für solche Sonderfälle ist immer ein expliziter Stringeintrag die sicherste Lösung.

4. Safelist mit Regex-Patterns: Ganze Klassen-Familien absichern

Wenn eine ganze Familie von Tailwind-Klassen dynamisch verwendet wird – etwa alle Hintergrundfarben in allen Stufen –, ist ein Regex-Pattern in der Safelist die effiziente Lösung. Das Pattern-Format ist ein Objekt mit dem Schlüssel pattern, der einen regulären Ausdruck enthält. Tailwind prüft jede bekannte Utility-Klasse gegen dieses Pattern und schließt alle Treffer in den Build ein. Das Pattern trifft auf den vollständigen Klassennamen, nicht auf Substrings.

Regex-Patterns in der Tailwind CSS Safelist müssen sorgfältig formuliert werden. Ein zu breites Pattern wie /bg-/ würde alle Klassen einschließen, die die Zeichenkette "bg-" enthalten – also bg-transparent, bg-inherit, bg-current und alle Farbklassen. Das erzeugt unnötig große CSS-Dateien. Ein präzises Pattern wie /^bg-(red|blue|green|yellow|purple)-(100|200|300|400|500|600|700|800|900)$/ schließt genau die benötigten Klassen ein und nichts weiter. Die Kunst liegt in der Balance zwischen Präzision und Vollständigkeit.


// tailwind.config.js — Regex patterns in the safelist
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: ['./src/**/*.{html,js,ts,vue}'],
  safelist: [
    // All background colors for a dynamic color picker component
    {
      pattern: /^bg-(slate|gray|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-(100|200|300|400|500|600|700|800|900)$/,
    },
    // Text colors matching the same palette
    {
      pattern: /^text-(slate|gray|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-(100|200|300|400|500|600|700|800|900)$/,
    },
    // Border colors for status badges from API
    {
      pattern: /^border-(red|yellow|green|blue)-(300|400|500)$/,
    },
    // Ring colors for focus states driven by a theme config
    {
      pattern: /^ring-(sky|indigo|violet)-(400|500|600)$/,
    },
    // Grid column spans for a dynamic layout engine
    {
      pattern: /^col-span-(1|2|3|4|5|6|7|8|9|10|11|12)$/,
    },
  ],
  theme: { extend: {} },
  plugins: [],
}

5. Varianten in der Safelist: Responsive und State-Klassen

Regex-Patterns in der Safelist decken standardmäßig nur die Basisklasse ab – ohne responsive Präfixe wie sm:, md:, lg: und ohne State-Varianten wie hover:, focus: oder dark:. Wenn dynamische Klassen auch mit diesen Varianten verwendet werden, müssen die Varianten explizit im Pattern-Objekt unter dem Schlüssel variants als Array angegeben werden. Tailwind generiert dann die Kombination aus allen Treffern des Patterns und allen angegebenen Varianten.

Das Varianten-Feature der Tailwind CSS Safelist hat einen wichtigen Nebeneffekt auf die Bundle-Größe: Eine Kombination aus einem Pattern, das 50 Klassen trifft, und vier Varianten erzeugt 200 CSS-Regeln. Bei einem Pattern, das alle Farben in allen Stufen trifft und mit responsive und hover abgesichert wird, können schnell tausende von Klassen entstehen. Es ist deshalb wichtig, Varianten nur für die Kombinationen anzugeben, die tatsächlich dynamisch gebraucht werden, und das Ergebnis mit npx tailwindcss --dry-run oder einer Bundle-Analyse zu prüfen.


// tailwind.config.js — Variants in safelist patterns
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: ['./src/**/*.{html,js,ts,vue}'],
  safelist: [
    // Background colors with hover and focus variants (e.g. dynamic button colors)
    {
      pattern: /^bg-(sky|indigo|violet|rose)-(400|500|600)$/,
      variants: ['hover', 'focus', 'active'],
    },
    // Text colors with dark mode variant
    {
      pattern: /^text-(slate|gray)-(600|700|800|900)$/,
      variants: ['dark'],
    },
    // Responsive grid columns — different column count per breakpoint
    {
      pattern: /^grid-cols-(1|2|3|4|6|12)$/,
      variants: ['sm', 'md', 'lg', 'xl'],
    },
    // Opacity for dynamically shown/hidden elements
    {
      pattern: /^opacity-(0|25|50|75|100)$/,
      variants: ['group-hover', 'hover'],
    },
  ],
  theme: { extend: {} },
  plugins: [],
}

6. Praxisfall: CMS-Backends und serverseitig generierte Klassen

Ein klassischer Anwendungsfall für die Tailwind CSS Safelist ist das CMS-Backend: Redakteure können in einem WYSIWYG-Editor oder einem Feldkonfigurationsdialog Farben, Abstände oder Layouts wählen. Diese Auswahl wird als Datenbankwert gespeichert und zur Laufzeit in PHP, Twig oder Blade als Tailwind-Klassenstring ausgegeben. Die möglichen Klassenkombinationen sind bekannt, aber in keiner statischen Quelldatei vorhanden, die Tailwind scannen könnte.

In Magento 2 mit Hyvä Themes tritt dasselbe Muster bei Produkt-Attributen auf: Eine Farbfamilie wird als Attributwert gespeichert, und das Template setzt daraus einen Tailwind-Klassen-String zusammen. Ohne Safelist funktioniert die Darstellung in der Entwicklungsumgebung mit Watch-Modus korrekt, weil JIT dort jeden Klassenstring on-demand generiert. Im Produktionsbuild fehlen die Klassen. Die beste Lösung in solchen Szenarien: Eine separate TypeScript- oder JavaScript-Datei anlegen, die alle möglichen Klassen als vollständige Strings in einem Objekt oder Array enthält, und diese Datei in den content-Paths von Tailwind einschließen. Das ist sauberer als eine Safelist, weil es die möglichen Klassen dokumentiert und typsicher macht.

7. Tailwind CSS v4: Safelist im CSS-first-Ansatz

Tailwind CSS v4 wechselt von der JavaScript-Konfiguration in tailwind.config.js zu einem CSS-first-Ansatz: Die Konfiguration findet in der CSS-Hauptdatei mit @theme-Blöcken statt. Die Safelist ist in diesem neuen Ansatz über eine @source-Direktive kombiniert mit einem unsafe-Inline-Pattern lösbar. Die grundlegende Mechanik bleibt dieselbe – Tailwind muss explizit mitgeteilt werden, welche Klassen es nicht aus Quelldateien inferieren kann, sondern immer generieren soll.

In v4 gibt es die Direktive @source inline("..."), die einen String oder ein Glob-Pattern direkt als Quelle behandelt. Wer alle roten Hintergrundklassen absichern will, schreibt @source inline("{bg-red-100,bg-red-200,...,bg-red-900}"). Das fühlt sich anders an als die JavaScript-Konfiguration, erreicht aber dasselbe Ziel. Für Projekte, die noch auf Tailwind CSS v3 basieren, ist die tailwind.config.js-Safelist mit Regex-Patterns weiterhin der empfohlene Weg. Der Wechsel auf v4 sollte nicht allein wegen der Safelist-Syntax stattfinden – die Migration hat deutlich größere Auswirkungen auf das Gesamtsetup.

8. Alternativen zur Safelist: Wann Content-Paths besser sind

Die Tailwind CSS Safelist ist nicht immer die beste Lösung. Wenn die dynamischen Klassen aus einer definierten Menge stammen, die in einer Konfigurationsdatei oder einem Mapping-Objekt im Quellcode steht, ist es besser, diese Datei in die content-Konfiguration aufzunehmen. Tailwind scannt dann die Datei und findet alle vollständigen Klassenstrings automatisch, ohne dass eine Safelist gepflegt werden muss.

Das Prinzip: Eine zentrale Datei src/config/tailwind-classes.ts enthält ein Objekt, das alle möglichen dynamischen Klassen als vollständige Strings auflistet. Diese Datei wird in content eingetragen. Tailwind scannt sie, findet alle Klassen und behält sie im Build. Der Vorteil gegenüber der Safelist: Die Klassen sind dokumentiert, typsicher und refaktorierbar. Ein Nachteil: Bei sehr großen Mengen oder bei extern definierten Klassen (CMS, Datenbank) ist diese Strategie nicht praktikabel – dort bleibt die Safelist das richtige Werkzeug.

9. Safelist-Strategien im Vergleich

Die Wahl der richtigen Strategie für dynamische Tailwind-Klassen hängt vom Anwendungsfall ab. Die folgende Tabelle zeigt die wichtigsten Optionen und wann sie jeweils passen.

Strategie Funktioniert bei Tailwind CSS Safelist nötig? Empfehlung
Vollständige Strings im Quellcode Lookup-Tabellen, Mapping-Objekte Nein Beste Lösung, immer bevorzugen
Content-Path auf Konfig-Datei Klassen in separater TS/JS-Datei Nein Sauber, dokumentiert, typsicher
Safelist mit Strings Wenige, stabile dynamische Klassen Ja Gut für kleine, bekannte Mengen
Safelist mit Regex-Pattern Ganze Klassen-Familien Ja Effizient, Bundle-Größe beachten
Safelist + Varianten Responsive/State für dyn. Klassen Ja, mit Bedacht Bundle-Explosion vermeiden

Die Tailwind CSS Safelist ist kein Sicherheitsnetz, das pauschal aktiviert werden sollte. Sie löst ein spezifisches Problem und sollte so präzise wie möglich formuliert sein. Ein zu breites Pattern hat direkte Auswirkungen auf die CSS-Bundle-Größe und damit auf die Ladezeit der Website. Der Idealzustand ist eine Codebasis, die so strukturiert ist, dass vollständige Klassenstrings immer im statisch analysierbaren Quellcode vorhanden sind – dann braucht man keine Safelist.

Mironsoft

Tailwind CSS Setup, Build-Optimierung und Frontend-Architektur

Tailwind-Build mit dynamischen Klassen optimieren?

Wir analysieren euren Tailwind-Build, identifizieren fehlende Klassen und konfigurieren eine präzise Safelist-Strategie – ohne unnötige Bundle-Aufblähung und mit sauberer Dokumentation der dynamischen Klassen.

Build-Analyse

Fehlende Klassen im Produktionsbuild identifizieren und Ursachen dokumentieren

Safelist-Konfiguration

Präzise Regex-Patterns und Varianten für alle dynamischen Klassen

Bundle-Optimierung

CSS-Output minimieren und auf tatsächlich genutzte Klassen beschränken

10. Zusammenfassung

Die Tailwind CSS Safelist löst ein spezifisches und häufig auftretendes Problem: Klassen, die erst zur Laufzeit zusammengesetzt werden, fehlen im Produktionsbuild, weil der Content-Scanner sie nicht als vollständige Strings findet. Die Safelist registriert diese Klassen explizit – entweder als einzelne Strings für kleine, stabile Mengen oder als Regex-Patterns für ganze Klassen-Familien. Varianten im Pattern-Objekt sichern responsive und State-Varianten ab, müssen aber mit Bedacht eingesetzt werden, um die Bundle-Größe nicht unnötig zu steigern.

Die beste Strategie für dynamische Tailwind-Klassen ist immer, vollständige Klassenstrings im statisch analysierbaren Quellcode zu haben – in einer Lookup-Tabelle, einem Mapping-Objekt oder einer Konfigurations-Datei, die im Content-Path von Tailwind eingetragen ist. Wenn das nicht möglich ist – bei CMS-generierten Klassen, Datenbankwerten oder extern konfigurierten Layouts –, ist die Safelist das präzise Werkzeug. Tailwind CSS v4 löst dasselbe Problem mit @source inline()-Direktiven im CSS-first-Ansatz. Die Kernregel bleibt in beiden Versionen gleich: Niemals Klassennamen zur Laufzeit aus Teilstrings zusammensetzen, ohne den vollständigen Klassenstring irgendwo statisch verfügbar zu machen.

Tailwind CSS Safelist — Das Wichtigste auf einen Blick

Ursache des Problems

Dynamisch zusammengesetzte Klassen ('bg-' + color) werden vom Content-Scanner nicht als vollständige Tailwind-Klassen erkannt und im Build entfernt.

Safelist-Grundform

In tailwind.config.js: safelist: ['bg-red-500', ...] für einzelne Klassen oder { pattern: /regex/, variants: ['hover'] } für Familien.

Beste Alternative

Vollständige Klassenstrings in einer statischen Lookup-Datei ablegen und deren Pfad in content eintragen — sauberer als Safelist.

Tailwind v4

@source inline("{bg-red-100,...}") in der CSS-Hauptdatei ist das Äquivalent zur JavaScript-Safelist im CSS-first-Ansatz von v4.

11. FAQ: Tailwind CSS Safelist und dynamische Klassen

1Warum fehlen dynamische Klassen im Produktionsbuild?
Tailwind findet dynamisch zusammengesetzte Klassen (z. B. 'bg-' + color) nicht als vollständige Strings im Quellcode und entfernt sie. Safelist oder Lookup-Tabelle lösen das Problem.
2Safelist vs. Content-Paths: Was ist der Unterschied?
Content-Paths lassen Tailwind Dateien scannen und vollständige Strings finden. Die Safelist registriert Klassen explizit, unabhängig vom Scan. Lookup-Datei in Content bevorzugen.
3Regex in der Safelist: Wie formuliere ich ein präzises Pattern?
/^bg-(red|blue|green)-(400|500|600)$/ trifft genau die genannten Klassen. Ohne Anker (^ und $) werden Substrings anderer Klassen gematcht – immer Anker setzen.
4Hover- und responsive Varianten in der Safelist?
{ pattern: /.../, variants: ['hover', 'sm', 'lg'] } erzeugt alle Kombinationen. Varianten nur angeben, wenn sie tatsächlich dynamisch gebraucht werden – jede Variante multipliziert die Klassen-Anzahl.
5Bundle-Größe bei breiter Safelist?
200 Klassen × 4 Varianten = 800 CSS-Regeln. Mit gzip/brotli überschaubar, aber immer das präziseste Pattern wählen. npx tailwindcss -o out.css zum Prüfen nutzen.
6Safelist in Tailwind CSS v4?
In v4: @source inline("{bg-red-100,...}") in der CSS-Datei. Kein tailwind.config.js mehr – der CSS-first-Ansatz übernimmt die Konfiguration vollständig.
7In Entwicklung funktioniert es, im Build nicht – warum?
Der JIT-Watch-Modus generiert Klassen on-demand. Der Produktionsbuild scannt statisch und entfernt nicht gefundene Klassen. Die Entwicklungsumgebung verbirgt das Problem.
8Lookup-Tabelle statt Safelist – wie implementieren?
Datei src/config/tw-classes.ts mit vollständigen Klassenstrings anlegen, in content: ['./src/config/tw-classes.ts'] einträgen. Tailwind scannt und behält alle Strings.
9CMS-generierte Klassen absichern?
Wenn Klassen aus der Datenbank kommen, ist die Safelist oft die einzige Option. Ein Regex-Pattern, das alle möglichen CMS-Klassen abdeckt, und regelmäßige Prüfung bei neuen Klassen.
10Welche Klassen sind nach dem Build vorhanden?
npx tailwindcss -o output.css --minify ausführen, dann grep 'bg-red-500' output.css. Fehlt die Klasse, ist die Safelist oder Lookup-Tabelle noch nicht korrekt konfiguriert.