2026-07-09-mailhub-landing-page-design.md 7.2 KB

MailHub Public Landing Page Design

Goal

Replace the current “anonymous root URL → login/admin app” experience with a developer-first public marketing homepage (Resend / Mailjet-inspired). Unauthenticated visitors see product value, API/SMTP/Webhook integration paths, and bilingual CTAs; authenticated users still reach the admin console at /.

Product Decisions

Item Choice
Audience Developers first
Scope (MVP) Single-page landing with in-page anchors
Language zh-CN / en-US toggle (localStorage)
Pricing None
Implementation Static landing entry (Vite landing build), decoupled from React admin bundle
Visual system Same Modern SaaS indigo tokens as admin (#4F46E5, canvas, ink)

Non-Goals (MVP)

  • Multi-page docs site, blog, changelog site
  • Pricing tables or billing
  • Embedding the landing page inside the React admin SPA
  • Changing send/webhook business APIs
  • Fake customer logos or fabricated social proof

Current Behavior (to change)

  • Unauthenticated browser traffic to protected pages redirects to /login.
  • / maps to public/index.html (admin app).
  • Login/register assets are public; admin is session-gated.

Routing & Auth

Path Unauthenticated Authenticated
GET / Serve landing (landing.html) Serve admin (index.html)
/login, /register, /forgot-password, /resend-verification, /reset-password login.html Optional redirect to admin if already logged in (existing)
/app (optional alias) Redirect to /login Admin console

Server rules (src/server.js)

  1. Treat landing HTML/CSS/JS as login-class public assets (no session required), same pattern as /login.html.
  2. For GET / (or mapped /index root):
    • If getRequestUser is present → index.html (admin).
    • Else → landing.html.
  3. Do not redirect anonymous / to /login.
  4. Keep /api/* auth unchanged.
  5. Caching: Root HTML responses that depend on session (GET / landing vs admin) MUST be non-cacheable: e.g. Cache-Control: private, no-store (and prefer the same for landing.html / index.html HTML documents) so proxies/browsers do not stick a visitor on the wrong shell after login.
  6. /app alias: out of MVP; primary CTAs use /register and /login only.

Information Architecture (single page)

Sticky header

  • Brand: MH mark + MailHub
  • Anchor nav: Features · Integrate · API · SMTP · Webhooks
  • Language toggle: 中文 / EN
  • CTAs: Log in (secondary), Sign up / Get started (primary)

Hero

  • Developer-oriented headline + subcopy (self-hosted, DKIM/SPF/DMARC, logs, webhooks)
  • Primary CTA → /register
  • Secondary CTA → #api (scroll)
  • Visual: dark code panel with curl POST /api/send sample

Trust strip (light)

  • Text chips only: Self-hosted · DKIM · SPF · DMARC · Delivery webhooks
  • No fake logos

Features grid (5–6 cards)

  1. Domain + DNS automation
  2. SMTP Submission
  3. HTTP Send API + tokens
  4. Delivery tracking & logs
  5. Webhooks (sent / bounced / failed)
  6. Multi-user isolation

Integrate (3 columns)

Column Content
API Token auth, POST /api/send, field list, link to register
SMTP Host/ports aligned with product submission config, example snippet
Webhooks Event types + signature headers summary, “configure after login”

Deep sections

  • #api — curl + JSON body + Authorization header (match existing token docs field names)
  • #smtp — ports and connection notes consistent with runtime submission
  • #webhooks — event type table + X-MailHub-Signature one-liner

Closing CTA

  • Headline + Register + Log in
  • No pricing

Footer

  • Product name, Log in, Register, optional GitHub link
  • Copyright line

Static sample content policy

Landing is fully static (no authenticated API on first paint).

  • Use placeholders for host/base URL: https://mail.example.com, host mail.example.com (or “your MailHub host” in prose).
  • Document default public SMTP ports from product defaults: 25 (smtp), 587 (smtp/STARTTLS), 465 (smtps), 2525 (smtp). Do not hardcode a specific production hostname from env.
  • Forbidden in MVP: unauthenticated config API for landing; build-time injection of production .env into landing assets.
  • API sample fields match product helpers: from, to, subject, text; header Authorization: Bearer <USER_API_TOKEN>.
  • Webhook samples: events sent | bounced | failed; payload type email.sent / email.bounced / email.failed; header X-MailHub-Signature: t=…,v1=….

Visual Design

  • CSS variables aligned with admin redesign:
    • --mh-primary: #4F46E5
    • canvas / ink / border / radius / shadow
  • Light marketing layout; dark code blocks for developer feel
  • Comfortable spacing; responsive stack; mobile nav collapse or wrap
  • System font stack (no CDN Inter required)

Internationalization

  • Dictionary module for zh-CN and en-US
  • Persist choice in localStorage (key e.g. mailhub-landing-locale)
  • Default: navigator.language starts with zh → zh-CN, else en-US
  • Toggle updates all data-i18n nodes and document.documentElement.lang
  • Code identifiers remain English; comments/labels in dictionary may switch

Engineering

Build (committed path)

Vite multi-page entry only (same pipeline as index / login):

  • Source: repo-root landing.html + src/frontend/landing/main.ts + landing.css + i18n dictionary
  • vite.config.ts input key: landing
  • Output: public/landing.html + hashed assets under public/assets/

Do not maintain a parallel hand-written-only public/landing.html source of truth outside the Vite build.

Client behavior

  • Smooth scroll for header anchors
  • Copy-to-clipboard on code samples
  • No API calls required for first paint

Tests

  • Server: anonymous GET / body/title or content-type indicates landing (or path resolution helper unit test)
  • Server: authenticated session GET / resolves to admin index
  • Public: landing assets reachable without cookie
  • Optional: i18n dictionary has both locales for required keys

File Map (expected)

Path Role
landing.html (source) Landing markup
src/frontend/landing/main.ts i18n, interactions
src/frontend/landing/landing.css Styles
src/frontend/landing/i18n.js Copy dictionary
vite.config.ts landing input
src/server.js Root routing + public asset allowlist
test/server-*.test.js Route tests
public/landing.html + assets Build output

Delivery Order

  1. Landing static structure + CSS tokens + zh/en copy
  2. Landing JS (i18n, scroll, copy)
  3. Vite entry + build
  4. Server public routing for / and assets
  5. Tests + build gate
  6. Deploy per Agents.md

Success Criteria

  • Anonymous / shows marketing landing, not login form as the only chrome
  • Bilingual toggle works and persists
  • Register/Login CTAs work
  • Logged-in / still loads admin console
  • Existing auth and API behavior unchanged
  • npm test and npm run build pass

Open Follow-ups

  • Full /docs site
  • Pricing page
  • Marketing analytics
  • Open Graph / social meta images