Labels und Zuordnungen

Schlechtes Beispiel

Anmeldung

Angemeldet bleiben
<!-- ❌ Schlecht: Fehlende Labels -->
<form>
  <h4>Anmeldung</h4>

  <!-- Kein zugeordnetes Label -->
  <input
    type="email"
    placeholder="Ihre E-Mail">

  <!-- Kein zugeordnetes Label, keine Autovervollständigung -->
  <input
    type="password"
    placeholder="Passwort">

  <!-- Kontrollkästchen nicht zugeordnet -->
  <input type="checkbox">
  <span>Angemeldet bleiben</span>

  <button type="submit">Anmelden</button>
</form>

Gutes Beispiel

Anmeldung

Wir werden Ihre E-Mail niemals weitergeben
Mindestens 8 Zeichen mit Großbuchstaben und Zahlen
<!-- ✅ Gut: Explizite und zugeordnete Labels -->
<form>
  <h4>Anmeldung</h4>

  <!-- Label über for/id zugeordnet -->
  <label for="email-good">E-Mail-Adresse</label>
  <input
    type="email"
    id="email-good"
    placeholder="beispiel@domain.de"
    required
    aria-describedby="email-help"
    autocomplete="email">
  <div id="email-help">
    Wir werden Ihre E-Mail niemals weitergeben
  </div>

  <!-- Label mit Anweisungen -->
  <label for="password-good">Passwort</label>
  <input
    type="password"
    id="password-good"
    required
    aria-describedby="password-help"
    autocomplete="current-password">
  <div id="password-help">
    Mindestens 8 Zeichen mit Großbuchstaben und Zahlen
  </div>

  <!-- Kontrollkästchen korrekt zugeordnet -->
  <input type="checkbox" id="remember-good">
  <label for="remember-good">Angemeldet bleiben</label>

  <button type="submit">Anmelden</button>
</form>

Erklärung: Jedes Formularfeld muss ein explizites und korrekt zugeordnetes Label haben. Platzhalter ersetzen keine Labels.

Validierung und Fehlermeldungen

Schlechtes Beispiel

Newsletter-Anmeldung

<!-- ❌ Schlecht: Schlecht angesagte Fehler -->
<form @submit.prevent="submitBadForm">
  <h4>Newsletter-Anmeldung</h4>

  <!-- Keine Label-Input-Zuordnung -->
  <label>Name</label>
  <input
    type="text"
    v-model="badForm.name"
    :class="{ 'error': submitted && !badForm.name }">

  <!-- Nur visueller Fehler -->
  <label>E-Mail</label>
  <input
    type="email"
    v-model="badForm.email"
    :class="{ 'error': submitted && !isValidEmail(badForm.email) }">

  <!-- Keine Fehlerinformationen -->
  <label>Alter</label>
  <input
    type="number"
    v-model="badForm.age"
    :class="{ 'error': submitted && badForm.age < 16 }">

  <!-- Generische und nutzlose Fehlermeldung -->
  <div v-if="submitted && badFormErrors.length">
    ❌ Bitte korrigieren Sie die Fehler
  </div>

  <button type="submit">Anmelden</button>
</form>

Gutes Beispiel

Newsletter-Anmeldung

Sie müssen mindestens 16 Jahre alt sein
<!-- ✅ Gut: Klar zugeordnete Fehler -->
<form @submit.prevent="submitGoodForm" novalidate>
  <h4>Newsletter-Anmeldung</h4>

  <!-- Fehlerzusammenfassung mit Navigation -->
  <div
    v-if="submitted && goodFormErrors.length"
    role="alert"
    aria-labelledby="error-summary-title">
    <h5 id="error-summary-title">Zu korrigierende Fehler:</h5>
    <ul>
      <li v-for="error in goodFormErrors" :key="error.field">
        <a :href="`#${error.field}-good-form`">
          
        </a>
      </li>
    </ul>
  </div>

  <!-- Feld mit spezifischem zugeordnetem Fehler -->
  <label for="name-good-form">Vollständiger Name *</label>
  <input
    type="text"
    id="name-good-form"
    v-model="goodForm.name"
    required
    aria-invalid="submitted && !goodForm.name"
    aria-describedby="name-error">
  <div
    v-if="submitted && !goodForm.name"
    id="name-error"
    role="alert">
    ❌ Name ist erforderlich
  </div>

  <button type="submit">Anmelden</button>
</form>

Erklärung: Fehler müssen klar angesagt, mit den betreffenden Feldern verknüpft sein und genaue Anweisungen zur Korrektur geben.

Feldgruppen mit aria-labelledby

Schlechtes Beispiel

Einstellungen

<!-- ❌ Schlecht: Unorganisierte Struktur -->
<form>
  <h4>Einstellungen</h4>

  <!-- Gemischte Elemente ohne Logik -->
  <div>
    <input type="radio" name="contact-bad"
           id="email-contact-bad" value="email">
    <label for="email-contact-bad">E-Mail</label>

    <input type="checkbox" id="newsletter-bad"
           value="newsletter">
    <label for="newsletter-bad">Newsletter</label>
  </div>

  <div>
    <input type="radio" name="contact-bad"
           id="phone-contact-bad" value="phone">
    <label for="phone-contact-bad">Telefon</label>

    <!-- Textfeld ohne Kontext -->
    <input type="text" placeholder="Name">
  </div>

  <div>
    <input type="checkbox" id="offers-bad" value="offers">
    <label for="offers-bad">Angebote</label>

    <input type="radio" name="contact-bad"
           id="sms-contact-bad" value="sms">
    <label for="sms-contact-bad">SMS</label>
  </div>

  <!-- Auswahl ohne Kontext -->
  <select>
    <option>Wählen...</option>
    <option>Täglich</option>
    <option>Wöchentlich</option>
  </select>

  <button type="submit">OK</button>
</form>

Gutes Beispiel

Kontakteinstellungen

Persönliche Informationen
Ihr vollständiger Name, wie er in unseren Mitteilungen erscheinen wird
Wir werden diese E-Mail für alle unsere Mitteilungen verwenden
Bevorzugte Kontaktmethode *
Kommunikationsarten

Wählen Sie die Arten von Mitteilungen aus, die Sie erhalten möchten

Kommunikationshäufigkeit
Legt die maximale Häufigkeit unserer Mailings fest
<!-- ✅ Gut: Logische Gruppen mit aria-labelledby -->
<form>
  <h4>Kontakteinstellungen</h4>

  <!-- Abschnitt für persönliche Informationen -->
  <div role="group" aria-labelledby="personal-info-title">
    <h5 id="personal-info-title">
      Persönliche Informationen
    </h5>

    <label for="name-good">Vollständiger Name *</label>
    <input
      type="text"
      id="name-good"
      name="name"
      required
      aria-required="true"
      aria-describedby="name-desc">
    <span id="name-desc">
      Ihr vollständiger Name, wie er in unseren Mitteilungen erscheinen wird
    </span>
  </div>

  <!-- Optionsfeld-Gruppe -->
  <div
    role="radiogroup"
    aria-labelledby="contact-method-title"
    aria-required="true">
    <h5 id="contact-method-title">
      Bevorzugte Kontaktmethode *
    </h5>

    <input
      type="radio"
      name="contact-good"
      id="email-contact-good"
      value="email"
      required>
    <label for="email-contact-good">E-Mail</label>

    <input
      type="radio"
      name="contact-good"
      id="phone-contact-good"
      value="phone"
      required>
    <label for="phone-contact-good">Telefon</label>
  </div>

  <!-- Kontrollkästchen-Gruppe -->
  <div role="group" aria-labelledby="comm-types-title">
    <h5 id="comm-types-title">Kommunikationsarten</h5>
    <p id="comm-types-desc">
      Wählen Sie die Arten von Mitteilungen aus, die Sie erhalten möchten
    </p>

    <input
      type="checkbox"
      id="newsletter-good"
      value="newsletter"
      aria-describedby="comm-types-desc">
    <label for="newsletter-good">Newsletter</label>
  </div>

  <button type="submit">
    Einstellungen speichern
  </button>
</form>

Erklärung: Gruppen verwandter Felder müssen über aria-labelledby mit ihren Titeln verknüpft werden, um klare semantische Beziehungen zu schaffen, insbesondere für Optionsfelder und Kontrollkästchen.

Komplexes Formular mit Echtzeit-Validierung

Schlechtes Beispiel

Konto erstellen

<!-- ❌ Schlecht: Aufdringliche Validierung -->
<form>
  <h4>Konto erstellen</h4>

  <!-- Sofortige Validierung bei jedem Tastendruck -->
  <label>Benutzername</label>
  <input
    type="text"
    v-model="complexBadForm.username"
    @input="validateUsernameBad"
    :class="{ 'error': usernameBadError }">
  <!-- Fehler, der ständig erscheint/verschwindet -->
  <div v-if="usernameBadError">
    
  </div>

  <!-- Keine Zuordnung mit aria-describedby -->
  <label>E-Mail</label>
  <input
    type="email"
    v-model="complexBadForm.email"
    @input="validateEmailBad"
    :class="{ 'error': emailBadError }">
  <div v-if="emailBadError"></div>

  <!-- Fehlermeldungen zu kurz und nicht hilfreich -->
  <!-- Keine Autovervollständigung zur Unterstützung von Passwort-Managern -->
  <label>Passwort</label>
  <input
    type="password"
    v-model="complexBadForm.password"
    @input="validatePasswordBad"
    :class="{ 'error': passwordBadError }">
  <div v-if="passwordBadError"></div>

  <button type="submit">Konto erstellen</button>
</form>

Gutes Beispiel

Konto erstellen

Anforderungen:
  • Mindestens 3 Zeichen
  • Nur Buchstaben, Zahlen und Unterstrich
Passwortstärke:
8+ Zeichen
Großbuchstabe
Zahl
Sonderzeichen
<!-- ✅ Gut: Respektvolle Validierung mit Feedback -->
<form>
  <h4>Konto erstellen</h4>

  <!-- Validierung bei Blur, nicht bei Input -->
  <label for="username-complex">Benutzername *</label>
  <input
    type="text"
    id="username-complex"
    v-model="complexGoodForm.username"
    @blur="validateUsernameGood"
    :class="{ error: usernameGoodError,
              success: usernameGoodValid }"
    aria-describedby="username-requirements username-feedback"
    aria-invalid="!!usernameGoodError"
    required>

  <!-- Klare Anweisungen -->
  <div id="username-requirements">
    <h6>Anforderungen:</h6>
    <ul>
      <li :class="{ valid: complexGoodForm.username.length >= 3 }">
        Mindestens 3 Zeichen
      </li>
      <li :class="{ valid: /^[a-zA-Z0-9_]+$/.test(username) }">
        Nur Buchstaben, Zahlen und Unterstrich
      </li>
    </ul>
  </div>

  <!-- Barrierefreies Feedback -->
  <div
    v-if="usernameGoodError"
    id="username-feedback"
    role="alert"
    aria-live="polite">
    ❌ 
  </div>
  <div
    v-else-if="usernameGoodValid"
    id="username-feedback"
    role="status"
    aria-live="polite">
    ✅ Benutzername verfügbar
  </div>

  <!-- Indikator für Passwortstärke -->
  <label for="password-complex">Passwort *</label>
  <input
    type="password"
    id="password-complex"
    v-model="complexGoodForm.password"
    @input="validatePasswordGood"
    aria-describedby="password-strength"
    autocomplete="new-password"
    required>

  <div id="password-strength">
    <h6>Passwortstärke:</h6>
    <div class="strength-indicator">
      <div
        class="strength-bar"
        :class="`strength-${passwordStrength}`"
        :style="{ width: `${passwordStrength * 25}%` }">
      </div>
    </div>
  </div>

  <button type="submit" :disabled="!isFormValid">
    Konto erstellen
  </button>
</form>

Erklärung: Für komplexe Formulare kann Echtzeit-Validierung helfen, muss aber auf nicht aufdringliche und barrierefreie Weise implementiert werden.