P0: Formular-Accessibility — Fehlerboxen mit role=alert, Pflichtfeld-ARIA

- Alle fehler-Boxen: role=alert + tabindex=-1
- Login: aria-invalid/aria-describedby auf Feldern bei Fehler
- Pflichtfelder: aria-required=true ergänzt
This commit is contained in:
Flo Hartmann
2026-08-26 08:40:45 +00:00
parent b9444289b5
commit 742c265df8
11 changed files with 20 additions and 18 deletions

View File

@@ -22,7 +22,7 @@
<div class="mb-4 px-4 py-3 rounded-lg border bg-emerald-50 border-emerald-200 text-emerald-800 text-sm">{{ meldung }}</div>
{% endif %}
{% if fehler %}
<div class="mb-4 px-4 py-3 rounded-lg border bg-red-50 border-red-200 text-red-700 text-sm">{{ fehler }}</div>
<div class="mb-4 px-4 py-3 rounded-lg border bg-red-50 border-red-200 text-red-700 text-sm" role="alert" tabindex="-1">{{ fehler }}</div>
{% endif %}
<div class="bg-white border border-slate-200 rounded-xl shadow-sm p-4 mb-6">

View File

@@ -15,7 +15,7 @@
</div>
{% endif %}
{% if fehler %}
<div class="mt-4 mb-2 rounded-lg bg-red-50 border border-red-200 text-red-800 px-4 py-2 text-sm">
<div class="mt-4 mb-2 rounded-lg bg-red-50 border border-red-200 text-red-800 px-4 py-2 text-sm" role="alert" tabindex="-1">
{{ fehler }}
</div>
{% endif %}

View File

@@ -31,7 +31,7 @@
<form method="post" action="/dedup/pruefen" class="flex flex-wrap items-end gap-3">
<div>
<label for="titel" class="block text-xs font-medium text-slate-500 mb-1">Spieltitel *</label>
<input type="text" id="titel" name="titel" required value="{{ ergebnis.titel if ergebnis else '' }}"
<input type="text" id="titel" name="titel" required aria-required="true" value="{{ ergebnis.titel if ergebnis else '' }}"
class="border border-slate-300 rounded-lg px-3 py-2 text-sm w-72 focus:outline-none focus:ring-2 focus:ring-emerald-500">
</div>
<div>
@@ -51,7 +51,7 @@
</form>
{% if fehler %}
<div class="mt-4 px-4 py-3 rounded-lg border bg-red-50 border-red-200 text-red-700 text-sm">{{ fehler }}</div>
<div class="mt-4 px-4 py-3 rounded-lg border bg-red-50 border-red-200 text-red-700 text-sm" role="alert" tabindex="-1">{{ fehler }}</div>
{% endif %}
{% if meldung %}
<div class="mt-4 px-4 py-3 rounded-lg border bg-emerald-50 border-emerald-200 text-emerald-800 text-sm">{{ meldung }}</div>

View File

@@ -10,12 +10,12 @@
class="bg-white border border-slate-200 rounded-xl shadow-sm p-4 max-w-2xl space-y-4">
<div>
<label for="name" class="block text-xs font-medium text-slate-500 mb-1">Name/Titel der Ausgabe *</label>
<input type="text" id="name" name="name" required value="{{ ausgabe.name }}"
<input type="text" id="name" name="name" required aria-required="true" value="{{ ausgabe.name }}"
class="w-full border border-slate-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-emerald-500">
</div>
<div>
<label for="redaktionsschluss" class="block text-xs font-medium text-slate-500 mb-1">Redaktionsschluss *</label>
<input type="date" id="redaktionsschluss" name="redaktionsschluss" required value="{{ ausgabe.redaktionsschluss.isoformat() }}"
<input type="date" id="redaktionsschluss" name="redaktionsschluss" required aria-required="true" value="{{ ausgabe.redaktionsschluss.isoformat() }}"
class="border border-slate-300 rounded-lg px-3 py-2 text-sm w-48 focus:outline-none focus:ring-2 focus:ring-emerald-500">
</div>
<div class="flex items-center gap-3">

View File

@@ -13,7 +13,7 @@
<div class="mb-4 px-4 py-3 rounded-lg border bg-emerald-50 border-emerald-200 text-emerald-800 text-sm">{{ meldung }}</div>
{% endif %}
{% if fehler %}
<div class="mb-4 px-4 py-3 rounded-lg border bg-red-50 border-red-200 text-red-700 text-sm">{{ fehler }}</div>
<div class="mb-4 px-4 py-3 rounded-lg border bg-red-50 border-red-200 text-red-700 text-sm" role="alert" tabindex="-1">{{ fehler }}</div>
{% endif %}
{% if ist_admin %}
@@ -22,12 +22,12 @@
<form method="post" action="/erinnerung/ausgaben" class="flex flex-wrap items-end gap-3">
<div>
<label for="name" class="block text-xs font-medium text-slate-500 mb-1">Name/Titel der Ausgabe *</label>
<input type="text" id="name" name="name" required placeholder="z. B. 3/2025"
<input type="text" id="name" name="name" required aria-required="true" placeholder="z. B. 3/2025"
class="border border-slate-300 rounded-lg px-3 py-2 text-sm w-64 focus:outline-none focus:ring-2 focus:ring-emerald-500">
</div>
<div>
<label for="redaktionsschluss" class="block text-xs font-medium text-slate-500 mb-1">Redaktionsschluss *</label>
<input type="date" id="redaktionsschluss" name="redaktionsschluss" required
<input type="date" id="redaktionsschluss" name="redaktionsschluss" required aria-required="true"
class="border border-slate-300 rounded-lg px-3 py-2 text-sm w-48 focus:outline-none focus:ring-2 focus:ring-emerald-500">
</div>
<button type="submit"

View File

@@ -11,7 +11,7 @@
</p>
{% if fehler %}
<div class="mb-4 px-4 py-3 rounded-lg border bg-red-50 border-red-200 text-red-700 text-sm">{{ fehler }}</div>
<div class="mb-4 px-4 py-3 rounded-lg border bg-red-50 border-red-200 text-red-700 text-sm" role="alert" tabindex="-1">{{ fehler }}</div>
{% endif %}
<div class="grid gap-4 md:grid-cols-3 mb-6">

View File

@@ -15,7 +15,7 @@
</div>
{% endif %}
{% if fehler %}
<div class="mb-4 px-4 py-3 rounded-lg border bg-red-50 border-red-200 text-red-700 text-sm">{{ fehler }}</div>
<div class="mb-4 px-4 py-3 rounded-lg border bg-red-50 border-red-200 text-red-700 text-sm" role="alert" tabindex="-1">{{ fehler }}</div>
{% endif %}
<div class="flex flex-wrap items-end gap-3 mb-4">

View File

@@ -18,7 +18,7 @@
class="bg-white border border-slate-200 rounded-xl shadow-sm p-4 max-w-2xl space-y-4 w-full">
<div>
<label for="titel" class="block text-xs font-medium text-slate-500 mb-1">Spieltitel *</label>
<input type="text" id="titel" name="titel" required value="{{ eintrag.titel }}"
<input type="text" id="titel" name="titel" required aria-required="true" value="{{ eintrag.titel }}"
class="w-full border border-slate-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-emerald-500">
</div>
<div class="grid md:grid-cols-2 gap-4">

View File

@@ -13,7 +13,7 @@
<div class="mb-4 px-4 py-3 rounded-lg border bg-emerald-50 border-emerald-200 text-emerald-800 text-sm">{{ meldung }}</div>
{% endif %}
{% if fehler %}
<div class="mb-4 px-4 py-3 rounded-lg border bg-red-50 border-red-200 text-red-700 text-sm">{{ fehler }}</div>
<div class="mb-4 px-4 py-3 rounded-lg border bg-red-50 border-red-200 text-red-700 text-sm" role="alert" tabindex="-1">{{ fehler }}</div>
{% endif %}
<div class="bg-white border border-slate-200 rounded-xl shadow-sm p-4 mb-6">
@@ -22,7 +22,7 @@
<form method="post" action="/planung/neu" class="grid grid-cols-1 gap-3 sm:flex sm:flex-wrap sm:items-end">
<div class="sm:w-64 lg:w-72">
<label for="titel" class="block text-xs font-medium text-slate-500 mb-1">Spieltitel *</label>
<input type="text" id="titel" name="titel" required
<input type="text" id="titel" name="titel" required aria-required="true"
class="w-full min-h-[44px] border border-slate-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-emerald-500">
</div>
<div class="sm:w-48 lg:w-52">

View File

@@ -6,7 +6,7 @@
</h1>
{% if fehler %}
<div class="mb-4 max-w-lg rounded-lg border border-red-300 bg-red-50 text-red-700 text-sm px-3 py-2" role="alert">
<div class="mb-4 max-w-lg rounded-lg border border-red-300 bg-red-50 text-red-700 text-sm px-3 py-2" role="alert" tabindex="-1" id="formular-fehler">
{{ fehler }}
</div>
{% endif %}
@@ -17,7 +17,7 @@
<div>
<label for="username" class="block text-sm font-medium mb-1">Benutzername</label>
<input id="username" name="username" value="{{ form.username }}" required minlength="3"
<input id="username" name="username" value="{{ form.username }}" required aria-required="true" minlength="3"
{% if aktion != 'neu' %}disabled{% endif %}
class="w-full border border-slate-300 rounded-lg px-3 py-2 {% if aktion != 'neu' %}bg-slate-100{% endif %}">
{% if aktion != 'neu' %}
@@ -62,7 +62,7 @@
<label for="password" class="block text-sm font-medium mb-1">
{% if aktion == 'neu' %}Passwort{% else %}Neues Passwort (leer = unver&auml;ndert){% endif %}
</label>
<input id="password" name="password" type="password" {% if aktion == 'neu' %}required{% endif %} minlength="8"
<input id="password" name="password" type="password" {% if aktion == 'neu' %}required aria-required="true"{% endif %} minlength="8"
class="w-full border border-slate-300 rounded-lg px-3 py-2">
</div>

View File

@@ -11,7 +11,7 @@
<h1 class="text-xl font-bold mb-1 flex items-center gap-2">{% from "icons.html" import icon_wuerfelbecher %}{{ icon_wuerfelbecher("h-6 w-6 text-emerald-700") }} {{ app_name }}</h1>
<p class="text-sm text-slate-500 mb-6">Bitte anmelden.</p>
{% if fehler %}
<div class="mb-4 rounded-lg border border-red-300 bg-red-50 text-red-700 text-sm px-3 py-2" role="alert">
<div id="login-fehler" class="mb-4 rounded-lg border border-red-300 bg-red-50 text-red-700 text-sm px-3 py-2" role="alert" tabindex="-1">
{{ fehler }}
</div>
{% endif %}
@@ -19,11 +19,13 @@
<div>
<label for="username" class="block text-sm font-medium mb-1">Benutzername</label>
<input id="username" name="username" value="{{ username }}" required autocomplete="username"
{% if fehler %}aria-invalid="true" aria-describedby="login-fehler"{% endif %}
class="w-full border border-slate-300 rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-emerald-500">
</div>
<div>
<label for="password" class="block text-sm font-medium mb-1">Passwort</label>
<input id="password" name="password" type="password" required autocomplete="current-password"
{% if fehler %}aria-invalid="true" aria-describedby="login-fehler"{% endif %}
class="w-full border border-slate-300 rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-emerald-500">
</div>
<button type="submit"