Modul 5: Styling

Tailwind CSS mit Vue

Utility-first CSS trifft reaktive Templates – die produktivste Kombination für moderne UIs.

Warum Tailwind + Vue?

Tailwind CSS und Vue ergänzen sich perfekt: Tailwind liefert die Utility-Klassen, Vue macht sie mit :class-Bindings dynamisch. Das Ergebnis ist schnelleres Prototyping, konsistentes Design und keine CSS-Dateien mehr, die niemand aufräumt.

Setup mit Vite

In Nuxt 3 ist das Setup dank des offiziellen Vite-Plugins minimal:

Terminal
npm install tailwindcss @tailwindcss/vite
nuxt.config.ts
import tailwindcss from '@tailwindcss/vite'

export default defineNuxtConfig({
  vite: {
    plugins: [
      tailwindcss(),
    ],
  },
})
assets/css/main.css
@import 'tailwindcss';
💡

Tailwind v4

Seit Tailwind v4 brauchst du keine tailwind.config.js mehr. Die Konfiguration passiert direkt in CSS mit @theme. Das Vite-Plugin ersetzt den alten PostCSS-Ansatz.

Tailwind in Templates

Utility-Klassen direkt im Template – keine Kontextwechsel mehr zwischen HTML und CSS:

ProfilKarte.vue
<template>
  <div class="max-w-sm rounded-xl border border-gray-200 bg-white p-6 shadow-sm">
    <img
      :src="avatar"
      :alt="name"
      class="w-16 h-16 rounded-full object-cover ring-2 ring-emerald-400"
    />
    <h3 class="mt-4 text-lg font-semibold text-gray-900">{{ name }}</h3>
    <p class="text-sm text-gray-500 mt-1">{{ rolle }}</p>
    <div class="mt-4 flex gap-2">
      <span
        v-for="skill in skills"
        :key="skill"
        class="px-2 py-1 text-xs font-medium rounded-full bg-emerald-50 text-emerald-700"
      >
        {{ skill }}
      </span>
    </div>
  </div>
</template>

Dynamische Klassen mit :class

Vue bietet drei Syntaxen für dynamische Klassen – alle funktionieren perfekt mit Tailwind:

DynamischeKlassen.vue
<script setup>
import { ref } from 'vue'

const istAktiv = ref(false)
const status = ref('success') // 'success' | 'warning' | 'error'
</script>

<template>
  <!-- 1. Objekt-Syntax: Klasse wird bei true hinzugefügt -->
  <button
    :class="{
      'bg-emerald-500 text-white': istAktiv,
      'bg-gray-100 text-gray-700': !istAktiv,
    }"
    class="px-4 py-2 rounded-lg font-medium transition-colors"
    @click="istAktiv = !istAktiv"
  >
    {{ istAktiv ? 'Aktiv' : 'Inaktiv' }}
  </button>

  <!-- 2. Array-Syntax: Klassen zusammensetzen -->
  <div :class="[
    'p-4 rounded-lg border',
    istAktiv ? 'border-emerald-400' : 'border-gray-200',
  ]">
    Array-Syntax Beispiel
  </div>

  <!-- 3. Computed für komplexe Logik -->
  <span :class="statusKlassen">{{ status }}</span>
</template>

<script setup>
const statusKlassen = computed(() => {
  const basis = 'px-3 py-1 rounded-full text-sm font-medium'
  const farben = {
    success: 'bg-green-100 text-green-800',
    warning: 'bg-amber-100 text-amber-800',
    error: 'bg-red-100 text-red-800',
  }
  return [basis, farben[status.value]]
})
</script>
ℹ️

Statische + dynamische Klassen

Du kannst class und :class am selben Element verwenden. Vue merged beide automatisch. Nutze class für feste Utilities und :class für den dynamischen Teil.

Komponenten extrahieren vs. @apply

Wenn sich Utility-Kombinationen wiederholen, hast du zwei Optionen:

Vergleich
<!-- ✅ BESSER: Vue-Komponente extrahieren -->
<!-- components/UiButton.vue -->
<template>
  <button
    class="px-4 py-2 rounded-lg font-medium transition-colors
           bg-emerald-500 text-white hover:bg-emerald-600
           disabled:opacity-50 disabled:cursor-not-allowed"
  >
    <slot />
  </button>
</template>

<!-- ⚠️ OKAY für nicht-komponentenfähige Elemente: @apply -->
<style>
.prose-content h2 {
  @apply text-2xl font-bold mt-8 mb-4 text-gray-900;
}
</style>
💡

Faustregel

Wenn du Tailwind-Klassen wiederverwenden willst, extrahiere eine Vue-Komponente – nicht eine CSS-Klasse mit @apply. Das ist der Vue-Weg und bietet dir Props, Slots und Logik obendrein.

Dark Mode

Tailwind unterstützt Dark Mode über den dark:-Modifier. Standardmäßig reagiert er auf die .dark-Klasse am <html>-Element:

DarkMode.vue
<template>
  <div class="bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100
              border border-gray-200 dark:border-gray-700
              rounded-xl p-6 transition-colors">
    <h3 class="text-lg font-bold">Dark Mode Karte</h3>
    <p class="text-gray-500 dark:text-gray-400 mt-2">
      Dieser Text passt sich automatisch an.
    </p>
    <button
      class="mt-4 px-4 py-2 rounded-lg
             bg-emerald-500 hover:bg-emerald-600
             dark:bg-emerald-600 dark:hover:bg-emerald-500
             text-white font-medium transition-colors"
    >
      Aktion
    </button>
  </div>
</template>
🛤️

Rails-Vergleich

In Rails musst du Dark Mode manuell mit CSS-Variablen oder Media Queries bauen. Mit Tailwind schreibst du einfach dark:bg-gray-900 und es funktioniert. Kombiniert mit Vue's reaktivem Theme-Toggle hast du Dark Mode in Minuten statt Stunden.

Live-Demo: Dynamische Tailwind-Klassen

Karten-Builder
Interaktiv
emerald

Beispiel-Karte

Diese Karte wird komplett durch dynamische Tailwind-Klassen gesteuert. Ändere die Optionen oben!

:class="['p-5 text-base', 'bg-emerald-500/10 text-emerald-100', 'shadow-lg']"