Give every auth button an icon, and lay out the password and MFA screens

The button element now takes an `icon`, so a page gets one by passing
icon="name" rather than by hand-rolling its own button markup. Every button on
the account and MFA screens carries one; a test walks each page and asserts no
button is left bare.

Change password: labels dropped (allauth already sets a placeholder on each
field), the current password set apart from the new pair, help text kept on the
new password, and Forgot Password promoted from a bare link to an accent button.

MFA management: recovery-code actions are now ranked -- View is primary, Download
and Generate are outline. Generate silently invalidates the codes you already
hold, so it must not read as the obvious thing to click. Panel actions get
breathing room from the body text (card-actions mt-4).

Viewing recovery codes: Download and Generate sit side by side instead of
stacking.

TOTP activate: the code box loses its heading -- an otp field never takes a
visible label, the boxes say what they are -- and the authenticator secret gets
margin around it, since it is copied out by hand.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-13 23:53:35 +02:00
parent 0eb6838cba
commit bf86654e72
16 changed files with 4705 additions and 9 deletions

View File

@@ -1,4 +1,4 @@
{% load allauth %}
{% load allauth lucide %}
{% comment %}
allauth tags every button it renders, and mapping those tags is what styles the MFA
@@ -9,13 +9,14 @@
{% endcomment %}
{% comment %} djlint:off {% endcomment %}
<{% if attrs.href %}a href="{{ attrs.href }}"{% else %}button{% endif %}
class="btn {% if attrs.tags and 'danger' in attrs.tags %}btn-error{% elif attrs.tags and 'link' in attrs.tags %}btn-link{% elif attrs.tags and 'secondary' in attrs.tags %}btn-outline btn-neutral{% elif attrs.tags and 'outline' in attrs.tags %}btn-outline btn-primary{% else %}btn-primary{% endif %}"
class="btn gap-2 {% if attrs.tags and 'danger' in attrs.tags %}btn-error{% elif attrs.tags and 'link' in attrs.tags %}btn-link{% elif attrs.tags and 'secondary' in attrs.tags %}btn-outline btn-neutral{% elif attrs.tags and 'outline' in attrs.tags %}btn-outline btn-primary{% else %}btn-primary{% endif %}"
{% if attrs.form %}form="{{ attrs.form }}"{% endif %}
{% if attrs.id %}id="{{ attrs.id }}"{% endif %}
{% if attrs.name %}name="{{ attrs.name }}"{% endif %}
{% if attrs.value %}value="{{ attrs.value }}"{% endif %}
{% if attrs.type %}type="{{ attrs.type }}"{% endif %}
>
{% if attrs.icon %}{% lucide attrs.icon size=16 %}{% endif %}
{% slot %}
{% endslot %}
</{% if attrs.href %}a{% else %}button{% endif %}>

View File

@@ -6,7 +6,7 @@
character by character and must not misread.
{% endcomment %}
{{ attrs.errors }}
<div class="form-control my-3 w-full">
<div class="form-control my-6 w-full">
{% if slots.label %}
<label class="label" for="{{ attrs.id }}">
<span class="label-text">{% slot label %}{% endslot %}</span>

View File

@@ -35,7 +35,9 @@
<span class="label-text">{{ field.label }}</span>
</label>
{% else %}
{% if not attrs.unlabeled %}
{# An otp box never takes a visible label — the boxes say what they are, and a
heading above them just crowds the field. It keeps an sr-only one below. #}
{% if not attrs.unlabeled and not field|is_otp %}
<label class="label" for="{{ field.id_for_label }}">
<span class="label-text">{{ field.label }}</span>
</label>
@@ -53,7 +55,7 @@
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 %}
<label class="sr-only" for="{{ field.id_for_label }}">{{ field.label }}</label>
<div class="flex justify-center">
<div class="otp otp-lg" data-otp>
<span></span>

View File

@@ -1,8 +1,9 @@
{% load allauth %}
<div class="card border border-base-300 bg-base-100 my-4">
<div class="card border border-base-content/20 bg-base-100 my-4">
<div class="card-body gap-2">
{% if slots.title %}<h2 class="card-title text-lg">{% slot title %}{% endslot %}</h2>{% endif %}
<div>{% slot body %}{% endslot %}</div>
{% if slots.actions %}<div class="card-actions justify-end">{% slot actions %}{% endslot %}</div>{% endif %}
{% if slots.actions %}
<div class="card-actions mt-4 justify-end">{% slot actions %}{% endslot %}</div>{% endif %}
</div>
</div>