Neunzehn Buttons, sechs Rottöne

Ich habe letzte Woche gezählt, weil ich einen Fehler im Kontrast beheben sollte und nicht wusste, wo. Ergebnis: 41 Vorkommen eines Buttons im Code, 19 optisch verschiedene Varianten, sechs Rottöne. Drei davon lagen innerhalb eines Hex-Wertes voneinander, was niemand sieht und jeder im Diff sieht.


Die Varianten sind nicht entworfen. Sie sind entstanden. Jede hatte einen Grund, meistens einen Freitag, und jede hat einen Namen, den nur ihr Autor kennt: primary, primaryAlt, primaryAltNeu, cta, ctaKlein. Der Kontrast-Fehler steckte in zwei davon. Die anderen vier waren zufällig in Ordnung, und das ist der eigentliche Befund: vier von sechs Buttons waren korrekt, weil niemand sie angefasst hat.


Was Copy-Paste wirklich kostet

Copy-Paste ist nicht kostenlos, es ist nur später fällig. Ich habe für vier typische Änderungen mitgestoppt, einmal am realen Bestand und einmal an einer Stelle, an der eine Komponente liegt. Die Zahlen sind meine Messung, keine Studie, aber die Größenordnung hält jeder Diskussion stand.


ÄnderungCopy-Paste (41 Stellen)Bibliothek (1 Stelle)Differenz
Fokusring sichtbar machen4,1 h0,3 h3,8 h
Kontrast im Rot korrigieren2,5 h0,2 h2,3 h
Ladezustand ergänzen6,2 h0,7 h5,5 h
Vierte Größe einführen3,0 h0,5 h2,5 h

Vier Änderungen, 14,1 Stunden. Dieselben vier Änderungen an einer gepflegten Komponente: 1,7 Stunden. Dazu kommt der Teil, den keine Tabelle zeigt — die Suche. Ich habe 40 Minuten damit verbracht, herauszufinden, welche der 41 Stellen überhaupt zu dem Button gehören, den ich ändern sollte. Zwei davon waren Kopien in einem alten Ordner, den niemand mehr baut.


Und dann ist da noch der Fehler, der nicht auftaucht. Eine der Kopien hatte kein aria-label, eine andere ein falsches. Beide waren unsichtbar, weil sie nur an einer Stelle im Produkt vorkommen, an der niemand mit Screenreader testet. Copy-Paste erzeugt keine Fehler. Es vervielfältigt sie, und zwar genau die, die man nicht sieht.


Gezählt habe ich mit einer Suche nach dem Komponentennamen, nicht nach dem HTML-Element. Das ist wichtig, weil ein Link wie ein Button aussehen kann und ein Button ein Link sein kann. Der dritte Zähler lautet also: wie viele Stellen tun so, als wären sie dieser Button? Es waren 47. Vier davon in einem Zweig, der seit zwei Jahren nicht mehr gebaut wird.


Symbolbild: KI-generiert für diesen Artikel (Flux).

Eine Bibliothek ist kein Design-System

Der Gegeneinwand kommt immer sofort: „Wir haben doch eine Bibliothek.“ Meistens heißt das: es gibt einen Ordner components/, und darin liegen 60 Dateien, von denen 20 importiert werden. Eine Bibliothek ist eine Zusage, dass es genau eine Umsetzung gibt. Kein Ordner.


// eine Komponente, drei Achsen, keine Variante pro Freitag
type ButtonProps = {
  variant?: "primary" | "secondary" | "ghost";
  size?: "sm" | "md" | "lg";
  loading?: boolean;
  children: React.ReactNode;
} & React.ButtonHTMLAttributes<HTMLButtonElement>;

export function Button({
  variant = "primary",
  size = "md",
  loading = false,
  children,
  ...rest
}: ButtonProps) {
  return (
    <button
      type="button"
      data-variant={variant}
      data-size={size}
      aria-busy={loading || undefined}
      disabled={loading || rest.disabled}
      {...rest}
    >
      {loading ? <span className="sr-only">Wird geladen</span> : null}
      {children}
    </button>
  );
}

Der Trick ist nicht TypeScript. Der Trick ist die geschlossene Liste. Drei Varianten, drei Größen, ein Ladezustand — und wer eine vierte will, muss sie begründen. Genau diese Begründung hat vorher gefehlt, und deshalb gibt es jetzt 19 Varianten und sechs Rottöne.


Was zuerst, was später

Eine Bibliothek von null zu bauen ist ein Quartalsprojekt mit Ausschreibung. Eine Bibliothek aus dem Bestand zu ziehen ist eine Woche. Ich fange immer beim Schlimmsten an, nicht beim Schönsten:


flowchart TD A["Schritt 1: Zaehlen - wie oft, wie viele Varianten?"] --> B["Schritt 2: die drei haeufigsten zusammenfuehren"] B --> C["Button, Input, Dialog"] C --> D["Schritt 3: eine Regel - kein neuer Button ohne Grund"] D --> E["Schritt 4: Lint-Regel gegen den alten Import"] E --> F["Schritt 5: erst dann Tokens, Doku, Storybook"] G["Nie: alles auf einmal in einem Branch"] -.-> A F -.-> G

Schritt vier ist der, den alle auslassen. Ohne eine Regel, die den alten Pfad unmöglich macht, wächst der Bestand schneller nach als du ihn abbaust. Ich habe das zweimal gesehen: eine Woche Aufräumen, drei Wochen Feature-Druck, und danach 47 Vorkommen statt 41. Der Zähler ist die einzige Kennzahl in diesem Bereich, die nicht lügt.


Die Rechnung, die im Sprint nicht steht

Eine Bibliothek kostet am Anfang. Bei mir waren es in einem vergleichbaren Projekt 32 Stunden für die ersten fünf Komponenten, inklusive Doku und einem Test, der die Variantenliste prüft. Das ist ein halber Sprint, den niemand einplant, weil er nichts ausliefert, was man im Demo zeigen kann.


Gegenrechnung: 14,1 Stunden für vier Änderungen, und das war ein ruhiger Monat. In einem Quartal mit einem Redesign kommen zwanzig solcher Änderungen. Dann sind es nicht 32 Stunden gegen 14, sondern 32 Stunden gegen 70 — und die 70 fallen in dem Moment an, in dem der Release schon drei Tage überzogen ist. Genau dann wird geschludert, und genau dann entsteht der siebte Rotton.


Symbolbild: KI-generiert für diesen Artikel (Flux).

Dazu kommt die Wartung, die niemand aufschreibt. 19 Varianten bedeuten 19 Stellen, an denen ein neues aria-Attribut fehlen kann. Bei der letzten Zählung fand ich vier, in denen der Ladezustand nur optisch existierte: Spinner an, Screenreader still, aria-busy nirgends. Auf dem Screenshot sieht das fertig aus. Das ist die gefährlichste Sorte fertig.


Fazit

Copy-Paste ist eine Kreditaufnahme mit unbekanntem Zinssatz. Die erste Kopie kostet 25 Sekunden. Die Ablösung kostet einen halben Sprint und einen Abend, an dem du erklärst, warum der Button jetzt anders aussieht als gestern.


Zähl zuerst. Wenn es weniger als fünf Vorkommen sind, kopier sie und schlaf gut. Wenn es 41 sind, hör auf, Komponenten zu schreiben, und fang an, eine zu bauen. Der Unterschied zwischen einem Design-System und einem Ordner mit 60 Dateien ist eine Regel, die jemand durchsetzt — und meistens ist das ein Lint-Fehler, kein Meeting.


Zähl doch mal nach: Wie viele Button-Varianten liegen bei dir im Ordner, und wie viele davon kannst du noch begründen?