Add PaperPhone design system

This commit is contained in:
619dev
2026-08-26 08:21:31 +08:00
parent d54a733856
commit 2cd21b755e
2 changed files with 310 additions and 49 deletions

253
DESIGN.md Normal file
View File

@@ -0,0 +1,253 @@
# PaperPhonePlus Design System
## 1. Product character
PaperPhonePlus is a private, end-to-end encrypted mobile messenger. Its interface
combines Apple's quiet, content-first interaction model with a warm paper-gold
identity. It should feel trustworthy, calm, fast, and native rather than like a
marketing website or a desktop SaaS dashboard.
Use this document for application screens. It intentionally adapts ideas from
Apple-style interfaces instead of copying Apple marketing layouts.
### Design principles
1. **Messages are the product.** Chrome recedes; conversation content has the
strongest visual weight.
2. **Gold means PaperPhone.** Gold identifies primary actions, active navigation,
and brand moments. It is not a generic warning color.
3. **Security is legible, not theatrical.** Explain encryption with concise text,
familiar icons, and clear state. Avoid neon glows, hacker imagery, and excessive
lock decoration.
4. **Glass belongs to chrome.** Translucency is reserved for navigation bars,
composer bars, sheets, and overlays. Message bubbles and ordinary list rows use
stable, readable surfaces.
5. **One-handed by default.** Primary controls are reachable, touch targets are at
least 44×44 CSS pixels, and every screen respects device safe areas.
## 2. Color system
### Light theme
| Token | Value | Role |
| --- | --- | --- |
| `bg-primary` | `#f2f2f7` | App canvas and conversation background |
| `bg-secondary` | `#ffffff` | Solid elevated surface and incoming bubble |
| `bg-tertiary` | `#e5e5ea` | Dividers and disabled surfaces |
| `text-primary` | `#1c1c1e` | Primary text |
| `text-secondary` | `#636366` | Supporting text and message previews |
| `text-muted` | `#8e8e93` | Timestamps, placeholders, disabled labels |
| `accent` | `#d4a017` | PaperPhone brand and primary action |
| `accent-pressed` | `#b8860b` | Pressed primary action |
| `accent-soft` | `rgba(212, 160, 23, 0.12)` | Selection and active-navigation backing |
| `system-link` | `#007aff` | Links and neutral informational actions |
| `success` | `#34c759` | Online, delivered, verified, successful |
| `warning` | `#ff9f0a` | Expiry and caution states only |
| `danger` | `#ff3b30` | Destructive action, failed send, unread badge |
| `separator` | `rgba(60, 60, 67, 0.18)` | Hairline separators |
### Dark theme
| Token | Value | Role |
| --- | --- | --- |
| `bg-primary` | `#000000` | App canvas |
| `bg-secondary` | `#1c1c1e` | Solid elevated surface and incoming bubble |
| `bg-tertiary` | `#2c2c2e` | Secondary controls |
| `text-primary` | `#f5f5f7` | Primary text |
| `text-secondary` | `#aeaeb2` | Supporting text |
| `text-muted` | `#8e8e93` | Timestamps and placeholders |
| `system-link` | `#0a84ff` | Links and neutral informational actions |
| `separator` | `rgba(84, 84, 88, 0.65)` | Hairline separators |
Gold must maintain readable contrast. Use white text on a darkened gold fill for
large primary buttons. On pale backgrounds, use the darker gold variant for text.
Do not use gold for warnings; warning orange and danger red retain their semantic
meaning.
## 3. Typography
Use the platform system font. Never require a network font for core UI.
```css
font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI",
"Noto Sans", "Helvetica Neue", Arial, sans-serif;
```
| Style | Size / line height | Weight | Usage |
| --- | --- | --- | --- |
| Navigation title | `17px / 22px` | 600 | Page and conversation titles |
| Section title | `20px / 25px` | 600 | Major in-page section |
| Body | `16px / 22px` | 400 | Messages, inputs, settings |
| List name | `15px / 20px` | 500 | Contact and conversation name |
| Supporting | `13px / 18px` | 400 | Preview, status, helper text |
| Caption | `12px / 16px` | 400 | Timestamp and metadata |
| Tab label | `10px / 12px` | 500 | Bottom navigation only |
Use sentence case. Avoid all-caps labels. Allow user-generated messages to wrap and
scale; do not apply negative tracking to message text. Chinese, Japanese, Korean,
Cyrillic, and Latin scripts must share a visually balanced fallback stack.
## 4. Shape, spacing, and depth
### Spacing
Use a 4px base grid: `4, 8, 12, 16, 20, 24, 32, 40`.
- Page horizontal inset: 16px.
- Compact control gap: 8px.
- List row vertical padding: 12px.
- Message-to-message gap: 4px for the same sender, 12px when sender changes.
- Major section gap: 24px.
### Radius
| Token | Value | Usage |
| --- | --- | --- |
| `sm` | `10px` | Compact controls and menus |
| `md` | `14px` | Cards, inputs, attachment tiles |
| `lg` | `20px` | Message bubbles and sheets |
| `xl` | `28px` | Floating bottom navigation |
| `full` | `9999px` | Avatars, badges, circular buttons |
### Elevation
- Prefer surface color and a hairline border over shadow.
- Navigation glass may use one soft shadow no darker than 8% black in light mode.
- Modals and sheets may use a larger shadow, but never stack multiple glowing
shadows.
- Avatars and ordinary message bubbles should not glow.
## 5. Materials
Glass surfaces may be used for the page header, bottom tab bar, chat composer,
modal, and action sheet. Use a translucent theme surface with approximately
`24px` blur and `160%` saturation. Always provide an opaque fallback.
Do not use backdrop blur on:
- message bubbles;
- conversation rows;
- settings rows;
- media cards;
- warning or encryption banners.
When reduced transparency is requested, glass becomes an opaque `bg-secondary`
surface. When reduced motion is requested, remove entrance animations, glow
animations, and spring scaling.
## 6. Core components
### Navigation bar
- 56px content height plus top safe-area inset.
- Center or leading-align a 17px semibold title according to screen context.
- Back and utility buttons have a 44×44px hit area.
- Use gold for the primary route action; use `system-link` for informational links.
### Bottom tab bar
- Four destinations: Chats, Contacts, Discover, Profile.
- The active tab uses a gold icon and label on a subtle gold backing.
- Inactive tabs use muted text; do not place each tab inside a separate card.
- Unread badges use danger red and must not depend on color alone: include a count.
### Conversation list row
- Avatar: 44×44px.
- Primary line: contact/group name; truncate to one line.
- Secondary line: latest message summary; truncate to one line.
- Trailing column: timestamp above unread badge.
- Online green is a small status indicator, never a large label.
- Rows use a flat surface with a subtle pressed state, not floating glass cards.
### Message bubble
- Incoming: solid secondary surface, primary text.
- Outgoing: restrained gold surface with contrast-checked text.
- Maximum width: 78% of the conversation column.
- Use 1820px outer radius and a slightly tighter radius toward the sender edge.
- Group consecutive messages before repeating avatar/name metadata.
- Place delivery/read state beside the outgoing timestamp. Never communicate a
failed send through color alone; include an icon and accessible label.
- Long text, URLs, emoji, CJK text, and right-to-left text must wrap safely.
### Composer
- Anchored above the bottom safe area.
- Text field minimum height: 40px; action hit areas: 44×44px.
- The send control is gold only when content can be sent. Disabled state is neutral.
- Attachment and emoji actions remain secondary and must not compete with Send.
- Keyboard focus uses a visible 2px gold ring with a soft outer halo.
### Buttons
- Primary: solid PaperPhone gold, pill shape, minimum height 44px.
- Secondary: neutral solid or glass surface, depending on context.
- Destructive: system danger red; require confirmation for irreversible operations.
- Icon-only: circular 44px hit area and an accessible name.
- Avoid decorative gradients and shimmer on ordinary utility buttons.
### Security and warning banners
- Encryption state uses a shield/lock icon, a concise heading, and optional detail.
- Warning banners use semantic warning yellow/orange, not brand gold.
- Group-chat encryption limitations must be visually persistent until resolved.
- Never claim encryption merely through an icon; display the actual state in text.
### Empty, loading, and error states
- Empty states use one quiet line icon, a short explanation, and at most one action.
- Use skeletons only where layout is known; otherwise use a compact progress label.
- Retry errors remain near the failed operation and preserve the user's input.
## 7. Motion and feedback
- Standard transition: 200ms, `cubic-bezier(0.2, 0.8, 0.2, 1)`.
- Page entrance: subtle opacity and at most 8px vertical movement.
- Press feedback: scale no smaller than 0.96 for buttons and 0.98 for rows.
- Never animate message content while the user is selecting or copying it.
- Haptics may accompany Send, call connect, and destructive confirmation in native
shells, but the interface must remain understandable without haptics.
## 8. Responsive and accessibility rules
- Primary layout targets 320480px phone widths.
- Respect top, bottom, left, and right safe-area insets in every orientation.
- Minimum touch target: 44×44px on coarse pointers.
- Text must remain usable at 200% browser zoom without horizontal page scrolling.
- Interactive controls need `:focus-visible` treatment and accessible names.
- Never disable zoom.
- Support `prefers-reduced-motion`, `prefers-contrast`, and reduced-transparency
fallbacks where available.
- Body text and essential icons target WCAG AA contrast.
- Do not use color as the only indication of online, unread, failed, selected,
encrypted, or verified state.
## 9. Do and don't
### Do
- Keep conversations visually quiet and content-first.
- Use gold sparingly and consistently for PaperPhone identity.
- Prefer stable solid surfaces behind long-form message content.
- Test both themes with long multilingual strings and large dynamic text.
- Keep navigation and composer behavior familiar across iOS and Android.
### Don't
- Do not reproduce Apple marketing hero layouts inside the app.
- Do not turn the UI purple to resemble Slack or orange to resemble Intercom.
- Do not cover every card with blur, gloss, gradients, or glow.
- Do not use proprietary brand fonts that are unavailable to users.
- Do not hide privacy or encryption limitations behind decorative visuals.
- Do not sacrifice Android usability to imitate iOS chrome literally.
## 10. Agent implementation prompt
When building or modifying PaperPhonePlus UI, preserve existing behavior and use
this system as the source of truth. Build a mobile-first, content-led messenger
with platform-system typography, calm Apple-inspired hierarchy, PaperPhone gold
for primary brand actions, solid readable message surfaces, and glass only on
navigation/composer overlays. Implement both themes, safe areas, 44px touch targets,
keyboard focus, reduced motion, and long multilingual content. Do not introduce
marketing-page patterns or copy another company's brand identity.

View File

@@ -3,8 +3,6 @@
Translucent glass materials · Depth & refraction · Fluid motion
═══════════════════════════════════════════════════════════════ */
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
:root {
/* ── Color System (Light) ────────────────────────────────── */
--bg-primary: #f2f2f7;
@@ -30,8 +28,10 @@
--warning: #ff9f0a;
--danger: #ff453a;
--info: #0a84ff;
--system-link: #007aff;
--border: rgba(0, 0, 0, 0.06);
--separator: rgba(60, 60, 67, 0.18);
--border-active: rgba(212, 160, 23, 0.4);
--shadow: 0 2px 16px rgba(0, 0, 0, 0.06);
--shadow-lg: 0 8px 40px rgba(0, 0, 0, 0.08);
@@ -45,8 +45,8 @@
rgba(255,255,255,0.9) 0%,
rgba(255,255,255,0.3) 50%,
rgba(255,255,255,0.05) 100%);
--glass-blur: 40px;
--glass-saturate: 200%;
--glass-blur: 24px;
--glass-saturate: 160%;
--glass-shadow: 0 2px 20px rgba(0, 0, 0, 0.06),
0 0 0 0.5px rgba(0, 0, 0, 0.04);
@@ -91,8 +91,10 @@
--text-inverse: #000000;
--accent-glow: rgba(212, 160, 23, 0.35);
--system-link: #0a84ff;
--border: rgba(255, 255, 255, 0.06);
--separator: rgba(84, 84, 88, 0.65);
--border-active: rgba(212, 160, 23, 0.5);
--shadow: 0 4px 24px rgba(0, 0, 0, 0.4);
--shadow-lg: 0 8px 48px rgba(0, 0, 0, 0.5);
@@ -121,7 +123,8 @@
html, body, #root {
height: 100%;
height: 100dvh; /* Dynamic viewport height for iOS Safari */
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Segoe UI', sans-serif;
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Text', 'Segoe UI',
'Noto Sans', 'Helvetica Neue', Arial, sans-serif;
font-size: 15px;
line-height: 1.5;
color: var(--text-primary);
@@ -362,7 +365,7 @@ body {
width: 44px;
height: 44px;
border-radius: var(--radius-full);
background: linear-gradient(145deg, var(--accent), var(--accent-light));
background: var(--accent);
display: flex;
align-items: center;
justify-content: center;
@@ -371,7 +374,6 @@ body {
color: white;
flex-shrink: 0;
position: relative;
box-shadow: 0 2px 8px var(--accent-glow);
}
.avatar img { width: 100%; height: 100%; object-fit: cover; border-radius: var(--radius-full); }
@@ -402,7 +404,7 @@ body {
.list-meta .time { font-size: 12px; color: var(--text-muted); }
/* ═══════════════════════════════════════════════════════════════
Buttons — Liquid Glass Capsules
Buttons — PaperPhone Gold + Neutral Capsules
═══════════════════════════════════════════════════════════════ */
.btn {
display: inline-flex;
@@ -422,28 +424,14 @@ body {
overflow: hidden;
}
/* Shimmer highlight */
.btn::before {
content: '';
position: absolute;
top: 0; left: 0; right: 0;
height: 50%;
background: linear-gradient(180deg,
rgba(255,255,255,0.25) 0%,
rgba(255,255,255,0) 100%);
border-radius: var(--radius-full) var(--radius-full) 0 0;
pointer-events: none;
}
.btn-primary {
background: linear-gradient(145deg, var(--accent), var(--accent-dark));
background: var(--accent-dark);
color: white;
box-shadow: 0 2px 12px var(--accent-glow),
inset 0 1px 0 rgba(255,255,255,0.2);
box-shadow: 0 2px 8px var(--accent-glow);
}
.btn-primary:hover {
box-shadow: 0 4px 24px var(--accent-glow),
inset 0 1px 0 rgba(255,255,255,0.2);
background: var(--accent);
box-shadow: 0 3px 12px var(--accent-glow);
transform: translateY(-1px);
}
.btn-primary:active {
@@ -536,7 +524,7 @@ textarea.input {
}
/* ═══════════════════════════════════════════════════════════════
Chat Bubbles — Glass Message Capsules
Chat Bubbles — Stable, Readable Message Surfaces
═══════════════════════════════════════════════════════════════ */
.chat-messages-shell {
position: relative;
@@ -575,7 +563,7 @@ textarea.input {
.msg-row {
display: flex;
gap: 8px;
max-width: 80%;
max-width: 78%;
animation: msg-in 0.35s var(--ease-spring);
}
@@ -594,32 +582,15 @@ textarea.input {
}
.msg-row:not(.outgoing) .msg-bubble {
background: var(--glass-bg-thick);
backdrop-filter: blur(20px) saturate(160%);
-webkit-backdrop-filter: blur(20px) saturate(160%);
border: 0.5px solid var(--glass-border);
background: var(--bg-secondary);
border: 0.5px solid var(--border);
border-bottom-left-radius: 6px;
box-shadow: 0 1px 4px rgba(0,0,0,0.04);
}
.msg-row.outgoing .msg-bubble {
background: linear-gradient(145deg, var(--accent), var(--accent-dark));
background: var(--accent-dark);
color: white;
border-bottom-right-radius: 6px;
box-shadow: 0 2px 12px var(--accent-glow);
}
/* Outgoing bubble specular highlight */
.msg-row.outgoing .msg-bubble::before {
content: '';
position: absolute;
top: 0; left: 0; right: 0;
height: 50%;
background: linear-gradient(180deg,
rgba(255,255,255,0.15) 0%,
rgba(255,255,255,0) 100%);
border-radius: var(--radius-lg) var(--radius-lg) 0 0;
pointer-events: none;
}
.msg-time {
@@ -687,7 +658,7 @@ textarea.input {
width: 40px;
height: 40px;
border-radius: var(--radius-full);
background: linear-gradient(145deg, var(--accent), var(--accent-dark));
background: var(--accent-dark);
border: none;
color: white;
font-size: 16px;
@@ -1974,3 +1945,40 @@ textarea.input {
.privacy-footer p {
margin: 0;
}
/* Respect platform accessibility preferences without changing app structure. */
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
scroll-behavior: auto !important;
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
@media (prefers-reduced-transparency: reduce) {
.glass,
.page-header,
.tabbar,
.chat-input-bar,
.login-card,
.modal,
.btn-secondary,
.privacy-card {
background: var(--bg-secondary);
backdrop-filter: none;
-webkit-backdrop-filter: none;
}
}
@media (prefers-contrast: more) {
:root {
--border: rgba(0, 0, 0, 0.22);
--text-muted: #636366;
}
[data-theme="dark"] {
--border: rgba(255, 255, 255, 0.28);
--text-muted: #aeaeb2;
}
}