Skip to content

fix: #5486 give MultiWidget fields a working label association - #5487

Merged
mauromsl merged 1 commit into
openlibhums:masterfrom
alefnsc:fix/5486-multiwidget-label-for
Sep 3, 2026
Merged

fix: #5486 give MultiWidget fields a working label association#5487
mauromsl merged 1 commit into
openlibhums:masterfrom
alefnsc:fix/5486-multiwidget-label-for

Conversation

@alefnsc

@alefnsc alefnsc commented Aug 26, 2026

Copy link
Copy Markdown

closes #5486

The bug

templates/admin/elements/forms/field.html renders <label for="{{ field.id_for_label }}">. Django returns the empty string from BoundField.id_for_label whenever the widget is a MultiWidget, because there is no single control to point at — so the partial emits <label for="">, associated with nothing, and the field has no accessible name.

Not cosmetic on the registration form: with the default CAPTCHA_TYPE = "simple_math", django-simple-math-captcha's MathCaptchaField is a MultiWidget, so the required captcha input is announced as nothing. A screen-reader user cannot complete registration.

The partial is included from ~20 templates, so any other MultiWidget field (SplitDateTimeWidget, etc.) is affected wherever it renders.

The change

One line: fall back to the first subwidget, which Django ids as <auto_id>_0.

<label for="{% if field.id_for_label %}{{ field.id_for_label }}{% else %}{{ field.auto_id }}_0{% endif %}">

Why it is safe for the other ~20 include sites

Any field that already has an id_for_label takes the existing branch, so its output is byte-identical. Verified by rendering the patched line against both a plain field and a MultiWidget:

email    -> for="id_email"      (unchanged)
captcha  -> for="id_captcha_0"  (was for="")

The reproduction in #5486 is standalone — plain Django, no Janeway needed — if you want to confirm before and after yourself.

Deliberately not included

For the captcha specifically the question (<span class="captcha-question">) sits outside the label, so even with a correct for the accessible name is just "Answer this question:" — the user is told to answer a question they are never read. Fixing that by wrapping {{ field }} in the label would be wrong here, because a MultiWidget that renders several real inputs (e.g. SplitDateTimeWidget) would end up with two controls under one label. It likely belongs in the captcha field's own rendering rather than this shared partial. Happy to follow up separately if you'd like it addressed.

…tion

`BoundField.id_for_label` is the empty string for any MultiWidget, so
`admin/elements/forms/field.html` emitted `<label for="">` — associated with
nothing. Every field rendered through this partial with a MultiWidget-based
widget therefore had no accessible name.

It is not cosmetic on the registration form: with the default
`CAPTCHA_TYPE = "simple_math"`, django-simple-math-captcha's MathCaptchaField
is a MultiWidget, so the required captcha input is announced as nothing and a
screen-reader user cannot complete registration.

Falls back to the first subwidget, which Django ids as `<auto_id>_0`. Fields
that already have an `id_for_label` take the existing branch and render
byte-identically, so the ~20 templates including this partial are unaffected.

Verified by rendering the patched line against both field types:

    email    -> for="id_email"      (unchanged)
    captcha  -> for="id_captcha_0"  (was for="")
@mauromsl
mauromsl merged commit 2c986c0 into openlibhums:master Sep 3, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

MultiWidget fields render <label for="">, leaving them with no accessible name

3 participants