Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .changeset/email-autocomplete-hints.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
'ePDS': patch
---

Sign-in and recovery email fields now work more reliably with browser autofill and password managers.

**Affects:** End users

**End users:** email fields now identify themselves to browsers as email addresses, while the same sign-in field identifies itself as a username after switching to handle entry. This helps browsers and password managers offer the right saved value in each mode.
2 changes: 2 additions & 0 deletions e2e/step-definitions/atproto-login-button.steps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ Then(
const input = page.locator('#email')
await expect(input).toHaveAttribute('type', 'text')
await expect(input).toHaveAttribute('name', 'handle')
await expect(input).toHaveAttribute('autocomplete', 'username')
await expect(input).toHaveAttribute('placeholder', 'you.bsky.social')
await expect(page.locator('label[for="email"]')).toHaveText('Handle')
},
Expand All @@ -88,6 +89,7 @@ Then(
const input = page.locator('#email')
await expect(input).toHaveAttribute('type', 'email')
await expect(input).toHaveAttribute('name', 'email')
await expect(input).toHaveAttribute('autocomplete', 'email')
await expect(input).toHaveAttribute('placeholder', 'you@example.com')
await expect(page.locator('label[for="email"]')).toHaveText(
'Enter your email address',
Expand Down
1 change: 1 addition & 0 deletions packages/auth-service/src/routes/account-login.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ function renderLoginForm(opts: { csrfToken: string; error?: string }): string {
<div class="field">
<label for="email">Email address</label>
<input type="email" id="email" name="email" required autofocus
autocomplete="email"
placeholder="you@example.com">
</div>
<button type="submit" class="btn-primary">Continue with email</button>
Expand Down
3 changes: 3 additions & 0 deletions packages/auth-service/src/routes/login-page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -664,6 +664,7 @@ export function renderLoginPage(opts: {
<div class="field">
<label for="email">Enter your email address</label>
<input type="email" id="email" name="email" required autofocus
autocomplete="email"
placeholder="you@example.com"
value="${escapeHtml(opts.loginHint)}">
</div>
Expand Down Expand Up @@ -1035,6 +1036,7 @@ export function renderLoginPage(opts: {
emailInput.type = 'text';
emailInput.placeholder = 'you.bsky.social';
emailInput.name = 'handle';
emailInput.autocomplete = 'username';
emailInput.value = '';
// Browser's built-in type="email" validation would block valid
// handles; remove it for handle mode.
Expand All @@ -1046,6 +1048,7 @@ export function renderLoginPage(opts: {
emailInput.type = 'email';
emailInput.placeholder = 'you@example.com';
emailInput.name = 'email';
emailInput.autocomplete = 'email';
emailInput.value = '';
emailInput.setAttribute('required', '');
sendOtpBtn.textContent = 'Continue';
Expand Down
1 change: 1 addition & 0 deletions packages/auth-service/src/routes/recovery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,7 @@ export function renderRecoveryForm(opts: {
<div class="field">
<label for="email">Backup email address</label>
<input type="email" id="email" name="email" required autofocus
autocomplete="email"
placeholder="backup@example.com">
</div>
<button type="submit" class="btn-primary">Send recovery code</button>
Expand Down
Loading