Put the otp input after its boxes
A seventh box appeared when the field took focus. daisyUI positions each otp box with nth-child, which counts *every* child of the container, not just the spans. With the input as the first child, all six boxes shifted one stride right and the container matched :has(>span:nth-child(7)), so it grew to seven strides wide. The phantom box was the ::after active-box marker -- transparent until :focus-within gives it an outline -- sitting in the empty stride that the off-by-one had opened up. The input goes last, which is also how daisyUI's own examples order it. A test counts the boxes ahead of the input so this cannot come back. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -265,6 +265,15 @@ class TwoFactorPageTests(TestCase):
|
||||
self.assertContains(self.response, 'name="code"')
|
||||
self.assertContains(self.response, "otp otp-lg")
|
||||
|
||||
def test_the_input_comes_after_the_boxes(self):
|
||||
# daisyUI places each box with nth-child, which counts every child. With the input
|
||||
# first, all six boxes shift a stride right, the container grows to seven strides
|
||||
# and the ::after focus marker appears as a phantom seventh box.
|
||||
html = self.response.content.decode()
|
||||
otp = html[html.index('class="otp otp-lg"') : html.index('name="code"')]
|
||||
|
||||
self.assertEqual(otp.count("<span></span>"), 6)
|
||||
|
||||
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"')
|
||||
|
||||
@@ -43,20 +43,26 @@
|
||||
{% with icon=field|field_icon %}
|
||||
{% if field|is_otp %}
|
||||
{% comment %}
|
||||
daisyUI's otp is one input overlaid on the boxes, and it sizes itself from
|
||||
its `span` children — so the sr-only label goes OUTSIDE the container, or it
|
||||
would count as a seventh box and throw the width off.
|
||||
daisyUI's otp is one input overlaid on the boxes. Child order is load-bearing:
|
||||
it places each box with `nth-child`, which counts *every* child, not just the
|
||||
spans — so the input must come LAST. Put it first and all six boxes shift one
|
||||
stride right, the container matches :has(>span:nth-child(7)) and grows to
|
||||
seven strides, and the ::after active-box marker (invisible until focus) shows
|
||||
up in the gap as a phantom seventh box.
|
||||
|
||||
For the same reason the sr-only label lives outside the container: another
|
||||
child would throw the count off again.
|
||||
{% endcomment %}
|
||||
{% 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>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
{{ field|as_otp }}
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
</div>
|
||||
</div>
|
||||
{% elif icon %}
|
||||
|
||||
Reference in New Issue
Block a user