</>
tw
Tailwind CSS · Button-System · Variantenbibliothek
Tailwind CSS Button-System:
Variantenbibliothek systematisch aufbauen

Ein inkonsistentes Button-System ist einer der häufigsten Wartungsprobleme in Tailwind-Projekten. Wer Buttons ad hoc zusammenstellt, baut über Monate hinweg eine Sammlung kaum unterscheidbarer Klassen-Kombinationen auf. Dieses Tutorial zeigt, wie man mit Tailwind CSS eine skalierbare Variantenbibliothek entwirft – von Primary bis Loading-State.

12 Min. Lesezeit Primary · Secondary · Ghost · Danger · Icon · Loading Tailwind CSS v4 · Alpine.js · Hyvä

1. Warum ein Button-System und keine Ad-hoc-Klassen

Das Tailwind CSS Button-System unterscheidet sich grundlegend von einer Sammlung zufällig zusammengestellter Utility-Klassen. In Projekten ohne klare Konvention entstehen über Wochen hinweg Dutzende unterschiedlicher Button-Kombinationen – manche mit rounded-lg, andere mit rounded-md, manche mit px-4, andere mit px-4 py-2. Das Ergebnis ist optische Inkonsistenz, die dem Nutzer signalisiert, dass die Oberfläche nicht durchdacht ist. Ein strukturiertes Tailwind CSS Button-System löst dieses Problem an der Wurzel, indem es klare Varianten definiert, die in jedem Kontext identisch aussehen und verhalten.

Der zweite Vorteil eines Tailwind CSS Button-Systems liegt in der Wartbarkeit. Wenn der Designprozess ein neues Brand-Color-Set einführt, reicht es, die Basisvariante zu ändern – alle Buttons im Projekt aktualisieren sich. Ohne System bedeutet dieselbe Änderung eine Suche durch hunderte Templates. Besonders in Hyvä-Projekten mit zahlreichen phtml-Dateien zahlt sich die Investition in eine durchdachte Variantenbibliothek früh aus. Die folgenden Abschnitte zeigen den Aufbau Schritt für Schritt.

2. Die Basis: Basisklassen für alle Button-Varianten

Jede Variante im Tailwind CSS Button-System teilt einen gemeinsamen Satz von Klassen, der Größe, Schrift, Fokus-Ring und Transition definiert. Diese Basis-Klassen sollten niemals in den Varianten wiederholt werden – das ist der Kern des Systems. In Tailwind v4 arbeitet man dafür mit dem @layer components-Block in der CSS-Datei oder, in HTML-zentrierten Projekten, mit einer Klassen-Konvention, die in der Dokumentation festgehalten wird. Die Basis definiert: inline-flex items-center justify-center gap-2 font-semibold text-sm rounded-xl transition-all duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2.

Warum inline-flex statt flex? Ein Button ist ein Inline-Element – er soll sich in Textfluss und Flex-Layouts gleichermaßen einfügen, ohne einen eigenen Block-Kontext zu erzwingen. gap-2 stellt den Abstand zwischen Icon und Label sicher, ohne dass jeder Button individuell Padding für das Icon definieren muss. Der Fokus-Ring mit focus-visible:ring-2 ist Pflicht für Barrierefreiheit – focus-visible zeigt den Ring nur bei Tastaturnavigation, nicht bei Maus-Klick, was optisch sauber ist. Im Tailwind CSS Button-System wird dieser Basis-Block in einer zentralen buttons.css-Datei gepflegt, die in die Haupt-CSS importiert wird.


/* buttons.css — Central button component definitions */
@layer components {
  /* Base shared by ALL button variants */
  .btn {
    @apply inline-flex items-center justify-center gap-2;
    @apply font-semibold text-sm leading-none;
    @apply rounded-xl transition-all duration-200;
    @apply focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2;
    @apply disabled:opacity-50 disabled:pointer-events-none;
    @apply select-none cursor-pointer;
  }

  /* Size modifiers — combine with any variant */
  .btn-xs  { @apply px-3 py-1.5 text-xs; }
  .btn-sm  { @apply px-4 py-2 text-sm; }
  .btn-md  { @apply px-5 py-2.5 text-sm; }
  .btn-lg  { @apply px-6 py-3 text-base; }
  .btn-xl  { @apply px-8 py-4 text-lg; }
}

3. Primary Button – der Standard-CTA

Der Primary Button ist die wichtigste Variante im Tailwind CSS Button-System. Er repräsentiert die primäre Aktion einer Seite und sollte maximal einmal pro Seitenabschnitt sichtbar sein. In Tailwind definiert man ihn mit einem satten Hintergrund in der Brand-Farbe, weißem Text und einem hover:-State, der entweder die Helligkeit senkt (hover:bg-sky-700) oder die Sättigung erhöht. Wichtig: Der Fokus-Ring sollte in derselben Farbfamilie bleiben – focus-visible:ring-sky-500 – damit er als zusammengehörig wahrgenommen wird.

Das Tailwind CSS Button-System trennt Farb-Intent von Größe. Der Primary Button-Stil kombiniert man immer mit einem Größen-Modifier: btn btn-primary btn-md. Das verhindert, dass die Größe Teil der Varianten-Definition wird und man am Ende .btn-primary-lg und .btn-primary-sm als separate Klassen pflegen muss. Ein häufiger Fehler: active:-States vergessen. Bei Klick auf einen Button erwartet der Nutzer eine Reaktion – active:scale-95 oder active:brightness-95 erzeugt das nötige taktile Feedback.


/* Primary — main call-to-action, use once per section */
.btn-primary {
  @apply bg-sky-600 text-white;
  @apply hover:bg-sky-700 active:bg-sky-800;
  @apply focus-visible:ring-sky-500;
  @apply shadow-sm hover:shadow-md active:scale-95;
}

/* Secondary — supporting action, same weight as primary visually */
.btn-secondary {
  @apply bg-slate-800 text-white;
  @apply hover:bg-slate-700 active:bg-slate-900;
  @apply focus-visible:ring-slate-500;
  @apply shadow-sm hover:shadow-md active:scale-95;
}

4. Secondary und Outline-Variante

Die Secondary-Variante im Tailwind CSS Button-System steht neben dem Primary Button und signalisiert eine gleichwertige, aber weniger bevorzugte Alternative. Sie teilt die visuelle Schwere – beide haben einen gefüllten Hintergrund – aber unterscheidet sich durch Farbe. Wer Secondary mit einem dunklen Slate-Hintergrund definiert und Primary mit der Brand-Farbe, erzeugt eine klare visuelle Hierarchie ohne weitere Gestaltungsarbeit. Der Nutzer versteht intuitiv: der helle (brand) Button ist die empfohlene Aktion.

Die Outline-Variante ist eine dritte Abstufung: sie hat keinen Hintergrund, aber einen sichtbaren Border und Textfarbe in der Brand-Farbe. In Tailwind: border border-sky-600 text-sky-700 hover:bg-sky-50 active:bg-sky-100. Diese Variante eignet sich für tertiäre Aktionen oder für Kontexte, in denen ein gefüllter Button die visuelle Ruhe stört – etwa in Cards oder in Tabellen. Das Tailwind CSS Button-System unterscheidet klar: Primary = Hauptaktion, Secondary = Gleichwertige Alternative, Outline = Tertiäre oder kontextsensitive Aktion. Diese drei Stufen decken 90% aller UI-Szenarien ab.

5. Ghost Button – subtil und kontextsensitiv

Der Ghost Button ist die subtilste Variante des Tailwind CSS Button-Systems. Er hat weder Hintergrund noch Border, sondern zeigt nur Text – häufig mit einem Hover-State, der einen leichten Hintergrund andeutet. In Tailwind: text-slate-700 hover:bg-slate-100 active:bg-slate-200. Ghost Buttons eignen sich für Aktionen, die vorhanden sein müssen, aber nicht im Wettbewerb mit Primary-Aktionen stehen sollen – etwa „Abbrechen", „Mehr anzeigen" oder Navigation-Links, die Button-Semantik brauchen.

Ein häufiger Fehler im Tailwind CSS Button-System: Ghost Buttons auf dunklen Hintergründen zu verwenden. Wer einen Ghost Button auf einem Slate-800-Hintergrund platziert, bekommt kaum sichtbaren Text. Die Lösung: Eine ghost-on-dark-Variante mit text-white hover:bg-white/10 active:bg-white/20. Alternativ hilft das Tailwind-dark:-Prefix, falls das Projekt Dark-Mode-Support hat. In Hyvä-Projekten ohne Dark-Mode-Toggle reicht die explizite Variante, die direkt im Template verwendet wird, wenn der Kontext dunkel ist.

6. Danger Button für destruktive Aktionen

Destruktive Aktionen – Löschen, Zurücksetzen, Konto kündigen – brauchen im Tailwind CSS Button-System eine eigene visuelle Sprache. Der Danger Button kommuniziert durch Farbe (Rot), dass die Aktion nicht rückgängig gemacht werden kann. In Tailwind: bg-red-600 text-white hover:bg-red-700 active:bg-red-800 focus-visible:ring-red-500. Wichtig ist die Platzierung: Danger Buttons sollten nie direkt neben Primary Buttons stehen – der räumliche Abstand schützt vor versehentlichem Klick. Das Tailwind CSS Button-System empfiehlt, Danger Buttons in Dialogen zu platzieren, nicht direkt in der Hauptoberfläche.

Für zweistufige Bestätigung bietet sich ein Pattern aus Alpine.js und dem Tailwind CSS Button-System an: Der erste Klick wechselt den Button-Text und -Zustand, der zweite Klick führt die Aktion aus. Das Signal an den Nutzer ist eindeutig: „Bist du sicher?" ohne modalen Dialog. Der Button wechselt von btn-danger zu einem Zustand mit pulsierendem Ring (ring-2 ring-red-400 animate-pulse), der sofortigen Handlungsdruck vermeidet und Zeit zum Überdenken lässt. Dieser UX-Ansatz ist besonders im E-Commerce relevant, wo Löschen von Bestellpositionen unmittelbar spürbar ist.


<!-- Two-step confirmation pattern with Alpine.js -->
<button
  x-data="{ confirmed: false }"
  x-on:click="
    if (!confirmed) {
      confirmed = true;
      setTimeout(() => confirmed = false, 3000);
    } else {
      $dispatch('delete-item');
    }
  "
  x-bind:class="confirmed
    ? 'btn btn-md bg-red-700 text-white ring-2 ring-red-400 ring-offset-2'
    : 'btn btn-danger btn-md'"
  x-text="confirmed ? 'Wirklich löschen?' : 'Löschen'"
  type="button"
></button>

7. Icon-Only und Icon+Text-Buttons

Icon-Buttons sind ein Sonderfall im Tailwind CSS Button-System: Sie haben gleiche Breite und Höhe und brauchen eine separate Größendefinition. Ein Icon-Only-Button der Größe „md" sollte w-10 h-10 p-0 haben, damit das Icon zentriert sitzt. In Tailwind ist das einfach über eine zusätzliche Modifier-Klasse lösbar: btn-icon-md kombiniert die quadratischen Dimensionen mit dem Flex-Layout der Basis. Wichtig für Barrierefreiheit: Jeder Icon-Button braucht aria-label oder title, da kein sichtbarer Text vorhanden ist.

Icon+Text-Kombinationen nutzen das bereits in der Basis definierte gap-2 und funktionieren ohne zusätzliche Klassen. Das SVG-Icon sitzt links vom Label und erbt durch currentColor die Textfarbe des Buttons – keine separate Farbdefinition nötig. Im Tailwind CSS Button-System empfiehlt sich, Icons grundsätzlich mit w-4 h-4 oder w-5 h-5 zu fixieren, statt sie über text-size zu skalieren. Das verhindert, dass Icon und Text in der Größe auseinanderlaufen, wenn der Font-Size-Modifier wechselt. Alle Icons im System sollten denselben Strich-Stil haben – z.B. alle Stroke-basiert aus Heroicons.

8. Loading-State und disabled-Zustand

Interaktive Buttons brauchen im Tailwind CSS Button-System Loading-States, um dem Nutzer zu signalisieren, dass eine Anfrage im Gange ist. Der einfachste Ansatz: das Button-Label durch einen Spinner ersetzen und den Button gleichzeitig deaktivieren. In Alpine.js ist das ein x-bind:disabled="loading" und ein x-show auf Spinner vs. Label. Der Spinner selbst ist ein SVG-Element mit animate-spin – Tailwind's eingebaute Animation, die keine zusätzliche CSS-Definition braucht. Der Button behält seine Breite durch min-w-[8rem], damit kein Layout-Shift entsteht, wenn der Text durch den Spinner ersetzt wird.

Der disabled-Zustand ist in der Basis des Tailwind CSS Button-Systems bereits durch disabled:opacity-50 disabled:pointer-events-none abgedeckt. Das HTML-Attribut disabled reicht aus – kein manuelles Styling nötig. Wichtig: Für Links, die als Buttons gestylt sind (<a class="btn btn-primary">), greift das disabled-Attribut nicht. Hier muss aria-disabled="true" zusammen mit pointer-events-none opacity-50 als Klasse manuell gesetzt werden. Das Tailwind CSS Button-System definiert dafür eine Hilfsklasse .btn-disabled, die genau diese Kombination enthält.


<!-- Loading state button with Alpine.js -->
<button
  x-data="{ loading: false }"
  x-on:click="
    loading = true;
    fetch('/api/action')
      .then(r => r.json())
      .finally(() => loading = false)
  "
  x-bind:disabled="loading"
  class="btn btn-primary btn-md min-w-[9rem]"
  type="button"
>
  <!-- Spinner shown during loading -->
  <svg
    x-show="loading"
    class="w-4 h-4 animate-spin"
    xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
    aria-hidden="true"
  >
    <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"/>
    <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8v8z"/>
  </svg>
  <!-- Label hidden during loading -->
  <span x-show="!loading">Speichern</span>
  <span x-show="loading">Wird gespeichert…</span>
</button>

9. Button-Varianten im direkten Vergleich

Ein gut aufgebautes Tailwind CSS Button-System macht die Wahl der richtigen Variante für jeden Kontext eindeutig. Die folgende Tabelle zeigt, wann welche Variante zu verwenden ist und welche Tailwind-Klassen sie grundlegend ausmachen.

Variante Klassen (Kern) Einsatz Max. pro Sektion
Primary bg-sky-600 text-white hover:bg-sky-700 Hauptaktion, CTA
Secondary bg-slate-800 text-white hover:bg-slate-700 Alternative Hauptaktion 1–2×
Outline border border-sky-600 text-sky-700 hover:bg-sky-50 Tertiäre Aktion, Cards Unbegrenzt
Ghost text-slate-700 hover:bg-slate-100 Abbrechen, Navigation Unbegrenzt
Danger bg-red-600 text-white hover:bg-red-700 Löschen, destruktive Aktionen In Dialogen

Das Tailwind CSS Button-System ergibt nur dann seinen vollen Nutzen, wenn alle Entwickler im Team dieselbe Tabelle kennen und danach handeln. Ein kurzes Design-Token-Dokument im Projekt-Wiki, das diese Tabelle enthält und auf die buttons.css-Definitionen verweist, verhindert, dass neue Teammitglieder eigene Varianten erfinden. Code-Review-Checklisten können einen Punkt „Button-Variante korrekt?" enthalten, um Abweichungen früh zu erkennen.

Mironsoft

Tailwind CSS, Hyvä Themes und Alpine.js für Magento 2

Skalierbare Tailwind-Komponenten für euer Projekt?

Wir entwerfen vollständige Tailwind CSS Komponentenbibliotheken mit durchgängigem Button-System, einheitlicher Varianten-Architektur und dokumentierten Design-Tokens – direkt für Hyvä-Themes auf Magento 2.

Komponenten-Audit

Bestandsaufnahme inkonsistenter Button-Klassen und Erstellung einer einheitlichen Variantenbibliothek

Design-System

Vollständiges Tailwind CSS Button-System mit Größen, Varianten, Loading-States und Barrierefreiheit

Team-Onboarding

Dokumentation, Code-Review-Checklisten und Workshop für konsistente Komponentennutzung

10. Zusammenfassung

Ein durchdachtes Tailwind CSS Button-System mit klarer Variantenbibliothek reduziert Inkonsistenzen, beschleunigt die Entwicklung und macht Design-Anpassungen wartbar. Die Basis-Klassen definieren gemeinsame Eigenschaften – Größe, Fokus-Ring, Transition – die Varianten ergänzen nur Farbe und Intent. Primary für Hauptaktionen, Secondary für Alternativen, Outline für tertiäre Aktionen, Ghost für kontextsensitive Elemente, Danger für destruktive Operationen. Loading-States und disabled-Zustände sind Pflicht für interaktive Buttons in echten Anwendungen.

Die wichtigste Konvention: Größe und Stil sind getrennte Modifier. btn btn-primary btn-lg ist die richtige Struktur – nicht btn-primary-large. Diese Trennung hält die Anzahl der Klassen in der CSS-Datei linear statt exponentiell. Wer das Tailwind CSS Button-System dokumentiert und im Team kommuniziert, verhindert, dass in drei Monaten wieder ein Dutzend Sondervarianten entstehen, die keiner Konvention folgen.

Tailwind CSS Button-System — Das Wichtigste auf einen Blick

Basisklassen

inline-flex, gap-2, focus-visible:ring-2, disabled:opacity-50 – geteilt von allen Varianten, nie wiederholt.

Variantenprinzip

Stil (Primary, Ghost, Danger) und Größe (xs, sm, md, lg) sind getrennte Modifier. Nie als kombinierte Klassen pflegen.

Interaktivität

Loading-State mit animate-spin und x-bind:disabled. Zweistufige Bestätigung für Danger-Aktionen per Alpine.js.

Barrierefreiheit

focus-visible:ring-2 für Tastaturnavigation. aria-label für Icon-Only-Buttons. aria-disabled für Link-Buttons.

11. FAQ: Tailwind CSS Button-System und Variantenbibliothek

1Wie viele Button-Varianten braucht ein Projekt?
5 Varianten reichen: Primary, Secondary, Outline, Ghost, Danger. Kombiniert mit 4–5 Größen-Modifiern decken sie alle UI-Szenarien ab.
2@apply oder direkte Klassen im HTML?
@apply in einer zentralen buttons.css für Template-reiche Projekte (Hyvä/Magento). In Framework-Komponenten direkte Klassen in Variablen pflegen.
3Button-Wildwuchs im Team verhindern?
Variantendokumentation im Wiki + Code-Review-Checkliste + kurzes Onboarding-Dokument. Wer die Konvention kennt, erfindet keine neuen Varianten.
4Warum focus-visible statt focus?
focus-visible zeigt den Ring nur bei Tastaturnavigation – korrekte Barrierefreiheits-Konvention. focus zeigt ihn bei jedem Fokus, auch per Mausklick.
5Loading-State ohne JS-Framework?
aria-busy='true' mit CSS [aria-busy='true'] button { animate } funktioniert ohne Framework. In Hyvä mit Alpine.js x-bind:disabled + x-show auf Spinner/Label.
6Beste Icon-Bibliothek für Tailwind Buttons?
Heroicons (Tailwind Labs) passen am besten – SVG-basiert, currentColor, einheitlicher Stroke-Stil. Phosphor Icons als Alternative mit breiterer Auswahl.
7aria-label für Buttons mit Text?
Nein. aria-label nur für Icon-Only-Buttons ohne sichtbaren Text. Bei Buttons mit Label ist der Buttontext automatisch der zugängliche Name.
8Dark-Mode-Varianten im Button-System?
dark:-Prefix in Tailwind: dark:bg-sky-500 dark:hover:bg-sky-400. Oder separate btn-ghost-on-dark Variante für explizite dunkle Kontexte ohne Dark-Mode-Toggle.
9active:scale-95 oder active:opacity-80?
scale-95 gibt taktiles Drück-Feedback, das dem nativen Mobile-Pattern entspricht. Beide funktionieren – scale-95 ist die modernere und intuitivere Konvention.
10Button-Klassen auf <a>-Elemente?
Ja, aber disabled-Attribut wirkt nicht. aria-disabled='true' + pointer-events-none opacity-50 manuell setzen. Semantisch: Navigationsziele → a, Aktionen → button.