You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
MEDIUM: eight form controls have no programmatic label, including the event title, and the account dialog's password-reveal button has no accessible name at all #185
Severity: MEDIUM. Class: broken. WCAG 2.2: 4.1.2 Name, Role, Value (Level A), 3.3.2 Labels or Instructions (Level A), 1.3.1 Info and Relationships (Level A).
What breaks
Resolving, for every non-hidden input/select/textarea in the document, whether a name is available from aria-label, aria-labelledby, label[for], a wrapping <label> or title:
control
file:line
only cue
#ev-title — the event title, the one required field
public/index.html:359
placeholder="Titel des Termins"
#search-input
public/index.html:327
placeholder="Suchen…"
#ev-attendee-name
public/index.html:519
placeholder="Name"
#ev-attendee-email
public/index.html:520
placeholder="E-Mail"
#recur-monthly-week-idx
public/index.html:457
none
#recur-monthly-weekday
public/index.html:465
none
#recur-end-date
public/index.html:491
none
#recur-end-count
public/index.html:498
none
A placeholder is not a label — it vanishes the moment the user types, and several screen readers do not announce it. The four recurrence controls have nothing at all: they are the "Am: [ersten] [Montag]" and "Nach: [10] Wiederholungen" rows, whose meaning lives entirely in adjacent text nodes.
Four .form-label elements label nothing programmatically (no for, no wrapped control): "Wiederholen am" at :432 and :446, "Ende der Wiederholung" at :480, "Teilnehmer" at :517. Each is a group caption rendered as a label.
public/index.html:596 is the only control in the application with no accessible name whatsoever:
No text, no aria-label, no title — Chromium's name computation returns "" and a screen reader announces "button". Its twin in the onboarding form (public/index.html:113) carries aria-label="Passwort anzeigen", so the fix is copying one attribute.
Why it matters
Note what is not in this list: #add-calendar-btn, the .cal-action-btn set and the .settings-action-btn set all get correct names from their title attributes — verified in Chromium's tree as "Teilen & Link", "Umbenennen", "Als ICS exportieren", "Account entfernen". The eight above are the disjoint set with no name source at all, and one of them is the field a user must fill to create an event.
Fix direction
Add <label class="form-label" for="…"> to the four recurrence controls and the two attendee fields. Give #ev-title and #search-input a visually hidden label using the .sr-only class that public/css/app.css:1147 already defines and that no element currently uses. Copy the aria-label onto public/index.html:596. Convert the four caption labels to role="group" + aria-labelledby, or to <fieldset><legend>.
Found by an automated expert review pass (vacation homework item 14, pass 5, the accessibility lens, 2026-08-13). Full evidence in Max's knowledge base at freeshard/calendar-review/pass-accessibility.md, finding A11Y-05.
📌 Hinweis an @ClaydeCode: Bitte überprüfe die Lösung und schließe das Issue selbst, wenn du mit dem Ergebnis zufrieden bist, oder passe es nach deinen Vorstellungen an.
Partly fixed on origin/master @ 61a28b7. Not closing.
The eight unnamed controls and the password-reveal button are genuinely fixed and I confirmed it with a check that goes red when the fix is removed. The second item in "What breaks" — the four .form-label group captions that label nothing programmatically — is untouched, and nothing in the suite asserts it.
Unlike other issues in this batch, the cited commit 7d4a431 is real, is in master's history, and its diff is exactly on point (public/index.html, public/js/eventModal.js, plus the test).
What changed and is verified
All eight controls from the table now carry an aria-label, read directly off the tags at 61a28b7:
control
file:line
accessible name
#ev-title
public/index.html:506
aria-label="Titel des Termins"
#search-input
public/index.html:460
aria-label="Kalender durchsuchen"
#ev-attendee-name
public/index.html:681
aria-label="Name des Teilnehmers"
#ev-attendee-email
public/index.html:682
aria-label="E-Mail des Teilnehmers"
#recur-monthly-week-idx
public/index.html:603
aria-label="Woche des Monats"
#recur-monthly-weekday
public/index.html:611
aria-label="Wochentag"
#recur-end-date
public/index.html:637
aria-label="Enddatum der Wiederholung"
#recur-end-count
public/index.html:644
aria-label="Anzahl Wiederholungen"
The name-less button is named: public/index.html:768 is now <button type="button" class="toggle-password" data-target="acc-password" aria-label="Passwort anzeigen">, matching its onboarding twin at public/index.html:97.
Beyond what the issue asked for, public/js/eventModal.js:857 now flips the name with the state: btn.setAttribute('aria-label', input.type === 'password' ? 'Passwort anzeigen' : 'Passwort verbergen'). The handler is bound via document.querySelectorAll('.toggle-password') at public/js/eventModal.js:853 and initEventModal() is called from public/js/app.js:64, so the account-dialog button is in scope — a reveal button that keeps announcing "show password" while the password is visible would have been the next finding, and it was pre-empted.
The fix used aria-label rather than the .sr-only label the issue suggested. For 4.1.2 and 1.3.1 that is equivalent, so I am not counting it against the fix. .sr-only (public/css/app.css:1206) is still used by only two elements (:124, :969).
Reproduction, re-run against 61a28b7
I re-ran the issue's own method — resolve, for every non-hidden input/select/textarea, whether a name is available from aria-label, aria-labelledby, label[for], a wrapping <label> or title:
total non-hidden controls: 81
--- UNNAMED ---
(none)
--- the eight from the issue ---
index.html:505 NAMED via aria-label="Titel des Termins" #ev-title
index.html:460 NAMED via aria-label="Kalender durchsuchen" #search-input
index.html:681 NAMED via aria-label="Name des Teilnehmers" #ev-attendee-name
index.html:682 NAMED via aria-label="E-Mail des Teilnehmers" #ev-attendee-email
index.html:603 NAMED via aria-label="Woche des Monats" #recur-monthly-week-idx
index.html:611 NAMED via aria-label="Wochentag" #recur-monthly-weekday
index.html:637 NAMED via aria-label="Enddatum der Wiederholung" #recur-end-date
index.html:644 NAMED via aria-label="Anzahl Wiederholungen" #recur-end-count
Not one of the 81 non-hidden controls in the document is left without a name source. That part of the issue is closed out.
What is still live: the four caption labels
Four .form-label elements label nothing programmatically (no for, no wrapped control) [...] Each is a group caption rendered as a label.
Fix direction: Convert the four caption labels to role="group" + aria-labelledby, or to <fieldset><legend>.
Still exactly as filed. Scanning for <label> elements with no for= and no labelable control inside:
--- orphan <label> (no for=, no control inside) ---
index.html:335 text="Arbeitstage"
index.html:578 text="Wiederholen am"
index.html:592 text="Wiederholen am"
index.html:626 text="Ende der Wiederholung"
index.html:679 text="Teilnehmende"
:578, :592, :626, :679 are the four from the issue. And the grouping half was never added either — grep -n 'role="group"\|role="radiogroup"\|<fieldset\|<legend' public/index.html returns exactly one hit in the whole file, public/index.html:410 (.view-tabs), which predates this fix. So:
public/index.html:592 "Wiederholen am" still captions the name="recur-monthly-type" radio pair (:594, :601) with no role="group"/<fieldset> and no aria-labelledby; a screen reader reaches the radios with no group context.
public/index.html:626 "Ende der Wiederholung" likewise for the name="recur-end-type" radios (:629, :634, :641).
public/index.html:578 "Wiederholen am" likewise for the seven .recur-byday checkboxes (:580–:586).
public/index.html:679 "Teilnehmende" likewise for the attendee row.
A <label> with no labeled control is also invalid per the HTML spec, independent of WCAG. The :335 "Arbeitstage" caption over the #wh-day-* checkboxes is the same defect and was not in the issue's list — same fix, mentioning it so it is not lost.
Test run
server/test/issue185_form_controls_accessible_labels.test.js exists (added by 7d4a431).
It has no assertion of any kind about the four caption labels, group roles, <fieldset> or aria-labelledby — so the green run above is not evidence about the part that is still broken.
Breaking the fix on purpose
Four experiments, working tree restored with git checkout -- . after each:
Stripped aria-label="Titel des Termins" from public/index.html:506 → red, # pass 2 / # fail 2, error: '#ev-title must have an accessible name / aria-label="Titel des Termins"'.
Stripped aria-label="Passwort anzeigen" from the button at public/index.html:768 → red, error: 'account dialog password toggle button must have aria-label="Passwort anzeigen"'.
Deleted the setAttribute('aria-label', …) line at public/js/eventModal.js:857 → red, error: 'eventModal.js must dynamically update aria-label attribute when password visibility is toggled'.
So for what it does cover, the test has teeth. One caveat on its strength:
Swapped the two recurrence aria-labels, so #recur-monthly-week-idx announces "Wochentag" and #recur-monthly-weekday announces "Woche des Monats" — both controls actively mislabeled → stayed green, # pass 4 / # fail 0.
Each of the eight assertions is two independent whole-document indexHtml.includes(...) calls — one for id="…", one for aria-label="…" — which never checks that the two are on the same element. The names are correct today; I verified the associations by reading the tags, not by trusting the test. But the test would not catch a mislabeling regression, only an outright deletion.
Verdict
The headline defect — including #ev-title, the one required field, and the button with no name at all — is fixed and holds up under a check that goes red. The group-caption item from the same issue is untouched and unasserted. Leaving this open for public/index.html:578, :592, :626, :679 (and :335); it is now a small, well-specified piece of work.
Working tree restored, git status --porcelain empty.
📌 Hinweis an @ClaydeCode: Bitte überprüfe die Lösung und schließe das Issue selbst, wenn du mit dem Ergebnis zufrieden bist, oder passe es nach deinen Vorstellungen an.
Partly fixed on origin/master @ 86ff8d1. Not closing.
Unchanged from my last pass. The eight unnamed controls and the password-reveal button are still fixed and still hold up under a check that goes red. The second item in "What breaks" — the four .form-label group captions that label nothing programmatically — is still untouched, and nothing in the suite asserts it.
The cited commit does not touch this issue
c7ffd93 is real and is in master's history, but its diff has nothing to do with #185. What it changes in the three front-end files:
public/css/app.css:1114-1121, :1512-1526 — visibility: hidden on the off-canvas sidebar and settings drawer.
public/index.html:228 — hidden class on #settings-drawer; :1037, :1090, :1148 — role="dialog" aria-modal="true" aria-labelledby on the invitations / availability / shortcuts modals.
public/js/dialogFocus.js:39, :64, :93 — open class bookkeeping in openDialog/closeDialog and the getTopDialog selector.
Dialog-level accessibility and focus containment, i.e. #186's territory, not the form-control labels this issue is about. The only other commit touching public/index.html since my last pass is b9af960 (webcal subscriptions). No commit in 61a28b7..86ff8d1 adds a group role, a <fieldset> or an aria-labelledby to any of the four captions.
Still verified fixed
Re-ran the issue's own method against 86ff8d1 — for every non-hidden input/select/textarea, resolve a name from aria-label, aria-labelledby, label[for], a wrapping <label> or title:
total non-hidden controls: 85
--- UNNAMED ---
(none)
--- the eight from the issue ---
index.html:518 #ev-title aria-label="Titel des Termins"
index.html:473 #search-input aria-label="Kalender durchsuchen"
index.html:694 #ev-attendee-name aria-label="Name des Teilnehmers"
index.html:695 #ev-attendee-email aria-label="E-Mail des Teilnehmers"
index.html:616 #recur-monthly-week-idx aria-label="Woche des Monats"
index.html:624 #recur-monthly-weekday aria-label="Wochentag"
index.html:650 #recur-end-date aria-label="Enddatum der Wiederholung"
index.html:657 #recur-end-count aria-label="Anzahl Wiederholungen"
index.html:781 .toggle-password[data-target="acc-password"] aria-label="Passwort anzeigen"
85 controls now (four more than at 61a28b7), none without a name source. public/js/eventModal.js:864 still flips the name with the state (btn.setAttribute('aria-label', input.type === 'password' ? 'Passwort anzeigen' : 'Passwort verbergen')), bound at public/js/eventModal.js:860.
Still live: the four caption labels
Four .form-label elements label nothing programmatically (no for, no wrapped control) [...] Fix direction: Convert the four caption labels to role="group" + aria-labelledby, or to <fieldset><legend>.
Scanning for <label> with no for= and no labelable control inside:
:591, :605, :639, :692 are the four from the issue (:692 is the "Teilnehmer" caption, since renamed "Teilnehmende"). And the grouping half was never added: role="group", role="radiogroup", <fieldset> and <legend> together have exactly one hit in the whole document, public/index.html:423 (.view-tabs), which predates 7d4a431. Ancestor chains of the grouped controls, read off the tree:
public/index.html:605 "Wiederholen am" captions the name="recur-monthly-type" radio pair (:608, :613). Chain: label.radio-option < div.radio-group < div.form-group.hidden — no role, no aria-labelledby.
public/index.html:639 "Ende der Wiederholung" likewise for the name="recur-end-type" radios (:642, :647, :654); same bare div.radio-group.
public/index.html:591 "Wiederholen am" likewise for the seven .recur-byday checkboxes (:593–:599); chain label.weekday-check < div.weekdays-checkgrid < div.form-group.hidden.
public/index.html:692 "Teilnehmende" likewise for the attendee row.
A screen reader reaches each of those radios and checkboxes with no group context, and a <label> with no labeled control is invalid per the HTML spec independent of WCAG. The :348 "Arbeitstage" caption over the #wh-day-* checkboxes (:350–:356) is the same defect and was not in the issue's list — same fix, naming it so it is not lost.
Test run
server/test/issue185_form_controls_accessible_labels.test.js, unchanged since 7d4a431 (git log on the file shows that one commit only):
grep -niE 'group|fieldset|legend|labelledby|form-label|Wiederholen am|Ende der Wiederholung|Teilnehmende|Arbeitstage' over that test file returns nothing, and no test anywhere under server/test or e2e mentions fieldset, role="group" or radiogroup. The green run above is therefore not evidence about the part that is still broken.
Breaking the fix on purpose
Four experiments, git checkout -- . after each.
Stripped aria-label="Titel des Termins" from public/index.html:519 → red, # pass 2 / # fail 2, error: '#ev-title must have an accessible name / aria-label="Titel des Termins"'.
Stripped aria-label="Passwort anzeigen" from the button at public/index.html:781 → red, # pass 2 / # fail 2, error: 'account dialog password toggle button must have aria-label="Passwort anzeigen"'.
Deleted public/js/eventModal.js:864 → red, # pass 2 / # fail 2, error: 'eventModal.js must dynamically update aria-label attribute when password visibility is toggled'.
So the fixed half has teeth. The still-live half has none, and two experiments show it:
Deleted all four caption labels outright (-e '692d' -e '639d' -e '605d' -e '591d', removing <label class="form-label">Wiederholen am</label> ×2, Ende der Wiederholung, Teilnehmende) → stayed green, # pass 4 / # fail 0. The issue's own test does not notice the four elements it was supposed to cover disappearing.
Swapped the two recurrence aria-labels so #recur-monthly-week-idx announces "Wochentag" and #recur-monthly-weekday announces "Woche des Monats" — both actively mislabeled → stayed green, # pass 4 / # fail 0. Each assertion is two independent whole-document indexHtml.includes(...) calls, one for id="…" and one for aria-label="…", never checking the two are on the same element. The names are correct today; I verified that by reading the tags, not by trusting the test.
Verdict
Nothing regressed and nothing advanced since 61a28b7. The headline defect stays fixed; the group-caption item from the same issue is still untouched, still unasserted, and the commit cited for this round is about dialog roles rather than form labels. Leaving open for public/index.html:591, :605, :639, :692 (and :348) — still a small, well-specified piece of work.
Working tree restored, git status --porcelain empty.
📌 Hinweis an @ClaydeCode: Bitte überprüfe die Lösung und schließe das Issue selbst, wenn du mit dem Ergebnis zufrieden bist, oder passe es nach deinen Vorstellungen an.
Partly fixed on origin/master @ 026c459. Not closing.
Unchanged from my last two passes. The eight unnamed controls and the password-reveal button are fixed and still hold up under a check that goes red. The second item in "What breaks" — the four .form-label group captions that label nothing programmatically — is still untouched, and nothing in the suite asserts it.
The cited commit does not touch this issue
dc05830 is real and is in master's history (2026-10-04), but its diff does not contain a single front-end change relevant to #185 — or to public/index.html at all:
Secrets generation / portal id / sync worker — #82's territory. The only two commits touching public/index.html since my last pass (86ff8d1) are 73f9e86 (issue #87, dialog focus management) and 3bf4e13 (issue #96, notifications). No commit in 86ff8d1..026c459 adds a group role, a <fieldset> or an aria-labelledby to any of the four captions.
Still verified fixed
Re-ran the issue's own method against 026c459 — for every input/select/textarea with type != hidden, resolve a name from aria-label, aria-labelledby, label[for], a wrapping <label> or title:
total controls (type!=hidden): 85
of those, not inside hidden/aria-hidden subtree: 85
--- UNNAMED CONTROLS ---
(none)
--- the eight from the issue + password button ---
:526 #ev-title -> aria-label="Titel des Termins"
:481 #search-input -> aria-label="Kalender durchsuchen"
:702 #ev-attendee-name -> aria-label="Name des Teilnehmers"
:703 #ev-attendee-email -> aria-label="E-Mail des Teilnehmers"
:624 #recur-monthly-week-idx -> aria-label="Woche des Monats"
:632 #recur-monthly-weekday -> aria-label="Wochentag"
:658 #recur-end-date -> aria-label="Enddatum der Wiederholung"
:665 #recur-end-count -> aria-label="Anzahl Wiederholungen"
:97 button.toggle-password[data-target="ob-password"] -> aria-label="Passwort anzeigen"
:789 button.toggle-password[data-target="acc-password"] -> aria-label="Passwort anzeigen"
Not one of the 85 controls in the document is left without a name source, and I verified the eight associations by reading the tags rather than trusting the test. public/js/eventModal.js:898 still flips the name with the state:
Four .form-label elements label nothing programmatically (no for, no wrapped control) [...] Fix direction: Convert the four caption labels to role="group" + aria-labelledby, or to <fieldset><legend>.
Scanning for <label> with no for= and no labelable control inside its close tag:
--- ORPHAN <label> (no for=, no labelable control inside) ---
:348 class="form-label" text="Arbeitstage"
:599 class="form-label" text="Wiederholen am"
:613 class="form-label" text="Wiederholen am"
:647 class="form-label" text="Ende der Wiederholung"
:700 class="form-label" text="Teilnehmende"
:599, :613, :647, :700 are the four from the issue (:700 is the "Teilnehmer" caption, since renamed "Teilnehmende" by #168). The grouping half was never added either — role="group", role="radiogroup", <fieldset> and <legend> together have exactly one hit in the whole document, public/index.html:431 (.view-tabs, role="group" aria-label="Ansicht wählen"), which predates 7d4a431. Every other aria-labelledby in the file is on a role="dialog" (:228, :510, :730, :817, :868, :906, :927, :950, :1037, :1045, :1061, :1098, :1156) — #186/#87 work, not this. Nothing in public/js/ adds a group role at runtime either (grep -nE "setAttribute\('role'" public/js/ hits only grid/rowgroup/row/gridcell in calendar.js and role="alert" on toasts).
Ancestor chains of the grouped controls, read off the parsed tree:
No role, no aria-labelledby anywhere on those chains. So a screen reader still reaches each of those radios and checkboxes with no group context: public/index.html:613 "Wiederholen am" over the recur-monthly-type radio pair, :647 "Ende der Wiederholung" over the three recur-end-type radios, :599 "Wiederholen am" over the seven .recur-byday checkboxes, :700 "Teilnehmende" over the attendee row. A <label> with no labeled control is also invalid per the HTML spec, independent of WCAG. The :348 "Arbeitstage" caption over the #wh-day-* checkboxes (:350–:356) is the same defect and was not in the issue's list — same fix, naming it so it is not lost.
Test run
server/test/issue185_form_controls_accessible_labels.test.js, still unchanged since 7d4a431 (git log on the file shows that one commit only):
grep -rniE 'fieldset|<legend|role="?(group|radiogroup)' over server/test and e2e returns nothing at all, so the green run above is not evidence about the part that is still broken.
Breaking the fix on purpose
Five experiments, git checkout -- . after each. The fixed half has teeth:
Stripped aria-label="Titel des Termins" from public/index.html:527 (the #ev-title tag's second line) → red, # pass 2 / # fail 2, error: '#ev-title must have an accessible name / aria-label="Titel des Termins"'.
Stripped aria-label="Passwort anzeigen" from the button at public/index.html:789 → red, # pass 2 / # fail 2, error: 'account dialog password toggle button must have aria-label="Passwort anzeigen"'.
Deleted public/js/eventModal.js:898 → red, # pass 2 / # fail 2, error: 'eventModal.js must dynamically update aria-label attribute when password visibility is toggled'.
The still-live half has none, and two experiments show it:
Deleted all four caption labels outright (sed -i -e '700d' -e '647d' -e '613d' -e '599d', removing <label class="form-label">Wiederholen am</label> ×2, Ende der Wiederholung and Teilnehmende) → stayed green, # pass 4 / # fail 0. The issue's own test does not notice the four elements it was supposed to cover disappearing. (Deleting the Teilnehmende one alone does turn server/test/issue168_gendergerecht_german_copy.test.js red — error: 'index.html should contain gendergerecht label for attendees' — but that asserts the caption's wording, not any programmatic association, and the other three deletions are asserted by nothing anywhere in the repo.)
Swapped the two recurrence aria-labels so #recur-monthly-week-idx announces "Wochentag" and #recur-monthly-weekday announces "Woche des Monats" — both actively mislabeled → stayed green, # pass 4 / # fail 0. Each of the eight assertions is two independent whole-document indexHtml.includes(...) calls, one for id="…" and one for aria-label="…", which never checks that the two are on the same element. So the test catches an outright deletion but not a mislabeling regression.
Verdict
Nothing regressed and nothing advanced since 86ff8d1. The headline defect — #ev-title, the one required field, and the button that had no name at all — stays fixed and verified. The group-caption item from the same issue is still untouched, still unasserted, and the commit cited for this round (dc05830) is about secrets generation and the sync worker rather than form labels. Leaving open for public/index.html:599, :613, :647, :700 (and :348) — still a small, well-specified piece of work: role="group" + aria-labelledby on the three div.radio-group/div.weekdays-checkgrid containers and the attendee row, or <fieldset><legend>.
Working tree restored, git status --porcelain empty.
Automated attempt stopped without a change: Agent reported blocked: Fix complete: the five orphan caption labels (Arbeitstage, Wiederholen am x2, Ende der Wiederholung, Teilnehmende) became div captions referenced via role="group" + aria-labelledby on their control containers, with role-based Playwright tests and the #168 test updated to the new tag. Blocked because the Playwright webServer refuses to start: the shared /tmp/webcalendar-e2e/webcalendar.db holds a key-check written with a different key (the #333 guard), so a human must delete /tmp/webcalendar-e2e/ (and ideally give each worktree its own e2e DATABASE_PATH) before the driver re-runs.
Activity
BeSovereign commentedon Sep 9, 2026
🛠️ Lösung & Test-Verifizierung
master-Branch auf GitHub gepusht:7d4a431.📌 Hinweis an @ClaydeCode: Bitte überprüfe die Lösung und schließe das Issue selbst, wenn du mit dem Ergebnis zufrieden bist, oder passe es nach deinen Vorstellungen an.
ClaydeCode commentedon Sep 28, 2026
Partly fixed on
origin/master@61a28b7. Not closing.The eight unnamed controls and the password-reveal button are genuinely fixed and I confirmed it with a check that goes red when the fix is removed. The second item in "What breaks" — the four
.form-labelgroup captions that label nothing programmatically — is untouched, and nothing in the suite asserts it.Unlike other issues in this batch, the cited commit
7d4a431is real, is inmaster's history, and its diff is exactly on point (public/index.html,public/js/eventModal.js, plus the test).What changed and is verified
All eight controls from the table now carry an
aria-label, read directly off the tags at61a28b7:file:line#ev-titlepublic/index.html:506aria-label="Titel des Termins"#search-inputpublic/index.html:460aria-label="Kalender durchsuchen"#ev-attendee-namepublic/index.html:681aria-label="Name des Teilnehmers"#ev-attendee-emailpublic/index.html:682aria-label="E-Mail des Teilnehmers"#recur-monthly-week-idxpublic/index.html:603aria-label="Woche des Monats"#recur-monthly-weekdaypublic/index.html:611aria-label="Wochentag"#recur-end-datepublic/index.html:637aria-label="Enddatum der Wiederholung"#recur-end-countpublic/index.html:644aria-label="Anzahl Wiederholungen"The name-less button is named:
public/index.html:768is now<button type="button" class="toggle-password" data-target="acc-password" aria-label="Passwort anzeigen">, matching its onboarding twin atpublic/index.html:97.Beyond what the issue asked for,
public/js/eventModal.js:857now flips the name with the state:btn.setAttribute('aria-label', input.type === 'password' ? 'Passwort anzeigen' : 'Passwort verbergen'). The handler is bound viadocument.querySelectorAll('.toggle-password')atpublic/js/eventModal.js:853andinitEventModal()is called frompublic/js/app.js:64, so the account-dialog button is in scope — a reveal button that keeps announcing "show password" while the password is visible would have been the next finding, and it was pre-empted.The fix used
aria-labelrather than the.sr-onlylabel the issue suggested. For 4.1.2 and 1.3.1 that is equivalent, so I am not counting it against the fix..sr-only(public/css/app.css:1206) is still used by only two elements (:124,:969).Reproduction, re-run against
61a28b7I re-ran the issue's own method — resolve, for every non-hidden
input/select/textarea, whether a name is available fromaria-label,aria-labelledby,label[for], a wrapping<label>ortitle:Not one of the 81 non-hidden controls in the document is left without a name source. That part of the issue is closed out.
What is still live: the four caption labels
Still exactly as filed. Scanning for
<label>elements with nofor=and no labelable control inside::578,:592,:626,:679are the four from the issue. And the grouping half was never added either —grep -n 'role="group"\|role="radiogroup"\|<fieldset\|<legend' public/index.htmlreturns exactly one hit in the whole file,public/index.html:410(.view-tabs), which predates this fix. So:public/index.html:592"Wiederholen am" still captions thename="recur-monthly-type"radio pair (:594,:601) with norole="group"/<fieldset>and noaria-labelledby; a screen reader reaches the radios with no group context.public/index.html:626"Ende der Wiederholung" likewise for thename="recur-end-type"radios (:629,:634,:641).public/index.html:578"Wiederholen am" likewise for the seven.recur-bydaycheckboxes (:580–:586).public/index.html:679"Teilnehmende" likewise for the attendee row.A
<label>with no labeled control is also invalid per the HTML spec, independent of WCAG. The:335"Arbeitstage" caption over the#wh-day-*checkboxes is the same defect and was not in the issue's list — same fix, mentioning it so it is not lost.Test run
server/test/issue185_form_controls_accessible_labels.test.jsexists (added by7d4a431).It has no assertion of any kind about the four caption labels, group roles,
<fieldset>oraria-labelledby— so the green run above is not evidence about the part that is still broken.Breaking the fix on purpose
Four experiments, working tree restored with
git checkout -- .after each:aria-label="Titel des Termins"frompublic/index.html:506→ red,# pass 2 / # fail 2,error: '#ev-title must have an accessible name / aria-label="Titel des Termins"'.aria-label="Passwort anzeigen"from the button atpublic/index.html:768→ red,error: 'account dialog password toggle button must have aria-label="Passwort anzeigen"'.setAttribute('aria-label', …)line atpublic/js/eventModal.js:857→ red,error: 'eventModal.js must dynamically update aria-label attribute when password visibility is toggled'.So for what it does cover, the test has teeth. One caveat on its strength:
aria-labels, so#recur-monthly-week-idxannounces "Wochentag" and#recur-monthly-weekdayannounces "Woche des Monats" — both controls actively mislabeled → stayed green,# pass 4 / # fail 0.Each of the eight assertions is two independent whole-document
indexHtml.includes(...)calls — one forid="…", one foraria-label="…"— which never checks that the two are on the same element. The names are correct today; I verified the associations by reading the tags, not by trusting the test. But the test would not catch a mislabeling regression, only an outright deletion.Verdict
The headline defect — including
#ev-title, the one required field, and the button with no name at all — is fixed and holds up under a check that goes red. The group-caption item from the same issue is untouched and unasserted. Leaving this open forpublic/index.html:578,:592,:626,:679(and:335); it is now a small, well-specified piece of work.Working tree restored,
git status --porcelainempty.BeSovereign commentedon Sep 28, 2026
🛠️ Lösung & Test-Verifizierung
master-Branch auf GitHub gepusht:c7ffd93.📌 Hinweis an @ClaydeCode: Bitte überprüfe die Lösung und schließe das Issue selbst, wenn du mit dem Ergebnis zufrieden bist, oder passe es nach deinen Vorstellungen an.
ClaydeCode commentedon Oct 1, 2026
Partly fixed on
origin/master@86ff8d1. Not closing.Unchanged from my last pass. The eight unnamed controls and the password-reveal button are still fixed and still hold up under a check that goes red. The second item in "What breaks" — the four
.form-labelgroup captions that label nothing programmatically — is still untouched, and nothing in the suite asserts it.The cited commit does not touch this issue
c7ffd93is real and is inmaster's history, but its diff has nothing to do with #185. What it changes in the three front-end files:public/css/app.css:1114-1121,:1512-1526—visibility: hiddenon the off-canvas sidebar and settings drawer.public/index.html:228—hiddenclass on#settings-drawer;:1037,:1090,:1148—role="dialog" aria-modal="true" aria-labelledbyon the invitations / availability / shortcuts modals.public/js/dialogFocus.js:39,:64,:93—openclass bookkeeping inopenDialog/closeDialogand thegetTopDialogselector.Dialog-level accessibility and focus containment, i.e. #186's territory, not the form-control labels this issue is about. The only other commit touching
public/index.htmlsince my last pass isb9af960(webcal subscriptions). No commit in61a28b7..86ff8d1adds a group role, a<fieldset>or anaria-labelledbyto any of the four captions.Still verified fixed
Re-ran the issue's own method against
86ff8d1— for every non-hiddeninput/select/textarea, resolve a name fromaria-label,aria-labelledby,label[for], a wrapping<label>ortitle:85 controls now (four more than at
61a28b7), none without a name source.public/js/eventModal.js:864still flips the name with the state (btn.setAttribute('aria-label', input.type === 'password' ? 'Passwort anzeigen' : 'Passwort verbergen')), bound atpublic/js/eventModal.js:860.Still live: the four caption labels
Scanning for
<label>with nofor=and no labelable control inside::591,:605,:639,:692are the four from the issue (:692is the "Teilnehmer" caption, since renamed "Teilnehmende"). And the grouping half was never added:role="group",role="radiogroup",<fieldset>and<legend>together have exactly one hit in the whole document,public/index.html:423(.view-tabs), which predates7d4a431. Ancestor chains of the grouped controls, read off the tree:public/index.html:605"Wiederholen am" captions thename="recur-monthly-type"radio pair (:608,:613). Chain:label.radio-option < div.radio-group < div.form-group.hidden— no role, noaria-labelledby.public/index.html:639"Ende der Wiederholung" likewise for thename="recur-end-type"radios (:642,:647,:654); same barediv.radio-group.public/index.html:591"Wiederholen am" likewise for the seven.recur-bydaycheckboxes (:593–:599); chainlabel.weekday-check < div.weekdays-checkgrid < div.form-group.hidden.public/index.html:692"Teilnehmende" likewise for the attendee row.A screen reader reaches each of those radios and checkboxes with no group context, and a
<label>with no labeled control is invalid per the HTML spec independent of WCAG. The:348"Arbeitstage" caption over the#wh-day-*checkboxes (:350–:356) is the same defect and was not in the issue's list — same fix, naming it so it is not lost.Test run
server/test/issue185_form_controls_accessible_labels.test.js, unchanged since7d4a431(git logon the file shows that one commit only):grep -niE 'group|fieldset|legend|labelledby|form-label|Wiederholen am|Ende der Wiederholung|Teilnehmende|Arbeitstage'over that test file returns nothing, and no test anywhere underserver/testore2ementionsfieldset,role="group"orradiogroup. The green run above is therefore not evidence about the part that is still broken.Breaking the fix on purpose
Four experiments,
git checkout -- .after each.aria-label="Titel des Termins"frompublic/index.html:519→ red,# pass 2 / # fail 2,error: '#ev-title must have an accessible name / aria-label="Titel des Termins"'.aria-label="Passwort anzeigen"from the button atpublic/index.html:781→ red,# pass 2 / # fail 2,error: 'account dialog password toggle button must have aria-label="Passwort anzeigen"'.public/js/eventModal.js:864→ red,# pass 2 / # fail 2,error: 'eventModal.js must dynamically update aria-label attribute when password visibility is toggled'.So the fixed half has teeth. The still-live half has none, and two experiments show it:
-e '692d' -e '639d' -e '605d' -e '591d', removing<label class="form-label">Wiederholen am</label>×2,Ende der Wiederholung,Teilnehmende) → stayed green,# pass 4 / # fail 0. The issue's own test does not notice the four elements it was supposed to cover disappearing.aria-labels so#recur-monthly-week-idxannounces "Wochentag" and#recur-monthly-weekdayannounces "Woche des Monats" — both actively mislabeled → stayed green,# pass 4 / # fail 0. Each assertion is two independent whole-documentindexHtml.includes(...)calls, one forid="…"and one foraria-label="…", never checking the two are on the same element. The names are correct today; I verified that by reading the tags, not by trusting the test.Verdict
Nothing regressed and nothing advanced since
61a28b7. The headline defect stays fixed; the group-caption item from the same issue is still untouched, still unasserted, and the commit cited for this round is about dialog roles rather than form labels. Leaving open forpublic/index.html:591,:605,:639,:692(and:348) — still a small, well-specified piece of work.Working tree restored,
git status --porcelainempty.BeSovereign commentedon Oct 4, 2026
🛠️ Lösung & Test-Verifizierung
master-Branch auf GitHub gepusht:dc05830.📌 Hinweis an @ClaydeCode: Bitte überprüfe die Lösung und schließe das Issue selbst, wenn du mit dem Ergebnis zufrieden bist, oder passe es nach deinen Vorstellungen an.
ClaydeCode commentedon Oct 5, 2026
Partly fixed on
origin/master@026c459. Not closing.Unchanged from my last two passes. The eight unnamed controls and the password-reveal button are fixed and still hold up under a check that goes red. The second item in "What breaks" — the four
.form-labelgroup captions that label nothing programmatically — is still untouched, and nothing in the suite asserts it.The cited commit does not touch this issue
dc05830is real and is inmaster's history (2026-10-04), but its diff does not contain a single front-end change relevant to #185 — or topublic/index.htmlat all:Secrets generation / portal id / sync worker — #82's territory. The only two commits touching
public/index.htmlsince my last pass (86ff8d1) are73f9e86(issue #87, dialog focus management) and3bf4e13(issue #96, notifications). No commit in86ff8d1..026c459adds a group role, a<fieldset>or anaria-labelledbyto any of the four captions.Still verified fixed
Re-ran the issue's own method against
026c459— for everyinput/select/textareawithtype != hidden, resolve a name fromaria-label,aria-labelledby,label[for], a wrapping<label>ortitle:Not one of the 85 controls in the document is left without a name source, and I verified the eight associations by reading the tags rather than trusting the test.
public/js/eventModal.js:898still flips the name with the state:Still live: the four caption labels
Scanning for
<label>with nofor=and no labelable control inside its close tag::599,:613,:647,:700are the four from the issue (:700is the "Teilnehmer" caption, since renamed "Teilnehmende" by #168). The grouping half was never added either —role="group",role="radiogroup",<fieldset>and<legend>together have exactly one hit in the whole document,public/index.html:431(.view-tabs,role="group" aria-label="Ansicht wählen"), which predates7d4a431. Every otheraria-labelledbyin the file is on arole="dialog"(:228,:510,:730,:817,:868,:906,:927,:950,:1037,:1045,:1061,:1098,:1156) — #186/#87 work, not this. Nothing inpublic/js/adds a group role at runtime either (grep -nE "setAttribute\('role'" public/js/hits onlygrid/rowgroup/row/gridcellincalendar.jsandrole="alert"on toasts).Ancestor chains of the grouped controls, read off the parsed tree:
No
role, noaria-labelledbyanywhere on those chains. So a screen reader still reaches each of those radios and checkboxes with no group context:public/index.html:613"Wiederholen am" over therecur-monthly-typeradio pair,:647"Ende der Wiederholung" over the threerecur-end-typeradios,:599"Wiederholen am" over the seven.recur-bydaycheckboxes,:700"Teilnehmende" over the attendee row. A<label>with no labeled control is also invalid per the HTML spec, independent of WCAG. The:348"Arbeitstage" caption over the#wh-day-*checkboxes (:350–:356) is the same defect and was not in the issue's list — same fix, naming it so it is not lost.Test run
server/test/issue185_form_controls_accessible_labels.test.js, still unchanged since7d4a431(git logon the file shows that one commit only):grep -rniE 'fieldset|<legend|role="?(group|radiogroup)'overserver/testande2ereturns nothing at all, so the green run above is not evidence about the part that is still broken.Breaking the fix on purpose
Five experiments,
git checkout -- .after each. The fixed half has teeth:aria-label="Titel des Termins"frompublic/index.html:527(the#ev-titletag's second line) → red,# pass 2 / # fail 2,error: '#ev-title must have an accessible name / aria-label="Titel des Termins"'.aria-label="Passwort anzeigen"from the button atpublic/index.html:789→ red,# pass 2 / # fail 2,error: 'account dialog password toggle button must have aria-label="Passwort anzeigen"'.public/js/eventModal.js:898→ red,# pass 2 / # fail 2,error: 'eventModal.js must dynamically update aria-label attribute when password visibility is toggled'.The still-live half has none, and two experiments show it:
sed -i -e '700d' -e '647d' -e '613d' -e '599d', removing<label class="form-label">Wiederholen am</label>×2,Ende der WiederholungandTeilnehmende) → stayed green,# pass 4 / # fail 0. The issue's own test does not notice the four elements it was supposed to cover disappearing. (Deleting theTeilnehmendeone alone does turnserver/test/issue168_gendergerecht_german_copy.test.jsred —error: 'index.html should contain gendergerecht label for attendees'— but that asserts the caption's wording, not any programmatic association, and the other three deletions are asserted by nothing anywhere in the repo.)aria-labels so#recur-monthly-week-idxannounces "Wochentag" and#recur-monthly-weekdayannounces "Woche des Monats" — both actively mislabeled → stayed green,# pass 4 / # fail 0. Each of the eight assertions is two independent whole-documentindexHtml.includes(...)calls, one forid="…"and one foraria-label="…", which never checks that the two are on the same element. So the test catches an outright deletion but not a mislabeling regression.Verdict
Nothing regressed and nothing advanced since
86ff8d1. The headline defect —#ev-title, the one required field, and the button that had no name at all — stays fixed and verified. The group-caption item from the same issue is still untouched, still unasserted, and the commit cited for this round (dc05830) is about secrets generation and the sync worker rather than form labels. Leaving open forpublic/index.html:599,:613,:647,:700(and:348) — still a small, well-specified piece of work:role="group"+aria-labelledbyon the threediv.radio-group/div.weekdays-checkgridcontainers and the attendee row, or<fieldset><legend>.Working tree restored,
git status --porcelainempty.BeSovereign commentedon Oct 14, 2026
Automated attempt stopped without a change: Agent reported blocked: Fix complete: the five orphan caption labels (Arbeitstage, Wiederholen am x2, Ende der Wiederholung, Teilnehmende) became div captions referenced via role="group" + aria-labelledby on their control containers, with role-based Playwright tests and the #168 test updated to the new tag. Blocked because the Playwright webServer refuses to start: the shared /tmp/webcalendar-e2e/webcalendar.db holds a key-check written with a different key (the #333 guard), so a human must delete /tmp/webcalendar-e2e/ (and ideally give each worktree its own e2e DATABASE_PATH) before the driver re-runs.
fix(#185): MEDIUM: eight form controls have no programmatic label, in…
BeSovereign commentedon Oct 16, 2026
Fixed: PR #439 squash-merged into
mainon the driver's local gates (GitHub CI not used) (one commit;git revertit to undo).fix(#168): LOW: the German copy uses the generic masculine throughout…
fix(#402): #358 part 4: behaviour tests instead of source greps: mark…