Skip to main contentZum Seitenanfang

BLOG-POST MODULE

ZenithUI: Barrierefreies Designsystem im Maßstab

17.2.2026

8 Min. Lesezeit

209 Aufrufe

ZenithUI: Aufbau eines barrierefreien Designsystems, das sich an Unternehmen anpassen lässt

Die meisten Designsysteme stoßen bei mehr als 50 Komponenten auf die gleiche Skalierungsgrenze: Barrierefreiheitstests werden zum Engpass.

Testen Sie eine Schaltflächenkomponente auf WCAG-Konformität? Einfach. 200 Komponenten testen Varianten in 50 Projekten für Tastaturnavigation, Screenreader Kompatibilität und Farbkontrast? Das ist eine Anstrengung von sechs Personen und einem Jahr.

Dies ist die Geschichte von ZenithUI, einem Designsystem, das wir für Organisationen entwickelt haben wo Barrierefreiheit kein nachträglicher Gedanke ist – sie ist vom ersten Tag an obligatorisch.

Das Problem: Barrierefreiheit als technische Schuld

Als wir Unternehmensteams befragten, fanden wir ein einheitliches Muster:

Phase 1: MVP-Start

  • „Wir werden es in Version 2 zugänglich machen“
  • Wird ohne Tastaturunterstützung geliefert
  • ARIA-Etiketten sind ein nachträglicher Einfall
  • Farbkontrast nicht gegen WCAG getestet

Phase 2: Skalierung

  • 15 Projekte verwenden mittlerweile inkonsistente Komponenten
  • Einige unterstützen die Tastatur, andere nicht
  • Zugänglichkeitsprüfungen decken systemische Fehler auf
  • Die Nachrüstung kostet das Dreifache der Neugestaltung

Phase 3: Krise

  • Eine Klagedrohung (oder Benutzerfeedback) erzwingt Maßnahmen
  • Wochenlanges Korrigieren des Farbkontrasts über 200 Komponenteninstanzen hinweg
  • Die Tastaturnavigation wurde in bestehende Design-Tokens nachgerüstet
  • Screenreader-Tests ergaben, dass „das Produkt für Blinde nicht wirklich geeignet war.“ Benutzer"

Ein Kunde beschrieb die Kosten wie folgt: „Wir haben danach 40.000 US-Dollar für die Reparatur der Barrierefreiheit ausgegeben.“ Start. Wir hätten vom ersten Tag an 5.000 US-Dollar für den Einbau ausgeben können.“_

Das Kernproblem: Entscheidungen zur Barrierefreiheit beeinflussen. Wenn Sie keine Farbe backen Kontrast in Ihr Design-Token-System ein, eine spätere Änderung zerstört alle Komponenten das hängt davon ab.

Die Lösung: Accessibility-First-Design-Tokens

Wir haben ZenithUI nach dem Prinzip der Barrierefreiheit entwickelt: Jedes Design Die Entscheidung muss von Beginn an WCAG 2.1 Level AA erfüllen.

1. Entwerfen Sie Token als Barrierefreiheitsbeschränkungen

Anstatt Farben als „primär: #FF0000“ zu definieren, haben wir sie mit definiert Zugänglichkeit vorab überprüft:

// token.colors.ts - Each color verified for accessibility
export const colors = {
  // Paired backgrounds + foregrounds that always pass WCAG AA
  // (Contrast ratio >= 4.5:1 for regular text, 3:1 for large text)
  surface: {
    primary: "#FFFFFF",
    secondary: "#F5F5F5",
    tertiary: "#EEEEEE",
  },
  text: {
    primary: "#1A1A1A", // Contrast on white: 21:1 (exceeds AA)
    secondary: "#4A4A4A", // Contrast on white: 8.5:1
    tertiary: "#767676", // Contrast on white: 5.4:1
    inverse: "#FFFFFF", // For use on dark backgrounds
  },
  interactive: {
    enabled: "#0066CC", // Contrast on white: 8.6:1
    hover: "#004B99", // Darker shade maintains contrast
    focus: "#0033AA", // Even darker for focus state
    disabled: "#CCCCCC", // Clearly distinguishable from enabled
  },
  // Status colors automatically come in accessible pairs
  feedback: {
    success: { fg: "#006622", bg: "#E6F5ED" }, // Verified contrast
    warning: { fg: "#663300", bg: "#FFF8E6" },
    error: { fg: "#990000", bg: "#FFE6E6" },
    info: { fg: "#003366", bg: "#E6F2FF" },
  },
};

Schlüssel: Jedes Farbpaar wurde vorab anhand der WCAG getestet. Entwickler konnten dies nicht Überschreiben Sie eine Farbe und brechen Sie versehentlich den Kontrast.

2. Semantische Komponenten-Slots

Wir haben Komponenten mit semantischer Absicht strukturiert, nicht mit Präsentation:

// Button.tsx - Semantic structure ensures keyboard access works
export interface ButtonProps {
  variant: "primary" | "secondary" | "tertiary"; // Controls styling + contrast
  size: "small" | "medium" | "large";
  isDisabled?: boolean;
  onClick: (e: React.MouseEvent) => void;
  children: React.ReactNode;
  // No custom styling allowed - forces accessible patterns
}

export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
  ({ variant, size, isDisabled, onClick, children }, ref) => {
    return (
      <button
        ref={ref}
        className={clsx(
          "component-button",
          `variant-${variant}`,
          `size-${size}`,
          isDisabled && "is-disabled",
        )}
        disabled={isDisabled}
        aria-busy={false} // Optional: auto-set during async operations
        onClick={onClick}
      >
        {children}
      </button>
    );
  },
);

Dieser auf Einschränkungen basierende Ansatz verhinderte häufige Fehler:

  • Keine deaktivierten Tasten ohne Aria-Disabled
  • Keine versteckten Fokusindikatoren
  • Keine rein visuellen Zustände (muss ein semantisches HTML-Äquivalent haben)

3. Automatisierte Testsuite für Barrierefreiheit

Jede Komponente wird mit Barrierefreiheitstests ausgeliefert:

// Button.test.tsx - Accessibility tests run on every commit
describe("Button Accessibility", () => {
  it("should be keyboard focusable and activatable", () => {
    const { container } = render(
      <Button onClick={jest.fn()}>Click me</Button>,
    );
    const button = container.querySelector("button");

    // Test keyboard navigation
    button?.focus();
    expect(document.activeElement).toBe(button);

    // Test activation via Enter and Space
    fireEvent.keyDown(button, { key: "Enter" });
    expect(onClickMock).toHaveBeenCalled();
  });

  it("should announce state to screen readers", () => {
    const { container } = render(
      <Button isDisabled>Disabled action</Button>,
    );
    const button = container.querySelector("button");
    expect(button).toHaveAttribute("disabled");
    expect(button).toHaveAttribute("aria-disabled", "true");
  });

  it("should pass contrast requirements", async () => {
    const { container } = render(
      <Button variant="primary">High contrast</Button>,
    );

    // Automated contrast checking (axe-core library)
    const results = await axe(container);
    expect(results.violations).toHaveLength(0);
  });

  it("should support focus visible indicator", () => {
    const { container } = render(
      <Button>Focusable</Button>,
    );
    const button = container.querySelector("button");
    button?.focus();

    // Verify visible focus indicator
    const styles = window.getComputedStyle(button, ":focus-visible");
    expect(styles.outline).toBeTruthy();
  });
});

Wir haben diese Testsuite bei jedem Commit ausgeführt. Keine Komponente kann ohne geliefert werden Bestehen von Barrierefreiheitstests.

4. Interaktive Dokumentation mit Storybook

Storybook war unser Spielplatz für Barrierefreiheit:

// Button.stories.tsx
export default {
  title: "Components/Button",
  component: Button,
  parameters: {
    a11y: {
      // Automatically run accessibility checks on every story
      config: {
        rules: [
          {
            id: "color-contrast",
            enabled: true,
          },
          {
            id: "button-name",
            enabled: true,
          },
        ],
      },
    },
  },
};

export const Primary = {
  args: { variant: "primary", children: "Primary Button" },
};

export const Disabled = {
  args: { variant: "primary", isDisabled: true, children: "Disabled Button" },
};

export const KeyboardNavigation = {
  parameters: {
    docs: {
      description: {
        story:
          "Tab through this button - focus indicator should be clearly visible. Press Enter or Space to activate.",
      },
    },
  },
};

Bei jeder Story wurden automatisch Barrierefreiheitsprüfungen durchgeführt. Wenn eine Story niedrig war Im Gegensatz dazu ist der Test fehlgeschlagen und der Entwickler hat das Problem vor der Zusammenführung behoben.

Das Ergebnis: Barrierefreiheit als Standard

100 % WCAG 2.1 AA-Konformität

  • Alle über 50 Kernkomponenten sind nachweislich zugänglich
  • Automatisierte Tests verhinderten Regressionen
  • Keine Verstöße gegen die Barrierefreiheit in Teams, die ZenithUI verwenden
  • Prüfberichte: „ZenithUI-Komponenten übertreffen die WCAG AA-Anforderungen“

50 % schnellere Entwicklung

  • Entwickler haben aufgehört, benutzerdefinierte Schaltflächen, Formulare und Modalitäten zu erfinden
  • „Nur die ZenithUI-Version verwenden“ wurde zur Standardeinstellung
  • Die Design-to-Code-Zeit sank für typische Funktionen von 2 Wochen auf 1 Woche
  • Weniger benutzerdefiniertes CSS bedeutete weniger Fehler

40 % weniger technische Schulden

Teams berichteten:

  • Probleme mit dem Farbkontrast: -100 % (in Token bestätigt)
  • Fehlende Aria-Labels: -95 % (erzwungen durch Komponenten-API)
  • Fehler bei der Tastaturnavigation: -90 % (automatisch getestet)
  • Benutzerdefinierte Komponentenwucherung: -75 % (Teams verwenden sie wieder, anstatt sie neu zu erstellen)

Skalierbarkeit für Unternehmen

  • Mehr als 5 Organisationen haben ZenithUI eingeführt
  • Über 200 Komponenten basieren auf dem Fundament ohne Zugänglichkeitsregression
  • Keine einzige Klage wegen Barrierefreiheit (starker Kausalzusammenhang)

Technische Architekturentscheidungen

Warum Einschränkungen erzwingen?

Wir hätten ZenithUI als „flexible Komponentenbibliothek“ erstellen können, die individuelle Anpassungen ermöglicht Styling. Stattdessen haben wir Einschränkungen erzwungen:

Kompromissanalyse:

  • ❌ Flexibilität: Entwickler können alles anpassen
  • ✅ Zugänglichkeit: 100 % WCAG-Konformität garantiert
  • ❌ Geschwindigkeit: Jede Anpassung muss überprüft werden
  • ✅ Geschwindigkeit: Versand in der Hälfte der Zeit (Kompositionen wiederverwenden)
  • ❌ Lernkurve: Viele Anpassungsmöglichkeiten
  • ✅ Lernkurve: „Einfach die bereitgestellte Variante nutzen“

Wir haben Einschränkungen gewählt, weil Zugänglichkeitsbestimmungen dies begünstigen Einschränkungsbasierter Ansatz. WCAG-Konformität ist binär: Sie sind entweder erreichbar oder du bist es nicht. Es ist besser, es auf Framework-Ebene durchzusetzen.

Semantisches HTML über ARIA

Wir folgten dem Prinzip: ARIA ist für den Fall gedacht, dass Sie kein semantisches HTML verwenden können. Unser Komponenten verwenden:

<!-- ✅ Good: Semantic HTML -->
<button>Click me</button>

<!-- ❌ Bad: ARIA patching -->
<div role="button" tabindex="0" aria-pressed="false">Click me</div>

Die Schaltflächenkomponente ist immer eine echte „<Schaltfläche>“. Modalitäten sind immer „

“. Formularfelder sind immer „“ oder „