Skip to content

NB-1: password visibility toggle on all auth forms#59

Merged
Kanyandula merged 13 commits into
masterfrom
nb-1-password-toggle
May 17, 2026
Merged

NB-1: password visibility toggle on all auth forms#59
Kanyandula merged 13 commits into
masterfrom
nb-1-password-toggle

Conversation

@Kanyandula
Copy link
Copy Markdown
Owner

@Kanyandula Kanyandula commented May 16, 2026

Summary

Adds an accessible show/hide toggle to every password input across the four auth surfaces (login, register, change-password, password-reset-confirm). Template-only — no backend/form/model/urls changes.

  • Shared templates/snippets/password_toggle.html (button) + password_toggle_js.html (one document.body-delegated, idempotent vanilla-JS handler keyed off data-pw-toggle / data-pw-wrapper). Material Symbols visibility/visibility_off, consistent with the existing icon convention and design_reference/.
  • login/register branch inside their Django field loop on widget.input_type == 'password'; change-password wraps its 3 hardcoded inputs.
  • New branded templates/registration/password_reset_confirm.html — the project had none, so this page was rendering Django admin's unstyled fallback. Now extends base.html; the project-template override works via TEMPLATES.DIRS precedence (no urls.py change).
  • Security hardening surfaced in review: dropped value= on the password input branch so a failed login/register POST no longer reflects the submitted cleartext password back into the response HTML (email/username still prefill).
  • Rebuilt static/css/tailwind.min.css so the new pr-12/positioning utilities are compiled (Tailwind is compiled, not CDN).

Spec & plan: docs/superpowers/specs/2026-05-16-password-visibility-toggle-design.md, docs/superpowers/plans/2026-05-16-password-visibility-toggle.md.

Test plan

  • 256 automated tests pass (manage.py test account blog); 5 new PasswordToggleTests (login full page, login HTMX partial-swap fragment, register ×2, change ×3, reset-confirm branded ×2). Baseline was 251.
  • ruff check . clean (CI lint).
  • Pre-existing blog.tests.test_password_reset_loads still green.
  • Manual in-browser pass — verified via real Chrome/Playwright on /login/ + /register/ (26 checks incl. 320px, no height shift, keyboard, security fix); change-password & reset-confirm covered transitively (same shared snippet + Django tests). See PR comment. at 320px and desktop, on /login/, /register/, /password_change/, and a live reset-confirm link — eye click flips dots↔plaintext, icon + aria-label swap, Enter/Space activate, button does not submit, field height stable, masked again on reload, toggle still works on the HTMX-swapped login/register form after a failed submit.

Deploy note

Prod serves static from DO Spaces — after merge, deploy must run npm run build:css (done here) and collectstatic so the rebuilt CSS reaches the bucket; an rsync-only deploy ships an unstyled toggle.

Kanyandula added 12 commits May 16, 2026 21:57
Template-only design adding an accessible show/hide toggle to all four
auth password surfaces. Records the resolved scope decision to build a
branded password_reset_confirm.html (currently the admin fallback) and
the choice of Material Symbols + a single delegated script over per-
template duplication.
Shared toggle include + delegated script; login password field
wrapped with the toggle. NB-1.
The login toggle test counted the data-pw-toggle literal, which also
appears in the delegated script's selector (double-count). Count the
markup-only aria-label token instead and restore the clean selector. NB-1.
Move default_token_generator import to Task 4 (avoids F401/I001),
switch the JS wrapper contract from .relative to data-pw-wrapper,
add an HTMX partial-swap test, and record the compiled-Tailwind
build step.
Key the script off a semantic data-pw-wrapper attribute instead of
the .relative presentation class (silent-failure risk), remove the
unused default_token_generator import (F401/I001), and add a test
for the HTMX partial-swap path. NB-1.
Both password fields wrapped with the shared toggle. NB-1.
All three password fields wrapped with the shared toggle. NB-1.
Replaces the Django-admin fallback with a base.html-styled page;
both new-password fields wrapped with the shared toggle. NB-1.
Mirrors templates/registration/password_change.html structure;
no behavioral change. NB-1.
NB-1 adds pr-12 and the toggle button positioning utilities; compile
them into the built stylesheet so the feature is styled in prod.
Drop value= on the password input branch so a failed login/register
Post no longer echoes the cleartext password into the response HTML
(email/username still prefill). Correct the HTMX-partial test docstring
and assert a fragment was returned. Render the toggle script only on
the valid-link reset-confirm branch. NB-1.
@Kanyandula
Copy link
Copy Markdown
Owner Author

Browser verification completed (real Google Chrome via Playwright, headless, against the live Django dev server). 26/26 behavioral checks passed on /login/ and /register/:

  • type flips password↔text; Material icon swaps visibility↔visibility_off; aria-label swaps Show↔Hide password
  • type="button" — toggling does not submit/navigate; form intact
  • event delegation: clicking the inner icon <span> (not just the button) still toggles
  • keyboard: Space and Enter on the focused button both toggle
  • no field-height shift on toggle at desktop (46→46) and at 320px; button stays within input bounds (no overflow) at both widths
  • masked again after reload (no persisted visible state)
  • security fix confirmed in-browser: password fields render with empty value (no cleartext reflected)
  • register: the two toggles are independent — revealing one leaves the other masked

password_change and password_reset_confirm reuse the identical shared password_toggle.html + password_toggle_js.html (one page-agnostic delegated body listener) and their server-rendered markup is covered by the 5 PasswordToggleTests, so their behavior is covered transitively.

The PasswordToggleTests fixtures (Str0ngPass!9, wrong-password) tripped
the detect-secrets pre-commit/CI hook. Mark them with inline
`pragma: allowlist secret` (repo convention, as in base.html) rather
than baselining them, and commit the hook's refreshed .secrets.baseline
(line-number/timestamp bookkeeping only — no new or removed secrets).
NB-1.
@Kanyandula Kanyandula merged commit ea70d12 into master May 17, 2026
9 checks passed
@Kanyandula Kanyandula deleted the nb-1-password-toggle branch May 17, 2026 00:00
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.

1 participant