MailHub UI Visual Redesign Design
Goal
全面升级 MailHub 管理台与登录/注册页的视觉系统,使其从「默认 Ant Design 后台模板」提升为辨识度高的 Modern SaaS 控制台,同时不改动后端 API、权限模型、业务状态机与核心信息架构。
Confirmed Product Decisions
| Decision |
Choice |
| Scope |
Full visual system redesign (theme + layout shell + component patterns + key pages) |
| Auth pages |
Included; same visual language as admin console |
| Visual style |
Modern SaaS — light canvas, indigo primary, airy product feel (Resend / Railway-like) |
| Shell |
Dark left sidebar (full text labels) + light top bar + content canvas |
| Density |
Comfortable — larger type, generous whitespace, breathable cards |
| Implementation approach |
Design system + shell upgrade on existing React + Ant Design v5 |
| Dark mode |
Out of scope for this redesign |
Non-Goals
- New features, new routes, or new API endpoints
- Replacing Ant Design / React / Vite stack
- Full custom component library rebuild
- Dark mode theme
- Changing domain DNS workflow semantics, token lifecycle, or admin permissions
- Rewriting analytics models or i18n message semantics (minor chrome strings only)
Architecture
Continue the current React admin app (src/frontend/, src/layouts/, src/pages/, src/components/) built by Vite into public/. Visual upgrades land as:
- Design tokens — CSS custom properties + Ant Design
ConfigProvider theme tokens
- Shell redesign —
AdminLayout and auth shell (AuthApp)
- Shared presentation components — PageHeader, MetricCard, SectionCard, StatusPill, EmptyState, CodeBlock
- Page restyles — recompose existing page markup around the new patterns without changing data loading or handlers
Business logic remains in existing model modules (*-model.js), api.ts, and page containers.
Admin and auth are separate Vite entry points (src/frontend/main.tsx → App.tsx, src/frontend/auth/main.tsx → AuthApp). Each mounts its own ConfigProvider. Extract a shared theme/token module (e.g. src/frontend/theme.ts + CSS variables in styles.css) and apply it in both entry trees so brand stays consistent without implying a single React tree.
theme.ts + styles.css (shared tokens)
├── Auth entry: ConfigProvider → I18nProvider → AuthApp
└── Admin entry: ConfigProvider → I18nProvider → App
└── AdminLayout (sider + header + content)
└── pages (Dashboard, Domains, …)
└── shared presentation components
Visual System (Tokens)
Color
| Role |
Value |
Usage |
| Primary |
#4F46E5 |
Primary buttons, links, active nav accent |
| Primary hover |
#4338CA |
Hover / pressed primary |
| Primary soft |
#EEF2FF |
Soft fills, selected chips |
| Ink / Sider |
#0F172A |
Sidebar background, primary text |
| Text secondary |
#64748B |
Labels, helper text |
| Text muted |
#94A3B8 |
Meta / placeholders |
| Canvas |
#F4F6FB |
App background |
| Surface |
#FFFFFF |
Cards, header, auth form card |
| Border |
#E2E8F0 |
Dividers, card borders |
| Success |
#16A34A (+ soft green bg) |
Healthy / accepted |
| Warning |
#D97706 (+ soft amber bg) |
Pending / DNS issues |
| Danger |
#DC2626 (+ soft red bg) |
Failed / destructive |
Shape, Type, Space
- Radius: controls
10px, cards 14px, pills 999px
- Shadow: light elevation — thin border + soft ambient (
0 1px 2px + 0 8px 24px slate-tinted)
- Spacing rhythm: 8-based (
8 / 16 / 24 / 32); content padding 24–32px
- Type scale: page title
22–24px, metric value 28–32px, body 14px
- Font stack: Inter preferred, with system-ui / -apple-system / Segoe UI fallbacks
- Inter loading: prefer system stack only or self-hosted font files if Inter is added later; avoid third-party CDN font loads for self-hosted deploy reliability. Default implementation may use system-ui stack without shipping Inter files.
Ant Design Theme Mapping
Map tokens into ConfigProvider at least for:
colorPrimary, colorSuccess, colorWarning, colorError
colorBgLayout, colorBgContainer, colorBorderSecondary, colorText, colorTextSecondary
borderRadius, borderRadiusLG
- Component overrides for
Card, Table (comfortable cell padding), Button, Menu (sider), Tag or custom StatusPill preference
Default Ant blue #1677ff must not remain as brand primary.
Shell Design
AdminLayout
- Sticky dark sider width 248px, background ink
#0F172A
- Brand block: gradient MH mark + title
MailHub + subtitle Email Delivery
- Navigation groups (labels only; routes unchanged):
- Overview — Dashboard, Domains, DNS API
- Delivery — SMTP, API Tokens, Sending Logs, Webhooks
- System — Admin (admin role only), Settings
- Active item: primary-soft indigo wash + light text (custom sider styles; avoid stock dark-menu look)
- Header: white surface, border bottom, page title / breadcrumb, language select, refresh, primary CTA Add domain, user dropdown
- Content: canvas background, comfortable padding
- Responsive: below
lg, collapse sider to drawer-style behavior (existing Ant Layout breakpoint pattern)
- Optional light account summary at sider bottom is allowed; primary account actions stay in header dropdown
Auth (Login / Register / Forgot / Reset / Resend)
- Same tokens as admin
- Desktop split:
- Left: ink brand panel — logo, headline, short value prop, 2–3 capability signals
- Right: soft canvas/indigo wash + elevated white form card
- Mobile: single column; brand panel compressed on top
- Keep existing auth modes and API calls; restyle forms, alerts, segmented control, and spacing only
Shared Presentation Components
| Component |
Responsibility |
PageHeader |
Title, optional subtitle/description, right-side actions (one primary action per region) |
MetricCard |
Label, large value, optional trend pill / soft icon tile |
SectionCard |
Standard content card (14px radius, light shadow, consistent header/body padding) |
StatusPill |
Semantic soft status chip (success / warning / error / info / neutral) |
EmptyState |
Icon, message, optional primary CTA for empty lists/charts |
CodeBlock |
Mono, wrap-safe long values (DNS/token samples) with copy affordance |
Prefer placing these under src/components/common/ (or equivalent) and reusing across pages. Existing StatusTag may be adapted into or replaced by StatusPill for visual consistency.
Key Page Treatments
Dashboard
- Keep security alert (default admin password) at top when applicable
- Condense the metric strip to exactly four primary MetricCards:
- 今日发送 (today sent)
- 成功率 (success rate)
- 已验证域名 (verified domains)
- DNS 问题 (DNS issues count)
- Place remaining current summary fields as secondary, not equal-weight cards:
- Bounce rate / complaint rate — show as captions or mini stats under success-rate card, or in status distribution context
- Last sent at — meta line under recent-sends SectionCard header or toolbar
- SMTP status — compact readiness chip in PageHeader extra area or a small readiness row above charts
- Chart row: trend + status distribution in SectionCards; recolor plots to token palette
- Retain existing secondary panels (domain ranking, hourly heatmap, domain health list / recent failures if present) — restyle with SectionCard chrome; do not drop them from the layout inventory
- Recent sends table inside SectionCard
- No analytics model API changes
Domains list
- PageHeader + toolbar (search, status filter, add domain)
- Table inside SectionCard; domain name as emphasized link control
- Status via StatusPill; comfortable row padding
Domain detail
- Upgrade
DomainHealthCard to a hero block: domain title, status pill, key stats, DNS progress, primary action stack (apply DNS / check / test / edit)
- Tabs and two-column DNS records layout remain
- DNS record cards use CodeBlock + StatusPill + consistent action buttons
- No change to DNS apply/check/test flows
Other pages (SMTP, Tokens, Logs, DNS API, Settings, Admin, Webhooks placeholder)
- Apply PageHeader + SectionCard + table/form spacing polish
- Do not redesign information architecture or form field sets beyond visual hierarchy
- Token create modal / secret-once warning keeps existing behavior with improved visual emphasis
Engineering Boundaries
In scope files (expected)
src/frontend/App.tsx — theme provider tokens
src/frontend/styles.css — CSS variables + shell/page styles
src/frontend/auth/AuthApp.tsx (+ auth-related styles)
src/layouts/AdminLayout.tsx
src/components/common/* (new or updated shared UI)
src/components/domain/* (visual structure only)
src/pages/** (composition / classNames / shared components; handlers stay)
Out of scope
src/server.js, src/db.js, mailer/submission/DNS provider logic
- Model pure functions unless a pure presentation helper is needed
- Docker / deploy scripts
i18n
- Reuse existing keys for page meaning
- May add short chrome keys (nav group labels, empty-state titles) in both locales already supported by the app
Testing & verification
- Existing
npm test suite must remain green (models, i18n, server APIs unchanged)
- Add tests only if new pure helpers are introduced
- Manual / build verification:
npm run build (and UI smoke of login + dashboard + domain detail)
Delivery Order
- Design tokens (CSS vars + ConfigProvider)
- AdminLayout shell + nav groups
- Auth shell restyle
- Shared presentation components
- Dashboard
- Domains list + domain detail / health / DNS cards
- Remaining pages sweep
- Build + test gate
Success Criteria
- Brand primary is indigo-based, not default Ant blue
- Admin and auth feel like one product
- Layout remains dark sidebar + comfortable density as approved
- All existing navigation destinations and primary workflows remain reachable
- No intentional API or auth behavior regressions
npm test and npm run build pass
Open Questions
None blocking. Optional later (explicitly out of this spec): dark mode, illustration pack, custom icon set beyond @ant-design/icons.