Skip to content

feat(email): unify verify / reset / claim email layouts#35

Merged
alejandro-runner merged 2 commits intosynvya-stagingfrom
feat/email-visual-consistency
Apr 22, 2026
Merged

feat(email): unify verify / reset / claim email layouts#35
alejandro-runner merged 2 commits intosynvya-stagingfrom
feat/email-visual-consistency

Conversation

@alejandro-runner
Copy link
Copy Markdown
Member

Summary

Apply the visual language of the new team-invite email to the three remaining transactional emails — verification, password reset, and account claim — so every Synvya email recipients see shares the same header, button, and footer treatment.

Not a card rewrite. These flows have no subject entity to present (unlike the team invite, which shows team avatar + display name), and over-stylising reset/verify emails can read as phishing. The goal is consistency, not maximalism.

Changes

  • api/src/email_service.rs — new shared basic_email_html(heading, intro, cta_label, cta_url, footer_note) helper. verification_email_html, password_reset_html, and claim_email_html now call it. All interpolated values HTML-escaped.
  • Net: −69 / +61 lines (the three templates collapsed into calls against the shared helper, removed per-template duplicated styles).

Before / After

Before: left-aligned teal h1, left-aligned plain paragraph, left-aligned button with 4px radius, left-aligned copy-link, left-aligned footer. Each template duplicated the full HTML scaffold.

After: centred Synvya wordmark, centred heading (h2, 20px), centred intro paragraph, centred rounded-pill button (8px radius, 14×36 padding), centred copy-link fallback, centred muted footer.

Shared with the team-invite email: same background (#f5f5f5), same max-width (520px), same teal (#00B488), same button geometry.

Out of scope

Test plan

  • `cargo check --workspace` clean
  • `cargo clippy -p keycast_api --all-targets -- -D warnings` clean
  • `cargo test -p keycast_api --lib` — 62 passed
  • Staging smoke: trigger a registration (verify), a password-reset request, and an admin-preload claim; confirm each email renders the shared layout in Gmail + Apple Mail.

🤖 Generated with Claude Code

alejandro-runner and others added 2 commits April 21, 2026 19:51
Align the three remaining transactional emails (verification, password
reset, account claim) with the visual language of the new team-invite
email: centred Synvya wordmark header, centred heading + intro, rounded
pill button, copy-link fallback, muted footer note.

Done via a small shared helper `basic_email_html` that takes heading,
intro, CTA label, CTA URL, and footer note. Keeps the emails minimal —
no card, no stylised subject — since these flows have no entity to
present and overly stylised reset/verify emails can read as phishing.

All interpolated values are HTML-escaped.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Claim emails now render the same card layout as team invites when the
preloaded account has kind-0 metadata: avatar, display name (restaurant
name), and "Claim your login for {email}". Falls back to the plain
single-CTA layout when kind-0 is unavailable.

- Extend send_claim_email signature with account_display_name and
  account_picture Options; threaded through all three EmailSender impls
  and the EmailService facade.
- claim_email_html: branches to card layout when either field is set;
  otherwise delegates to basic_email_html. HTML-escapes interpolated
  values; avatar URL restricted to http/https via safe_http_url.
- admin.rs batch claim handler: best-effort kind-0 fetch from
  BUNKER_RELAYS for each user_pubkey (3s timeout, handled entirely in
  fetch_profile_metadata) before sending. Silent fallback on failure so
  batch throughput degrades gracefully.

Password-reset and verification emails intentionally left on the plain
layout — no entity to present, and over-stylised reset mail reads as
phishing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@alejandro-runner
Copy link
Copy Markdown
Member Author

Added a second commit (304f509) with a card layout for the claim email:

  • Extends send_claim_email with account_display_name / account_picture options.
  • claim_email_html renders the invite-style card (avatar + restaurant name + "Claim your login for {email}") when kind-0 is available; otherwise falls back to the plain single-CTA layout.
  • admin.rs batch claim handler fetches kind-0 for each preloaded pubkey (3s timeout, silent fallback) before sending.

Rationale: preloaded accounts are onboarded with kind-0 set up, so the card gives the recipient concrete brand context ("yes, this claim email is for my restaurant") rather than a generic "account ready" prompt. Reset/verify remain plain on purpose — no subject entity, and over-stylised password emails can read as phishing.

@alejandro-runner alejandro-runner merged commit 01eca16 into synvya-staging Apr 22, 2026
@alejandro-runner alejandro-runner deleted the feat/email-visual-consistency branch April 22, 2026 03:18
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