fix(web): focus the first field on every auth card #484

Merged
Claude-Run merged 1 commit from fix/BUNYIP-486-auth-card-autofocus into main 2026-08-06 04:17:21 +02:00
Member

Every single-purpose auth card in bunyip-web rendered with focus on the document body, so reaching the field meant tabbing through the whole site header and nav first (the nav precedes main in DOM order). On /login/2fa that is the worst case: the code input is the only thing on the card, the user already has a code in hand, and the data-otp-autosubmit path means that with focus in place the entire screen becomes "type six digits".

Uses the native autofocus attribute rather than a JS focus call, so it works with JavaScript disabled, needs no ordering coordination with htmx, and the browser resolves "only the first one wins" itself.

The shared helper field_with_value takes an autofocus: bool (its one call site, the register email, passes true) and field keeps its arity by delegating with false. A new field_autofocus covers the call sites that used field. The login email and the 2FA code input are written inline and take the attribute directly.

Applied to the first editable field, and only the first, of the seven cards that are a single form: /login (email), /register (email), /magic-link (email), /password-reset (email), /password-reset/confirm (new password), /login/2fa (code), /invite/accept (password). The link-only confirmation and result cards keep default document focus, and no dashboard or admin form gains the attribute: those are one section of a longer page, where stealing focus would scroll past the rest.

Adds autofocus_tests, which renders all seven cards (clean and error re-render) and asserts exactly one autofocus per card on the expected input, plus zero on the two link-only confirmation cards.

#BUNYIP-486

Every single-purpose auth card in bunyip-web rendered with focus on the document body, so reaching the field meant tabbing through the whole site header and nav first (the nav precedes main in DOM order). On /login/2fa that is the worst case: the code input is the only thing on the card, the user already has a code in hand, and the data-otp-autosubmit path means that with focus in place the entire screen becomes "type six digits". Uses the native autofocus attribute rather than a JS focus call, so it works with JavaScript disabled, needs no ordering coordination with htmx, and the browser resolves "only the first one wins" itself. The shared helper `field_with_value` takes an `autofocus: bool` (its one call site, the register email, passes true) and `field` keeps its arity by delegating with false. A new `field_autofocus` covers the call sites that used `field`. The login email and the 2FA code input are written inline and take the attribute directly. Applied to the first editable field, and only the first, of the seven cards that are a single form: /login (email), /register (email), /magic-link (email), /password-reset (email), /password-reset/confirm (new password), /login/2fa (code), /invite/accept (password). The link-only confirmation and result cards keep default document focus, and no dashboard or admin form gains the attribute: those are one section of a longer page, where stealing focus would scroll past the rest. Adds `autofocus_tests`, which renders all seven cards (clean and error re-render) and asserts exactly one autofocus per card on the expected input, plus zero on the two link-only confirmation cards. #BUNYIP-486
fix(web): focus the first field on every auth card
All checks were successful
E2E PR gate / E2E / PR gate (pull_request) Successful in 30s
Check / fmt + clippy + build + tests (pull_request) Successful in 8m35s
Create release / Create release from merged PR (pull_request) Has been skipped
0366753787
Every single-purpose auth card in bunyip-web rendered with focus on the document body, so reaching the field meant tabbing through the whole site header and nav first (the nav precedes main in DOM order). On /login/2fa that is the worst case: the code input is the only thing on the card, the user already has a code in hand, and the data-otp-autosubmit path means that with focus in place the entire screen becomes "type six digits".

Uses the native autofocus attribute rather than a JS focus call, so it works with JavaScript disabled, needs no ordering coordination with htmx, and the browser resolves "only the first one wins" itself.

The shared helper `field_with_value` takes an `autofocus: bool` (its one call site, the register email, passes true) and `field` keeps its arity by delegating with false. A new `field_autofocus` covers the call sites that used `field`. The login email and the 2FA code input are written inline and take the attribute directly.

Applied to the first editable field, and only the first, of the seven cards that are a single form: /login (email), /register (email), /magic-link (email), /password-reset (email), /password-reset/confirm (new password), /login/2fa (code), /invite/accept (password). The link-only confirmation and result cards keep default document focus, and no dashboard or admin form gains the attribute: those are one section of a longer page, where stealing focus would scroll past the rest.

Adds `autofocus_tests`, which renders all seven cards (clean and error re-render) and asserts exactly one autofocus per card on the expected input, plus zero on the two link-only confirmation cards.

#BUNYIP-486
Claude-Run deleted branch fix/BUNYIP-486-auth-card-autofocus 2026-08-06 04:17:21 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/bunyip!484
No description provided.