Tailwind JIT-Mode & Compiler: On-Demand CSS für schnelle Build-Zeiten
AI generated
</>
tw
Tailwind CSS · JIT · Compiler · Performance
Tailwind JIT-Mode & Compiler
On-Demand CSS für Build-Zeiten unter 200 ms

Klassisches Tailwind CSS erzeugte in der Entwicklung Stylesheets mit 3 MB und mehr. Der JIT-Compiler löst dieses Problem fundamental: Er scannt Template-Dateien in Echtzeit, generiert ausschließlich benötigte Klassen on demand und bringt arbiträre Werte wie [clamp(1rem,5vw,3rem)] ohne Konfiguration zum Laufen.

12 Min. Lesezeit JIT · On-Demand · Arbiträre Werte · Content-Scanning · Vite · PostCSS Tailwind CSS v3 · v4 · Magento Hyvä

1. Das Problem vor JIT: Riesige Dev-Bundles

Vor der Einführung des Tailwind JIT-Compilers erzeugte Tailwind CSS in der Entwicklungsumgebung ein vollständiges Stylesheet – alle Kombinationen aus Farbklassen, Spacing-Utilities, Responsive-Varianten und Pseudo-Klassen. Das Ergebnis waren CSS-Dateien zwischen 2 MB und 10 MB, abhängig davon, wie viele Konfigurationsvarianten aktiviert waren. Browser-DevTools wurden bei diesem Umfang träge, CSS-Parsing kostete messbar Zeit beim Laden, und Hot-Module-Replacement dauerte mehrere Sekunden, weil PostCSS das gesamte Stylesheet neu erzeugen musste.

Das Produktions-Build löste dieses Problem mit PurgeCSS oder dem eingebauten Content-Scan von Tailwind v2: Alle Klassen, die nicht im HTML, PHP oder JavaScript vorkamen, wurden aus dem finalen Bundle entfernt. Der Dev-Build blieb aber unberührt – und war damit erheblich langsamer als nötig. Entwickler arbeiteten in der Entwicklung mit einem anderen CSS-Bundle als in der Produktion, was subtile Unterschiede im Layout verursachte, die erst beim Deploy auffielen. Genau dieses Auseinanderlaufen von Dev und Prod wollte das Tailwind JIT-Konzept schließen.

2. Wie der Tailwind JIT-Compiler funktioniert

Der Tailwind JIT-Compiler dreht das klassische Prinzip um: Statt alle möglichen Klassen vorab zu generieren und danach zu purgieren, scannt er alle konfigurierten Template-Dateien beim Start und generiert ausschließlich die tatsächlich verwendeten Klassen. Wenn eine neue Klasse in einer Datei erscheint, triggert das den Watcher, der innerhalb von Millisekunden das CSS für diese einzelne Klasse hinzufügt. Das bedeutet: Das CSS-Bundle enthält zu jedem Zeitpunkt nur das, was gerade wirklich gebraucht wird.

Intern arbeitet der JIT-Compiler mit einem regulären Ausdruck-basierten Scanner, der durch alle Content-Dateien läuft und vollständige Tailwind-Klassennamen extrahiert – einschließlich Modifier wie hover:, sm:, dark: und geschachtelte Varianten wie group-hover:focus:. Für jede gefundene Klasse wird der entsprechende CSS-Block on demand erzeugt. Das macht Tailwind JIT auch in Projekten mit komplexen Variant-Kombinationen blitzschnell: Es werden nur die tatsächlich genutzten Kombination generiert, nicht das kartesische Produkt aller möglichen.


/* tailwind.config.js — JIT-Compiler configuration (Tailwind v3) */
/** @type {import('tailwindcss').Config} */
module.exports = {
  /* content: defines which files the JIT scanner reads */
  content: [
    './src/**/*.{html,js,php,phtml,vue,jsx,tsx}',
    './templates/**/*.phtml',
    /* Safelist: always include these classes even if not found in templates */
    /* Use sparingly — every safelisted class bypasses the JIT scanner */
  ],
  safelist: [
    /* Dynamic classes assembled at runtime (e.g. from CMS data) */
    { pattern: /bg-(red|green|blue)-(100|500|900)/ },
    'sr-only',
  ],
  theme: {
    extend: {
      /* Custom tokens are automatically available as JIT classes */
      colors: {
        brand: {
          50:  '#f0f9ff',
          500: '#0ea5e9',
          900: '#0c4a6e',
        },
      },
      /* Fluid typography token — accessible as text-fluid-h1 */
      fontSize: {
        'fluid-h1': ['clamp(1.75rem, 5vw, 3rem)', { lineHeight: '1.1' }],
      },
    },
  },
  plugins: [],
}

Ein wesentlicher Unterschied zur klassischen Konfiguration: Im JIT-Modus müssen keine Varianten mehr explizit aktiviert werden. In Tailwind v2 musste man in der Konfiguration für jedes Utility angeben, welche Varianten (hover, focus, dark, responsive) generiert werden sollen. Im JIT-Compiler sind alle Varianten automatisch für alle Utilities verfügbar – sie werden nur dann erzeugt, wenn sie tatsächlich in den Templates vorkommen. Das reduziert die Konfigurationskomplexität erheblich und beseitigt eine häufige Fehlerquelle.

3. Arbiträre Werte: Design-Tokens ohne Konfiguration

Eine der mächtigsten Funktionen des Tailwind JIT-Compilers sind arbiträre Werte in eckigen Klammern. Mit der Syntax class-name-[wert] lassen sich exakte CSS-Werte direkt im HTML definieren, ohne sie vorher in der Konfiguration zu registrieren. Das ist besonders wertvoll bei Werten, die aus Design-Specs stammen und nicht auf eine vordefinierte Tailwind-Skala passen: top-[117px], grid-cols-[repeat(auto-fill,minmax(280px,1fr))], bg-[#1a1a2e] oder text-[clamp(1rem,3vw,1.5rem)].

Arbiträre Werte mit dem Tailwind JIT-Compiler erlauben auch CSS-Funktionen und Variablen: w-[var(--sidebar-width)] bindet eine CSS Custom Property ein, bg-[url('/img/hero.webp')] setzt ein Hintergrundbild. Mit der Modifikator-Syntax [&>li]:list-none lassen sich CSS-Selektoren direkt schreiben. Und mit dem Property-Hint wie [padding:0_20px_0_16px] können vollständige CSS-Deklarationen inline definiert werden – eine Brücke zwischen Utility-First und purem CSS, ohne die Tailwind-Struktur zu verlassen.

4. Content-Konfiguration: Was der JIT-Compiler scannt

Die content-Konfiguration ist das Herzstück des Tailwind JIT-Compilers. Sie bestimmt, welche Dateien der Scanner durchsucht, um verwendete Klassen zu finden. Hier liegt auch die häufigste Fehlerquelle: Wenn eine Datei nicht im Content-Pfad liegt, werden die darin verwendeten Tailwind-Klassen nicht in das CSS aufgenommen. Das macht sich in der Entwicklung nicht bemerkbar, wenn die fehlende Klasse zufällig durch eine andere Datei im Scan abgedeckt wird – aber im Produktions-Build kann sie fehlen.

Der JIT-Compiler scannt Dateien nach Mustern, nicht nach CSS-Klassen im engeren Sinne. Er sucht nach zusammenhängenden Strings, die wie Tailwind-Klassen aussehen. Das bedeutet: Dynamisch zusammengesetzte Klassen wie 'bg-' + color werden nicht gefunden, weil der Scanner den JavaScript-Ausdruck nicht auswertet. Für dynamisch generierte Klassen muss man entweder vollständige Klassennamen als Strings hinterlegen oder die Safelist nutzen. Dieses Verhalten ist bewusst: Der Scanner ist ein einfacher Regex-Scanner, kein JavaScript-Interpreter – und genau das macht ihn so schnell.


/* tailwind.config.js — Advanced content configuration */
const path = require('path')

module.exports = {
  content: {
    /* files: array of glob patterns to scan */
    files: [
      './src/**/*.{html,js,ts,jsx,tsx,vue,svelte}',
      './node_modules/@headlessui/vue/dist/**/*.js',
    ],
    /* transform: pre-process file content before scanning */
    transform: {
      /* Strip PHP tags before scanning — so JIT finds classes in phtml templates */
      phtml: (content) => content.replace(/<\?php[^?]*\?>/g, ''),
      /* Extract class names from JSON data files */
      json: (content) => {
        const data = JSON.parse(content)
        return Object.values(data).join(' ')
      },
    },
    /* extract: custom extractor for non-standard class formats */
    extract: {
      /* Alpine.js x-bind:class objects need custom extraction */
      js: (content) => {
        const classRegex = /['"`]([a-z][a-z0-9-:[\]/.]*)/g
        return content.match(classRegex) ?? []
      },
    },
  },
  /* ... rest of config */
}

5. Vite-Integration: HMR in unter 50 ms

Die Kombination aus Tailwind JIT und Vite ist der schnellste verfügbare Frontend-Entwicklungs-Stack für CSS-schwere Projekte. Vite verarbeitet PostCSS-Plugins nativ und bindet Tailwind über den offiziellen @tailwindcss/vite-Plugin ein (ab Tailwind v4) oder über die PostCSS-Konfiguration (Tailwind v3). Bei einer Klassenänderung in einem Template triggert Vite den Tailwind-Scanner nur für die geänderte Datei, generiert das Delta-CSS und injiziert es via HMR in den Browser – ohne Seitenreload. Der Zeitraum zwischen Speichern und sichtbarer Änderung im Browser liegt typischerweise unter 50 ms.

Für Tailwind JIT mit Vite ist die vite.config.ts-Konfiguration minimal. In Tailwind v4 reicht die Plugin-Einbindung, der separate tailwind.config.js entfällt für einfache Projekte, weil Theme-Konfiguration direkt in der CSS-Datei über @theme erfolgt. In v3 braucht man postcss-import und autoprefixer zusätzlich zum Tailwind-PostCSS-Plugin. Beide Varianten profitieren von Vites nativem ES-Module-Graph: Nur Dateien, die sich geändert haben, werden neu verarbeitet – der JIT-Scan läuft inkrementell.


/* vite.config.ts — Tailwind JIT with Vite (v4 approach) */
import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'

export default defineConfig({
  plugins: [
    /* Tailwind v4: plugin handles JIT scanning and CSS generation natively */
    tailwindcss(),
  ],
  css: {
    /* PostCSS is still available for additional plugins */
    postcss: {
      plugins: [
        /* autoprefixer is bundled in Tailwind v4, explicit for v3 */
      ],
    },
  },
  build: {
    /* CSS code splitting: each entry point gets its own minimal CSS chunk */
    cssCodeSplit: true,
    rollupOptions: {
      output: {
        /* Deterministic asset filenames for long-term caching */
        assetFileNames: 'assets/[name]-[hash][extname]',
      },
    },
  },
})

/* styles/main.css — Entry point for Tailwind v4 */
/* @import 'tailwindcss'; */
/* @theme { */
/*   --color-brand-500: #0ea5e9; */
/*   --font-sans: 'Inter', sans-serif; */
/* } */

6. PostCSS-Setup für klassische Build-Pipelines

Nicht jedes Projekt nutzt Vite. In klassischen Webpack-Setups, in Laravel Mix oder in eigenständigen PostCSS-Build-Skripten ist Tailwind JIT über die PostCSS-Konfiguration eingebunden. Das postcss.config.js-File registriert Tailwind als PostCSS-Plugin. Der JIT-Compiler wird dabei automatisch aktiviert – in Tailwind v3 ist JIT die Standardeinstellung, ein separates mode: 'jit' ist nicht mehr nötig. In Tailwind v4 entfällt die PostCSS-Konfiguration für einfache Setups zugunsten des CSS-nativen @import "tailwindcss"-Ansatzes.

Die Watch-Konfiguration ist im PostCSS-Setup entscheidend: Tailwind beobachtet die unter content konfigurierten Dateien über den Betriebssystem-eigenen File-Watcher. Auf Linux-Systemen mit vielen Dateien kann es nötig sein, das Inotify-Limit zu erhöhen (fs.inotify.max_user_watches), weil Tailwind JIT für jede Content-Datei einen Watch-Deskriptor öffnet. In Magento-Projekten mit vielen Phtml-Templates und JavaScript-Dateien ist das ein bekanntes Konfigurationsthema, das mit einem einfachen sysctl-Eintrag gelöst ist.

7. Tailwind JIT in Magento Hyvä

Das Hyvä-Theme für Magento 2 baut vollständig auf Tailwind CSS auf und nutzt den JIT-Compiler für die CSS-Generierung. Der Build-Prozess läuft über ein NPM-Skript im web/tailwind/-Verzeichnis des Themes. Das tailwind.config.js scannt dabei nicht nur die eigenen Template-Dateien, sondern über Hyvä-spezifische Pfadkonfigurationen auch Templates aus Modulen, die das Theme erweitern. Wer eigene Klassen in Phtml-Templates oder Alpine.js-Komponenten verwendet, muss sicherstellen, dass diese Pfade im Content-Array registriert sind.

Ein häufiges Problem in Magento-Hyvä-Projekten mit Tailwind JIT: Klassen, die über PHP dynamisch zusammengesetzt werden, fehlen im Produktions-Bundle. Ein Beispiel ist ein Block, der die CSS-Klasse aus einem CMS-Block-Attribut liest und im Template als 'bg-' . $block->getData('color') ausgibt. Der JIT-Compiler findet diesen String nicht, weil er nur statische Zeichenketten scannt. Die Lösung ist die Safelist oder – besser – das Refactoring des Templates, sodass vollständige Klassennamen als statische Strings im PHP-Code hinterlegt sind. Das ist sauberer und macht den Code transparenter.

8. JIT vs. klassischer Build: Direkter Vergleich

Die Unterschiede zwischen Tailwind JIT und dem klassischen Pre-Build-Ansatz sind in der Praxis erheblich – nicht nur bei der Build-Zeit, sondern auch im täglichen Entwicklungsworkflow.

Aspekt Klassischer Build (v2) Tailwind JIT-Compiler Vorteil JIT
Dev-Bundle-Größe 2–10 MB (alle Klassen) 5–30 KB (nur genutzte) Schnelles Parsing, kein Scroll-Lag in DevTools
Build-Zeit (Dev) 3–8 s pro Änderung 50–200 ms inkrementell Echtzeit-Feedback ohne Wartezeit
Arbiträre Werte Nicht möglich ohne Konfiguration top-[117px] sofort nutzbar Exakte Design-Specs ohne Config-Overhead
Dev vs. Prod Parität Unterschiedliche Bundles Identisch – selber Scan Keine Überraschungen beim Deploy
Varianten-Konfiguration Manuell pro Utility aktivieren Alle automatisch verfügbar Weniger Konfiguration, weniger Fehler

Der entscheidende Vorteil des Tailwind JIT-Compilers liegt nicht nur in der Geschwindigkeit, sondern in der Parität zwischen Entwicklungs- und Produktionsumgebung. Weil beide den gleichen Scanner-Algorithmus auf die gleichen Content-Dateien anwenden, kann in der Produktion keine Klasse fehlen, die in der Entwicklung vorhanden war – solange alle Template-Pfade korrekt konfiguriert sind. Diese Parität beseitigt eine ganze Klasse von Deploy-Bugs, die bei klassischem Tailwind regelmäßig auftraten.

9. Häufige JIT-Probleme und ihre Lösung

Das häufigste Problem mit dem Tailwind JIT-Compiler ist fehlendes CSS im Produktions-Build, das in der Entwicklung vorhanden war. Die Ursache ist fast immer eine Content-Pfad-Konfiguration, die nicht alle Dateien erfasst, in denen Tailwind-Klassen vorkommen. Besonders tückisch: Wenn eine Klasse in einer Datei steht, die im Content-Pfad liegt, und dieselbe Klasse auch in einer Datei ohne Content-Pfad, fällt das in der Entwicklung nicht auf. Erst wenn man prüft, welche Datei die "Quelle" der Klasse ist, entdeckt man die Lücke.

Ein weiteres häufiges Problem: Der JIT-Compiler erkennt Klassen nicht, die zur Laufzeit dynamisch zusammengesetzt werden. Statt className={`text-${size}`} muss man vollständige Klassen als Objekt oder Array hinterlegen: className={ { 'text-sm': size === 'sm', 'text-lg': size === 'lg' } }. Das ist kein Bug des JIT-Compilers, sondern ein fundamentales Designprinzip: Der Scanner ist bewusst ein einfacher String-Matcher, der keine JavaScript-Ausdrücke auswertet. Das macht ihn schnell und deterministisch – aber es erfordert, Klassen als vollständige, statische Strings im Code zu schreiben.


/* Debug JIT: check which classes are being scanned */
/* Run this in the project root to see what JIT finds: */
/* npx tailwindcss --content './src/**/*.html' --dry-run */

/* Common fix: ensure all template paths are covered */
module.exports = {
  content: [
    /* Include node_modules for component libraries */
    './node_modules/@headlessui/**/*.js',
    /* Include PHP templates — note the .phtml extension */
    './src/**/*.phtml',
    /* Include JS files that contain class strings */
    './src/**/*.{js,ts,jsx,tsx}',
    /* Avoid scanning CSS files — leads to false positives */
    /* Do NOT include: './src/**/*.css' */
  ],
  /* Safelist for runtime-assembled classes */
  safelist: [
    /* Pattern-based: include all severity colors */
    { pattern: /^(bg|text|border)-(red|yellow|green)-(100|500|700)$/ },
  ],
}

/* Fix: split dynamic class names into static complete strings */
/* WRONG — JIT cannot find this: */
/* const cls = `bg-${color}-500` */

/* RIGHT — JIT finds these complete class strings: */
/* const colorMap = { red: 'bg-red-500', blue: 'bg-blue-500' } */
/* const cls = colorMap[color] */

10. Zusammenfassung

Der Tailwind JIT-Compiler hat die Art, wie Tailwind CSS in modernen Projekten eingesetzt wird, fundamental verändert. Statt ein riesiges Stylesheet vorab zu generieren und dann zu purgieren, scannt Tailwind JIT die Template-Dateien on demand und generiert ausschließlich das benötigte CSS. Das Ergebnis: Sub-200-ms-Builds in der Entwicklung, Dev-Prod-Parität ohne separate Purge-Konfiguration, und arbiträre Werte ohne Konfigurationsaufwand. Die Varianten-Konfiguration entfällt komplett – alle Modifier sind automatisch verfügbar.

Für die Praxis bedeutet das: Content-Pfade sauber konfigurieren, dynamisch zusammengesetzte Klassen als vollständige Strings schreiben und die Safelist gezielt für Ausnahmen nutzen. In Magento-Hyvä-Projekten ist die Phtml-Pfad-Konfiguration besonders wichtig. Wer diese Punkte beachtet, bekommt mit dem Tailwind JIT-Compiler ein CSS-Build-System, das schneller ist als jede Alternative und im Dev-Modus genauso verhält wie im Produktions-Build.

Tailwind JIT-Compiler — Das Wichtigste auf einen Blick

On-Demand-Generierung

JIT scannt Content-Dateien und generiert nur genutzte Klassen. Dev-Bundle: 5–30 KB statt 10 MB. Build-Zeiten unter 200 ms inkrementell.

Arbiträre Werte

top-[117px], bg-[#1a2e3b], grid-cols-[repeat(auto-fill,minmax(280px,1fr))] — exakte Werte ohne Konfiguration sofort nutzbar.

Content-Konfiguration

Alle Template-Pfade müssen im content-Array registriert sein. Dynamische Klassen müssen als vollständige statische Strings im Code stehen.

Dev-Prod-Parität

Selber Scan-Algorithmus in Dev und Prod. Keine PurgeCSS-Konfiguration nötig. Kein Klassen-Verschwinden mehr beim Produktions-Deploy.