Barrierefreie Formulare
Erstellen Sie Formulare, die für alle mit einer klaren und geführten Erfahrung nutzbar sind
Labels und Zuordnungen
Schlechtes Beispiel
<!-- ❌ 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
<!-- ✅ 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
<!-- ❌ 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
<!-- ✅ 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
<!-- ❌ 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
<!-- ✅ 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
<!-- ❌ 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
<!-- ✅ 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.