Tailwind ist im Svelte-Ökosystem so verbreitet wie überall sonst — schnelles Stylen, kleine Bundles, ein klares Design-System per Konfig. In Svelte funktioniert es ohne nennenswerte Sondereffekte: Tailwind-Klassen sind globale CSS-Klassen, die das Style-Scoping nicht stören. Dieser Artikel zeigt das Setup, das Zusammenspiel mit dem Scoping und die zwei wichtigsten Patterns für lange Klassenlisten.

Setup mit dem sv-CLI

Wer sein Projekt mit npx sv create my-app neu aufgesetzt hat, konnte Tailwind direkt im Wizard auswählen. Wer es nachrüsten will, lässt den sv add-Befehl die nötigen Schritte erledigen:

bash Tailwind nachrüsten
npx sv add tailwindcss

Das CLI macht drei Dinge:

  1. Es installiert die Pakete (tailwindcss, das Vite-Plugin, ggf. PostCSS).
  2. Es legt eine tailwind.config-Datei an oder ergänzt die vite.config.ts um das Plugin.
  3. Es importiert Tailwind in deiner zentralen CSS-Datei.

Danach kannst du Klassen wie p-4, text-lg, bg-slate-100 direkt in jeder .svelte-Datei verwenden.

Wie Tailwind und Sveltes Scoping zusammenspielen

Sveltes Style-Scoping wirkt nur auf den <style>-Block einer Komponente. Tailwind-Klassen kommen aus einem globalen Stylesheet — der ist vom Scoping unbeeinflusst. Das heißt:

  • Du schreibst class="p-4 bg-slate-100" in jede Komponente, die Klassen funktionieren ganz normal.
  • Es gibt keine Doppelung, keinen Hash, keine Optimierungsstufe — Tailwind wird vom Vite-Plugin verarbeitet und ist beim Browser-Rendern fertig.
  • Eigene Klassen im <style>-Block der Komponente bleiben weiterhin gescoped. Beide Welten existieren parallel.

Eine Beispiel-Komponente:

svelte Card.svelte
<script>
    let { title, children } = $props();
</script>

<article class="rounded-lg border border-slate-200 bg-white p-4 shadow-sm">
    <h2 class="mb-2 text-lg font-semibold text-slate-900">
        {title}
    </h2>
    <div class="text-slate-700 leading-relaxed">
        {@render children()}
    </div>
</article>

Kein <style>-Block, kein Scoping nötig — Tailwind macht die Arbeit.

@apply im scoped Style-Block

Wenn dieselbe Tailwind-Klassen-Kombination an mehreren Stellen vorkommt, muss man sie nicht jedes Mal wiederholen. Innerhalb des scoped <style>-Blocks kannst du sie zu einem benannten Selektor zusammenfassen:

svelte Mit @apply
<article class="card">
    <h2 class="title">{title}</h2>
    <p class="body">{body}</p>
</article>

<style>
    @reference "tailwindcss";

    .card {
        @apply rounded-lg border border-slate-200 bg-white p-4 shadow-sm;
    }

    .title {
        @apply mb-2 text-lg font-semibold text-slate-900;
    }

    .body {
        @apply text-slate-700 leading-relaxed;
    }
</style>

Die .card-, .title-, .body-Klassen sind weiterhin gescoped zur Komponente — du holst dir nur die Tailwind-Werte rein. Das @reference-Statement am Anfang des Style-Blocks ist seit Tailwind v4 nötig, damit der Compiler weiß, dass Tailwind-Direktiven hier verwendet werden.

Das ist die saubere Lösung, wenn dieselben fünf bis zehn Klassen immer zusammen auftauchen.

Klassen-Listen werden zu lang — was tun?

Tailwind ist großartig schnell beim Prototypen. Sobald aber class="..." länger wird als der eigentliche Inhalt, leidet die Lesbarkeit.

svelte Wird unleserlich
<button class="inline-flex items-center justify-center gap-2 rounded-md bg-slate-900 px-4 py-2 text-sm font-semibold text-white transition-colors hover:bg-slate-800 focus:outline-none focus:ring-2 focus:ring-slate-900 focus:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none">
    Speichern
</button>

Dafür gibt es drei Patterns, die in Svelte besonders gut funktionieren.

Pattern 1 – Eigene Komponente

Die simpelste und wirksamste Lösung. Der Button kommt einmal in eine Button.svelte mit Tailwind-Klassen — überall sonst rufst du nur <Button>...</Button> auf.

svelte Button.svelte
<script>
    let { variant = 'primary', children, ...rest } = $props();
</script>

<button
    class={[
        'inline-flex items-center justify-center gap-2 rounded-md px-4 py-2',
        'text-sm font-semibold transition-colors',
        'focus:outline-none focus:ring-2 focus:ring-offset-2',
        'disabled:opacity-50 disabled:pointer-events-none',
        variant === 'primary' && 'bg-slate-900 text-white hover:bg-slate-800',
        variant === 'danger' && 'bg-red-600 text-white hover:bg-red-700',
    ]}
    {...rest}
>
    {@render children()}
</button>

Svelte akzeptiert ein Array als class-Wert: Falsy-Einträge werden übersprungen, der Rest wird zu einem String verbunden. Damit kannst du Klassen sauber in mehrere Zeilen aufteilen, ohne clsx-Library.

Pattern 2 – clsx mit Map-Form

Wer es klassisch gewohnt ist:

svelte Mit clsx
<script>
    import clsx from 'clsx';

    let { variant = 'primary', loading } = $props();
</script>

<button
    class={clsx(
        'btn',
        {
            'btn-primary': variant === 'primary',
            'btn-danger': variant === 'danger',
            'btn-loading': loading,
        },
    )}
>

</button>

Funktioniert ebenfalls — und manchmal liest sich die Map-Form sauberer als ein Array mit &&-Konstrukten.

Pattern 3 – @apply (siehe oben)

Wenn die Logik nicht variantengetrieben ist, sondern einfach eine wiederkehrende Klassen-Komposition, ist @apply im scoped Block die kompakteste Lösung. Drei Zeilen Style sind oft lesbarer als achtzig Klassen im Markup.

Tailwind-Plugins, die in Svelte gut funktionieren

Ein paar Tools aus dem Tailwind-Ökosystem lohnen sich besonders:

  • @tailwindcss/forms — vernünftiger Default-Look für Form-Elemente, ohne Reset-Frust.
  • @tailwindcss/typography — erzeugt eine .prose-Klasse für Lesetexte (Markdown-Output, Blog-Artikel).
  • tailwind-merge — löst Konflikte auf, wenn dieselbe Property doppelt im Klassen-String steht (p-2 p-4p-4 gewinnt). Nützlich beim Weiterreichen von Klassen über Wrapper.
  • class-variance-authority (CVA) — wenn du eine Komponente mit vielen Varianten hast und die Klassen typisiert konfigurieren willst.

Alle vier laufen unverändert in Svelte — sie sind reine Tailwind-/CSS-Tools, kein Framework-spezifischer Code.

Häufige Stolperfallen

Klasse aus einem dynamisch gebildeten String — wird vom JIT nicht erkannt.

svelte Funktioniert nicht zuverlässig
<div class="bg-{color}-500"></div>

Tailwind scannt deine Source-Files nach Klassennamen, um nur die genutzten zu generieren. Eine Klasse, die per String-Konkatenation entsteht, ist im Source nicht als ganzer Klassenname zu sehen — Tailwind kennt sie nicht und purged sie weg. Lösung: Vollständige Klassen-Strings im Source haben oder per Map mappen ({ red: 'bg-red-500', blue: 'bg-blue-500' }[color]).

@apply ohne @reference in Svelte-Style-Block. Seit Tailwind v4 muss in scoped <style>-Blöcken explizit angegeben werden, dass Tailwind-Direktiven verwendet werden. Mit @reference "tailwindcss"; am Anfang.

Tailwind-Klassen mit eigenen Klassen kombinieren — Reihenfolge stimmt nicht.

svelte Tailwind verliert
<button class="my-btn p-2">…</button>

<style>
    .my-btn { padding: 1rem; }
</style>

Die scoped .my-btn-Regel ist spezifischer als die Tailwind-Klasse — sie gewinnt. Wenn du wirklich Tailwind das letzte Wort geben willst, nutze !important oder kehre die Reihenfolge um (Tailwind als gescopte @apply).

Sehr lange Klassenlisten beim Linting/Formatting. Der offizielle Prettier-Plugin für Tailwind sortiert Klassen automatisch in eine kanonische Reihenfolge. Macht Diffs lesbarer und einheitlich.

Dunkle Themes mit Klassen-Schalter. Tailwinds dark:-Variante kann auf [data-theme='dark'] oder .dark gehängt werden — über die Tailwind-Config (darkMode: 'selector'). Passt besser zu Sveltes Theme-Toggle (siehe CSS-Variablen-Artikel).

Weiterführende Ressourcen

Externe Quellen

Verwandte Artikel

/ Weiter

Zurück zu Styling

Zur Übersicht