Drop the placeholder from the otp field

allauth sets placeholder="Code" on the field. Grey text sitting inside the otp
boxes reads as an already-typed code, so it goes -- the sr-only label outside the
box still names the field.

Suppressed with placeholder=False rather than by deleting the key: as_widget()
merges the widget's own attrs back in at render time, so popping it from a copy
does nothing. Django's attribute template omits attrs whose value is False.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-13 23:11:06 +02:00
parent addfc61a2c
commit fa213d95ee
3 changed files with 20 additions and 1 deletions

View File

@@ -265,6 +265,10 @@ class TwoFactorPageTests(TestCase):
self.assertContains(self.response, 'name="code"')
self.assertContains(self.response, "otp otp-lg")
def test_the_otp_field_has_no_placeholder(self):
# allauth sets placeholder="Code"; inside the boxes it reads as a typed-in code.
self.assertNotContains(self.response, 'placeholder="Code"')
def test_cancel_sits_beside_sign_in_and_is_not_primary(self):
self.assertContains(self.response, '<button class="btn gap-2" type="submit" form="logout-from-stage">')
self.assertContains(self.response, '<button class="btn btn-primary gap-2" type="submit">')

View File

@@ -68,6 +68,21 @@ def is_otp(field):
return field.name in OTP_FIELDS
@register.filter
def as_otp(field):
"""Render the code input for the boxed otp layout.
No daisy classes — the wrapping div carries them — and no placeholder: allauth sets
one ("Code"), and grey text sitting inside the boxes reads as an already-typed code.
The label lives outside the box as an sr-only element, so nothing is lost.
False, not a pop: as_widget() merges the widget's own attrs back in, so deleting the
placeholder from a copy achieves nothing. Django's attribute template omits any attr
whose value is False, which is the only way to actually suppress one.
"""
return field.as_widget(attrs={"class": "", "placeholder": False})
@register.filter
def field_icon(field):
return FIELD_ICONS.get(field.name, "")

View File

@@ -50,7 +50,7 @@
{% if attrs.unlabeled %}<label class="sr-only" for="{{ field.id_for_label }}">{{ field.label }}</label>{% endif %}
<div class="flex justify-center">
<div class="otp otp-lg" data-otp>
{{ field|daisy:"" }}
{{ field|as_otp }}
<span></span>
<span></span>
<span></span>