open-design/design-systems/wechat/components.html
chaoxiaoche 336620e06f
feat(design-systems): add tokens.css + components.html for 10 consumer / hardware / cultural brands (#2033)
Adds the schema-compliant token + fixture pair for the next 10 brands in
Tier E (consumer / hardware / global-cultural surfaces):

- pinterest, airtable          (visual discovery + no-code product)
- bmw, tesla                   (automotive: German precision vs. EV minimalism)
- spacex                       (aerospace cosmic minimalism, zero-shadow)
- nike                         (sportswear, monochrome editorial)
- playstation                  (gaming console, dark-first cobalt)
- starbucks                    (warm cream + Siren Green)
- wechat, xiaohongshu          (CJK-primary consumer apps, bilingual stacks)

Each pair declares all 56 shared tokens (26 A1 + 26 A2 + 4 B-slot) in
:root with brand-rationale comments in tokens.css and a comment-free
byte-equivalent :root in components.html. No C-extensions were needed.

Validation:
- pnpm guard: passed (66 brand pairs aligned, 3714 declarations, 66
  brands declare all A1/A2/B-slot tokens, A2 defaults parity intact,
  flag parity unchanged)

Co-authored-by: chaoxiaoche <chaoxiaoche@chaoxiaochedeMacBook-Pro.local>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-18 15:40:52 +08:00

507 lines
20 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>WeChat 微信 — reference components</title>
<meta
name="description"
content="Reference fixture for design-systems/wechat. Every visible
value comes from tokens.css. WeChat (微信) signature moves: muted
#EDEDED chat-list canvas, single decisive WeChat Green
(#07C160) accent, bilingual CJK-aware typography, modest
mobile-first type scale, whisper-quiet elevation lifted from
the incoming chat bubble."
/>
<style>
:root {
--bg: #ededed;
--surface: #f7f7f7;
--surface-warm: var(--surface);
--fg: #1a1a1a;
--fg-2: var(--fg);
--muted: #888888;
--meta: var(--muted);
--border: #e0e0e0;
--border-soft: var(--border);
--accent: #07c160;
--accent-on: #ffffff;
--accent-hover: #10b160;
--accent-active: #059050;
--success: #07c160;
--warn: #fab702;
--danger: #fa5151;
--font-display: -apple-system, BlinkMacSystemFont, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans SC", "Helvetica Neue", Helvetica, Arial, sans-serif;
--font-body: -apple-system, BlinkMacSystemFont, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans SC", "Helvetica Neue", Helvetica, Arial, sans-serif;
--font-mono: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Monaco, Consolas, monospace;
--text-xs: 11px;
--text-sm: 13px;
--text-base: 15px;
--text-lg: 16px;
--text-xl: 18px;
--text-2xl: 24px;
--text-3xl: 32px;
--text-4xl: 48px;
--leading-body: 1.6;
--leading-tight: 1.3;
--tracking-display: -0.01em;
--space-1: 4px;
--space-2: 8px;
--space-3: 12px;
--space-4: 16px;
--space-5: 20px;
--space-6: 24px;
--space-8: 32px;
--space-12: 48px;
--section-y-desktop: 72px;
--section-y-tablet: 48px;
--section-y-phone: 32px;
--radius-sm: 4px;
--radius-md: 8px;
--radius-lg: 16px;
--radius-pill: 9999px;
--elev-flat: none;
--elev-ring: 0 0 0 1px var(--border);
--elev-raised: 0 1px 2px rgba(0, 0, 0, 0.06);
--focus-ring: 0 0 0 3px color-mix(in oklab, var(--accent), transparent 70%);
--motion-fast: 100ms;
--motion-base: 200ms;
--ease-standard: cubic-bezier(0.25, 0.1, 0.25, 1);
--container-max: 1200px;
--container-gutter-desktop: 24px;
--container-gutter-tablet: 16px;
--container-gutter-phone: 16px;
}
/* ─── Reset ─────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
background: var(--bg);
color: var(--fg);
font-family: var(--font-body);
font-size: var(--text-base);
line-height: var(--leading-body);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
/* ─── Layout ─────────────────────────────────────────────── */
.container {
max-width: var(--container-max);
margin-inline: auto;
padding-inline: var(--container-gutter-desktop);
}
section { padding-block: var(--section-y-desktop); }
section + section { border-top: 1px solid var(--border); }
@media (max-width: 1023px) {
.container { padding-inline: var(--container-gutter-tablet); }
section { padding-block: var(--section-y-tablet); }
}
@media (max-width: 639px) {
.container { padding-inline: var(--container-gutter-phone); }
section { padding-block: var(--section-y-phone); }
}
/* ─── Typography — modest, bilingual, mobile-first ──────── */
h1, h2, h3 {
font-family: var(--font-display);
line-height: var(--leading-tight);
margin: 0;
color: var(--fg);
}
h1 {
font-size: var(--text-4xl);
letter-spacing: var(--tracking-display);
font-weight: 600;
}
h2 {
font-size: var(--text-3xl);
letter-spacing: var(--tracking-display);
font-weight: 600;
}
h3 {
font-size: var(--text-xl);
font-weight: 600;
}
p { margin: 0; }
.lead {
font-size: var(--text-lg);
color: var(--muted);
line-height: var(--leading-body);
}
.body-muted { color: var(--muted); }
.body-sm { font-size: var(--text-sm); }
.body-xs { font-size: var(--text-xs); color: var(--meta); }
.eyebrow {
font-size: var(--text-xs);
color: var(--muted);
text-transform: uppercase;
letter-spacing: 0.08em;
font-weight: 500;
}
.stack-2 > * + * { margin-block-start: var(--space-2); }
.stack-3 > * + * { margin-block-start: var(--space-3); }
.stack-4 > * + * { margin-block-start: var(--space-4); }
.stack-6 > * + * { margin-block-start: var(--space-6); }
/* ─── Buttons — green is the load-bearing colour ───────── */
.btn {
display: inline-flex;
align-items: center;
gap: var(--space-2);
padding: 10px var(--space-6);
border-radius: var(--radius-md);
font-family: var(--font-body);
font-size: var(--text-lg);
font-weight: 500;
line-height: 1;
cursor: pointer;
border: none;
transition: background-color var(--motion-fast) var(--ease-standard),
color var(--motion-fast) var(--ease-standard);
text-decoration: none;
}
.btn:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.btn-primary {
background: var(--accent);
color: var(--accent-on);
}
.btn-primary:hover { background: var(--accent-hover); }
.btn-primary:active { background: var(--accent-active); }
.btn-secondary {
background: var(--surface);
color: var(--fg);
border: 1px solid var(--border);
}
.btn-secondary:hover { background: color-mix(in oklab, var(--surface), var(--fg) 4%); }
/* ─── Inputs — light surface, green focus ──────────────── */
.field { display: flex; flex-direction: column; gap: var(--space-2); }
.field label {
font-size: var(--text-sm);
font-weight: 500;
color: var(--fg);
}
.field input {
padding: 10px var(--space-3);
border-radius: var(--radius-md);
border: 1px solid var(--border);
background: var(--surface);
color: var(--fg);
font-family: var(--font-body);
font-size: var(--text-base);
outline: none;
transition: border-color var(--motion-fast) var(--ease-standard),
box-shadow var(--motion-fast) var(--ease-standard);
}
.field input:focus-visible {
border-color: var(--accent);
box-shadow: var(--focus-ring);
}
.field input::placeholder { color: var(--muted); }
.field-help { font-size: var(--text-xs); color: var(--meta); }
/* ─── Cards — flat surface, hairline border ────────────── */
.card {
background: var(--surface);
border-radius: var(--radius-lg);
padding: var(--space-6);
display: flex;
flex-direction: column;
gap: var(--space-3);
box-shadow: var(--elev-raised);
border: 1px solid var(--border-soft);
}
.card h3 { line-height: var(--leading-tight); }
/* ─── Badges ────────────────────────────────────────────── */
.badge {
display: inline-flex; align-items: center; gap: var(--space-2);
padding: 3px var(--space-2);
border-radius: var(--radius-pill);
font-size: var(--text-xs);
font-weight: 500;
line-height: 1.6;
}
.badge-success {
color: var(--accent-on);
background: var(--success);
}
.badge-muted {
color: var(--muted);
background: color-mix(in oklab, var(--muted), transparent 88%);
}
.badge-dot { width: 6px; height: 6px; border-radius: var(--radius-pill); background: currentColor; }
/* ─── Links ─────────────────────────────────────────────── */
a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; text-underline-offset: 3px; }
kbd {
font-family: var(--font-mono); font-size: var(--text-xs);
padding: 2px 6px; border-radius: var(--radius-sm);
border: 1px solid var(--border); background: var(--surface); color: var(--muted);
}
/* ─── Hero — chat-bubble preview anchored on the right ── */
.hero-grid {
display: grid;
grid-template-columns: 1.3fr 1fr;
gap: var(--space-12);
align-items: center;
}
@media (max-width: 1023px) {
.hero-grid { grid-template-columns: 1fr; gap: var(--space-8); }
}
.hero-actions {
display: flex; gap: var(--space-3);
margin-block-start: var(--space-6);
flex-wrap: wrap;
}
.hero-meta {
display: flex; flex-direction: column; gap: var(--space-3);
padding: var(--space-4) var(--space-5);
border: 1px solid var(--border);
border-radius: var(--radius-md);
background: var(--surface);
}
/* Chat-bubble illustration — DESIGN.md §Components ───────
* Bubble fills (#95EC69 outgoing, #FFFFFF incoming) are
* brand-specific C-extensions and therefore stay inline as
* one-off literals; they are not promoted to tokens. */
.chat-frame {
display: flex; flex-direction: column;
gap: var(--space-2);
padding: var(--space-5);
background: var(--bg);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
}
.chat-row {
display: flex; align-items: flex-end; gap: var(--space-2);
max-width: 100%;
}
.chat-row.self { justify-content: flex-end; }
.chat-avatar {
width: 32px; height: 32px;
border-radius: var(--radius-pill);
background: var(--border);
flex-shrink: 0;
}
.bubble {
max-width: 78%;
padding: 10px var(--space-3);
border-radius: var(--radius-lg);
font-size: var(--text-base);
line-height: var(--leading-body);
color: var(--fg);
}
.bubble.self {
background: #95EC69;
border-top-right-radius: var(--radius-sm);
}
.bubble.other {
background: #ffffff;
border-top-left-radius: var(--radius-sm);
box-shadow: var(--elev-raised);
}
.timestamp {
align-self: center;
padding: var(--space-1) var(--space-2);
border-radius: var(--radius-sm);
background: color-mix(in oklab, var(--fg), transparent 92%);
font-size: var(--text-xs);
color: var(--muted);
}
/* ─── Features grid — three mini-program style cards ───── */
.features-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: var(--space-5);
}
@media (max-width: 1023px) { .features-grid { grid-template-columns: 1fr 1fr; } }
@media (max-width: 639px) { .features-grid { grid-template-columns: 1fr; } }
.card-icon {
width: 40px; height: 40px;
border-radius: var(--radius-md);
background: color-mix(in oklab, var(--accent), transparent 86%);
color: var(--accent);
display: inline-flex; align-items: center; justify-content: center;
}
.card-icon svg { width: 22px; height: 22px; }
/* ─── Form row ─────────────────────────────────────────── */
.form-row {
display: grid;
grid-template-columns: 1.3fr 1fr;
gap: var(--space-12);
align-items: start;
}
@media (max-width: 1023px) { .form-row { grid-template-columns: 1fr; } }
.form { display: flex; flex-direction: column; gap: var(--space-4); max-width: 420px; }
.form-actions { display: flex; gap: var(--space-3); margin-block-start: var(--space-2); }
.icon { width: 16px; height: 16px; flex-shrink: 0; }
.row-between { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); }
</style>
</head>
<body>
<main class="container">
<section data-od-id="hero">
<div class="hero-grid">
<div class="stack-4">
<p class="eyebrow">Reference fixture · wechat · 微信</p>
<h1>微信,是一个生活方式。</h1>
<p class="lead" style="max-width: 56ch">
WeChat — a way of life. From messages and Moments to Mini Programs,
payments, and official accounts, one app holds the whole day.
所有功能,皆在微信。
</p>
<div class="hero-actions">
<a href="./tokens.css" class="btn btn-primary">
立即体验
<svg class="icon" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2"
stroke-linecap="round" stroke-linejoin="round"
aria-hidden="true"><path d="M5 12h14M13 6l6 6-6 6"/></svg>
</a>
<a href="./DESIGN.md" class="btn btn-secondary">了解更多 · Learn more</a>
</div>
</div>
<aside class="chat-frame" aria-label="Chat preview · 聊天预览">
<span class="timestamp"><time datetime="2026-05-18T14:08">下午 2:08</time></span>
<div class="chat-row">
<span class="chat-avatar" aria-hidden="true"></span>
<div class="bubble other">在吗?周末出来喝杯咖啡?</div>
</div>
<div class="chat-row self">
<div class="bubble self">好呀!老地方见 ☕</div>
</div>
<div class="chat-row">
<span class="chat-avatar" aria-hidden="true"></span>
<div class="bubble other">See you Saturday — I'll send the location.</div>
</div>
</aside>
</div>
</section>
<section data-od-id="features">
<div class="stack-3">
<p class="eyebrow">What this fixture exercises · 本示例所演示</p>
<h2 style="max-width: 24ch">日常的连接,安静地发生。</h2>
<p class="lead" style="max-width: 56ch">
Everyday connection, quietly delivered — chat, pay, scan, share.
Green is the only colour that raises its voice.
</p>
</div>
<div class="features-grid" style="margin-block-start: var(--space-8)">
<article class="card">
<span class="card-icon" aria-hidden="true">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round">
<path d="M21 12a8 8 0 0 1-11.7 7.1L4 21l1.9-5.3A8 8 0 1 1 21 12z"/>
</svg>
</span>
<h3>聊天 · Chat</h3>
<p class="body-muted body-sm">
15px 正文配 1.6 行高,#95EC69 outgoing bubbles, #FFFFFF
incoming — the colour pair every WeChat user knows by heart.
</p>
<a href="./DESIGN.md" class="body-sm">查看气泡规范 →</a>
</article>
<article class="card">
<span class="card-icon" aria-hidden="true">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round">
<rect x="3" y="3" width="7" height="7" rx="1.5"/>
<rect x="14" y="3" width="7" height="7" rx="1.5"/>
<rect x="3" y="14" width="7" height="7" rx="1.5"/>
<path d="M14 14h3M14 17h7M17 21v-7"/>
</svg>
</span>
<h3>小程序 · Mini Programs</h3>
<p class="body-muted body-sm">
卡片以 16px 圆角浮于灰底之上hairline border 替代厚阴影 —
cards float on the chat-list gray with whisper-quiet elevation.
</p>
<a href="./tokens.css" class="body-sm">Inspect radius →</a>
</article>
<article class="card">
<span class="card-icon" aria-hidden="true">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round">
<rect x="4" y="6" width="16" height="12" rx="2"/>
<path d="M4 10h16M8 14h3"/>
</svg>
</span>
<h3>支付 · WeChat Pay</h3>
<p class="body-muted body-sm">
#07C160 是“完成”的颜色 — green doubles as success because in
WeChat green is paid, done, delivered, confirmed.
</p>
<a href="./tokens.css" class="body-sm">Inspect accent →</a>
</article>
</div>
</section>
<section data-od-id="form">
<div class="form-row">
<div class="stack-4">
<p class="eyebrow">Form components · 表单组件</p>
<h2>用微信号登录。</h2>
<p class="body-muted" style="max-width: 48ch">
Sign in with your WeChat ID. Inputs sit on the surface tier,
focus rings inherit the brand green — no cool grays, no
competing accents. 简单、安静、熟悉。
</p>
<div class="hero-meta" role="status" aria-label="Service status · 服务状态">
<div class="row-between">
<span class="body-sm">服务状态 · Service</span>
<span class="badge badge-success">
<span class="badge-dot" aria-hidden="true"></span>
正常 Operational
</span>
</div>
<p class="body-xs">
Last reviewed <time datetime="2026-05-18">2026-05-18</time> ·
Press <kbd></kbd> <kbd>K</kbd> to search tokens.
</p>
</div>
</div>
<form class="form" onsubmit="event.preventDefault();">
<div class="field">
<label for="wxid">微信号 · WeChat ID</label>
<input id="wxid" type="text" placeholder="wxid_xxxxxxxx" autocomplete="username" required />
<p class="field-help">仅用于身份验证 — used for sign-in only.</p>
</div>
<div class="field">
<label for="phone">手机号 · Mobile</label>
<input id="phone" type="tel" placeholder="+86 138 0000 0000" autocomplete="tel" />
<p class="field-help">We'll send a verification code · 我们会发送验证码。</p>
</div>
<div class="form-actions">
<button type="submit" class="btn btn-primary">登录 · Sign in</button>
<button type="button" class="btn btn-secondary">扫码登录</button>
</div>
</form>
</div>
</section>
</main>
</body>
</html>