mirror of
https://github.com/nexu-io/open-design.git
synced 2026-05-31 19:04:39 +07:00
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>
This commit is contained in:
parent
0026dfa344
commit
336620e06f
20 changed files with 8062 additions and 0 deletions
489
design-systems/airtable/components.html
Normal file
489
design-systems/airtable/components.html
Normal file
|
|
@ -0,0 +1,489 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Airtable — reference components</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="Reference fixture for design-systems/airtable. White canvas, Deep Navy text,
|
||||
Airtable Blue accent, Haas typography, blue-tinted multi-layer card shadow."
|
||||
/>
|
||||
|
||||
<style>
|
||||
:root {
|
||||
--bg: #ffffff;
|
||||
--surface: #ffffff;
|
||||
--surface-warm: var(--surface);
|
||||
|
||||
--fg: #181d26;
|
||||
--fg-2: #333333;
|
||||
--muted: rgba(4, 14, 32, 0.69);
|
||||
--meta: var(--muted);
|
||||
|
||||
--border: #e0e2e6;
|
||||
--border-soft: #eef0f3;
|
||||
|
||||
--accent: #1b61c9;
|
||||
--accent-on: #ffffff;
|
||||
--accent-hover: #254fad;
|
||||
--accent-active: color-mix(in oklab, var(--accent), black 14%);
|
||||
|
||||
--success: #006400;
|
||||
--warn: #eab308;
|
||||
--danger: #dc2626;
|
||||
|
||||
--font-display: "Haas Groot Disp", Haas, -apple-system, system-ui, "Segoe UI", Roboto, sans-serif;
|
||||
--font-body: Haas, -apple-system, system-ui, "Segoe UI", Roboto, sans-serif;
|
||||
--font-mono: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Monaco, Consolas, monospace;
|
||||
|
||||
--text-xs: 12px;
|
||||
--text-sm: 14px;
|
||||
--text-base: 16px;
|
||||
--text-lg: 20px;
|
||||
--text-xl: 24px;
|
||||
--text-2xl: 32px;
|
||||
--text-3xl: 40px;
|
||||
--text-4xl: 48px;
|
||||
|
||||
--leading-body: 1.35;
|
||||
--leading-tight: 1.2;
|
||||
--tracking-display: 0;
|
||||
|
||||
--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: 96px;
|
||||
--section-y-tablet: 64px;
|
||||
--section-y-phone: 48px;
|
||||
|
||||
--radius-sm: 12px;
|
||||
--radius-md: 16px;
|
||||
--radius-lg: 24px;
|
||||
--radius-pill: 9999px;
|
||||
|
||||
--elev-flat: none;
|
||||
--elev-ring: 0 0 0 1px var(--border);
|
||||
--elev-raised:
|
||||
0 0 1px rgba(0, 0, 0, 0.32),
|
||||
0 0 2px rgba(0, 0, 0, 0.08),
|
||||
0 1px 3px rgba(45, 127, 249, 0.28),
|
||||
inset 0 0 0 0.5px rgba(0, 0, 0, 0.06);
|
||||
|
||||
--focus-ring: 0 0 0 3px color-mix(in oklab, var(--accent), transparent 70%);
|
||||
|
||||
--motion-fast: 150ms;
|
||||
--motion-base: 200ms;
|
||||
--ease-standard: cubic-bezier(0.2, 0, 0, 1);
|
||||
|
||||
--container-max: 1200px;
|
||||
--container-gutter-desktop: 24px;
|
||||
--container-gutter-tablet: 16px;
|
||||
--container-gutter-phone: 12px;
|
||||
}
|
||||
|
||||
/* ─── Reset ─────────────────────────────────────────────── */
|
||||
*, *::before, *::after { box-sizing: border-box; }
|
||||
html, body { margin: 0; padding: 0; }
|
||||
body {
|
||||
background: var(--bg);
|
||||
color: var(--fg-2);
|
||||
font-family: var(--font-body);
|
||||
font-size: var(--text-base);
|
||||
line-height: var(--leading-body);
|
||||
letter-spacing: 0.01em;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
/* ─── 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 — Haas / Groot Disp, navy headings ─────── */
|
||||
h1, h2, h3 {
|
||||
font-family: var(--font-display);
|
||||
color: var(--fg);
|
||||
line-height: var(--leading-tight);
|
||||
letter-spacing: var(--tracking-display);
|
||||
margin: 0;
|
||||
}
|
||||
h1 { font-size: var(--text-4xl); font-weight: 500; }
|
||||
h2 { font-size: var(--text-3xl); font-weight: 500; }
|
||||
h3 { font-size: var(--text-xl); font-weight: 500; letter-spacing: 0.12px; }
|
||||
p { margin: 0; }
|
||||
.lead {
|
||||
font-size: var(--text-lg);
|
||||
color: var(--fg-2);
|
||||
line-height: 1.5;
|
||||
letter-spacing: 0.1px;
|
||||
}
|
||||
.body-muted { color: var(--muted); }
|
||||
.body-sm { font-size: var(--text-sm); letter-spacing: 0.07px; }
|
||||
.eyebrow {
|
||||
font-size: var(--text-xs);
|
||||
color: var(--accent);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.28px;
|
||||
font-weight: 500;
|
||||
}
|
||||
.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 — 12px radius, positive tracking ──────────── */
|
||||
.btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--space-2);
|
||||
padding: 14px 24px;
|
||||
border-radius: var(--radius-sm);
|
||||
font-family: var(--font-body);
|
||||
font-size: var(--text-base);
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.08px;
|
||||
line-height: 1.25;
|
||||
cursor: pointer;
|
||||
border: 1px solid transparent;
|
||||
transition:
|
||||
background-color var(--motion-fast) var(--ease-standard),
|
||||
border-color var(--motion-fast) var(--ease-standard),
|
||||
box-shadow 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: #ffffff;
|
||||
color: var(--fg);
|
||||
border-color: var(--border);
|
||||
}
|
||||
.btn-secondary:hover { border-color: var(--fg); }
|
||||
|
||||
/* ─── Inputs — 12px radius, navy-on-white ───────────────── */
|
||||
.field { display: flex; flex-direction: column; gap: var(--space-2); }
|
||||
.field label {
|
||||
font-size: var(--text-sm);
|
||||
font-weight: 500;
|
||||
color: var(--fg);
|
||||
letter-spacing: 0.07px;
|
||||
}
|
||||
.field input {
|
||||
padding: 12px 14px;
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid var(--border);
|
||||
background: var(--surface);
|
||||
color: var(--fg);
|
||||
font-family: var(--font-body);
|
||||
font-size: var(--text-base);
|
||||
letter-spacing: 0.08px;
|
||||
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(--muted); letter-spacing: 0.07px; }
|
||||
|
||||
/* ─── Cards — 16px radius, blue-tinted shadow ───────────── */
|
||||
.card {
|
||||
background: var(--surface);
|
||||
border-radius: var(--radius-md);
|
||||
padding: var(--space-6);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-3);
|
||||
box-shadow: var(--elev-raised);
|
||||
transition: transform var(--motion-base) var(--ease-standard);
|
||||
}
|
||||
.card:hover { transform: translateY(-2px); }
|
||||
.card .card-icon {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: var(--radius-sm);
|
||||
background: color-mix(in oklab, var(--accent), transparent 88%);
|
||||
color: var(--accent);
|
||||
margin-block-end: var(--space-2);
|
||||
}
|
||||
.card-link {
|
||||
color: var(--accent);
|
||||
font-weight: 500;
|
||||
font-size: var(--text-sm);
|
||||
letter-spacing: 0.07px;
|
||||
text-decoration: none;
|
||||
margin-block-start: var(--space-2);
|
||||
}
|
||||
.card-link:hover { text-decoration: underline; text-underline-offset: 3px; }
|
||||
|
||||
/* ─── Badges ────────────────────────────────────────────── */
|
||||
.badge {
|
||||
display: inline-flex; align-items: center; gap: var(--space-2);
|
||||
padding: 3px 10px;
|
||||
border-radius: var(--radius-pill);
|
||||
font-size: var(--text-xs);
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.14px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
.badge-success { color: var(--success); background: color-mix(in oklab, var(--success), transparent 88%); }
|
||||
.badge-muted { color: var(--fg-2); background: color-mix(in oklab, var(--fg), transparent 92%); }
|
||||
.badge-dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
|
||||
|
||||
/* ─── Base-color chips (Airtable's hallmark) ────────────── */
|
||||
.base-chips { display: flex; gap: var(--space-2); flex-wrap: wrap; }
|
||||
.base-chip {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--space-2);
|
||||
padding: 6px 12px;
|
||||
border-radius: var(--radius-pill);
|
||||
font-size: var(--text-xs);
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.14px;
|
||||
color: var(--fg);
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
.base-chip::before {
|
||||
content: "";
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
background: var(--chip-color, var(--accent));
|
||||
}
|
||||
|
||||
/* ─── Links ─────────────────────────────────────────────── */
|
||||
a { color: var(--accent); text-decoration: none; }
|
||||
a:hover { color: var(--accent-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(--fg-2);
|
||||
}
|
||||
|
||||
/* ─── Layout helpers ────────────────────────────────────── */
|
||||
.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-4);
|
||||
padding: var(--space-6);
|
||||
border-radius: var(--radius-md);
|
||||
background: var(--surface);
|
||||
box-shadow: var(--elev-raised);
|
||||
}
|
||||
.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; } }
|
||||
.form-row { display: grid; grid-template-columns: 1fr 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: 440px; }
|
||||
.form-actions { display: flex; gap: var(--space-3); margin-block-start: var(--space-2); }
|
||||
.icon { width: 16px; height: 16px; flex-shrink: 0; }
|
||||
.icon-lg { width: 22px; height: 22px; flex-shrink: 0; }
|
||||
.row-between { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); }
|
||||
.divider { height: 1px; background: var(--border-soft); }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main class="container">
|
||||
<section data-od-id="hero">
|
||||
<div class="hero-grid">
|
||||
<div class="stack-4">
|
||||
<p class="eyebrow">Reference fixture · airtable</p>
|
||||
<h1 style="font-size: 56px; line-height: 1.1">
|
||||
Build the apps your team needs — without writing code.
|
||||
</h1>
|
||||
<p class="lead" style="max-width: 56ch">
|
||||
Turn any workflow into a custom app. Airtable connects your data,
|
||||
your processes, and your people in a single workspace that scales
|
||||
from a side project to enterprise-wide.
|
||||
</p>
|
||||
<div class="hero-actions">
|
||||
<a href="./tokens.css" class="btn btn-primary">
|
||||
Start building free
|
||||
<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">Watch the demo</a>
|
||||
</div>
|
||||
<p class="body-sm body-muted" style="margin-block-start: var(--space-2)">
|
||||
Trusted by 500,000+ organizations · No credit card required
|
||||
</p>
|
||||
</div>
|
||||
<aside class="hero-meta" aria-label="Workspace status">
|
||||
<div class="row-between">
|
||||
<span class="body-sm" style="color: var(--fg); font-weight: 500">
|
||||
Marketing campaigns
|
||||
</span>
|
||||
<span class="badge badge-success">
|
||||
<span class="badge-dot" aria-hidden="true"></span>
|
||||
Synced
|
||||
</span>
|
||||
</div>
|
||||
<div class="divider"></div>
|
||||
<div class="stack-3">
|
||||
<p class="body-sm" style="color: var(--fg-2); font-weight: 500">
|
||||
Your bases
|
||||
</p>
|
||||
<div class="base-chips">
|
||||
<span class="base-chip" style="--chip-color: #1b61c9">Product</span>
|
||||
<span class="base-chip" style="--chip-color: #f5a623">Marketing</span>
|
||||
<span class="base-chip" style="--chip-color: #ec4899">Design</span>
|
||||
<span class="base-chip" style="--chip-color: #16a34a">Sales</span>
|
||||
</div>
|
||||
</div>
|
||||
<p class="body-sm body-muted">
|
||||
Press <kbd>⌘</kbd> <kbd>K</kbd> to jump between bases.
|
||||
</p>
|
||||
</aside>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section data-od-id="features">
|
||||
<div class="stack-3">
|
||||
<p class="eyebrow">One workspace, every workflow</p>
|
||||
<h2 style="max-width: 24ch">
|
||||
Bring data, automations, and interfaces together.
|
||||
</h2>
|
||||
<p class="lead" style="max-width: 60ch; color: var(--fg-2)">
|
||||
Configure once, deploy everywhere. Every base, app, and automation
|
||||
stays in sync — across teams, devices, and stakeholders.
|
||||
</p>
|
||||
</div>
|
||||
<div class="features-grid" style="margin-block-start: var(--space-8)">
|
||||
<article class="card">
|
||||
<span class="card-icon" aria-hidden="true">
|
||||
<svg class="icon-lg" viewBox="0 0 24 24" fill="none"
|
||||
stroke="currentColor" stroke-width="1.75"
|
||||
stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="3" y="4" width="18" height="16" rx="2"/>
|
||||
<path d="M3 10h18M9 4v16"/>
|
||||
</svg>
|
||||
</span>
|
||||
<h3>Flexible bases</h3>
|
||||
<p class="body-muted">
|
||||
Spreadsheets meet databases. Color-code records, link tables,
|
||||
and surface the views each teammate actually needs.
|
||||
</p>
|
||||
<a href="./tokens.css" class="card-link">Explore bases →</a>
|
||||
</article>
|
||||
<article class="card">
|
||||
<span class="card-icon" aria-hidden="true">
|
||||
<svg class="icon-lg" viewBox="0 0 24 24" fill="none"
|
||||
stroke="currentColor" stroke-width="1.75"
|
||||
stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M12 2v6M12 16v6M4.93 4.93l4.24 4.24M14.83 14.83l4.24 4.24M2 12h6M16 12h6M4.93 19.07l4.24-4.24M14.83 9.17l4.24-4.24"/>
|
||||
</svg>
|
||||
</span>
|
||||
<h3>Automations that run themselves</h3>
|
||||
<p class="body-muted">
|
||||
Trigger on a status change, a form submit, or a scheduled hour.
|
||||
Connect Slack, Gmail, and 50+ apps without writing a line of code.
|
||||
</p>
|
||||
<a href="./DESIGN.md" class="card-link">See automations →</a>
|
||||
</article>
|
||||
<article class="card">
|
||||
<span class="card-icon" aria-hidden="true">
|
||||
<svg class="icon-lg" 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="18" height="18" rx="3"/>
|
||||
<path d="M3 9h18M9 9v12"/>
|
||||
</svg>
|
||||
</span>
|
||||
<h3>Interfaces for every role</h3>
|
||||
<p class="body-muted">
|
||||
Wrap any base in a tailored interface — dashboards for execs,
|
||||
forms for contractors, kanbans for project leads. One source of truth.
|
||||
</p>
|
||||
<a href="./tokens.css" class="card-link">Build an interface →</a>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section data-od-id="form">
|
||||
<div class="form-row">
|
||||
<div class="stack-4">
|
||||
<p class="eyebrow">Get started in 90 seconds</p>
|
||||
<h2>Spin up your first base.</h2>
|
||||
<p class="body-muted" style="max-width: 48ch; font-size: var(--text-lg); line-height: 1.5">
|
||||
Drop in your work email and we'll send a starter base template,
|
||||
the API quick-start, and a 14-day Pro trial — no credit card needed.
|
||||
</p>
|
||||
<ul class="stack-3" style="list-style: none; padding: 0; margin: 0; color: var(--fg-2)">
|
||||
<li style="display: flex; gap: var(--space-2); align-items: center">
|
||||
<svg class="icon" viewBox="0 0 24 24" fill="none"
|
||||
stroke="var(--accent)" stroke-width="2.25"
|
||||
stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
||||
<path d="M5 13l4 4L19 7"/>
|
||||
</svg>
|
||||
Unlimited bases on the free plan
|
||||
</li>
|
||||
<li style="display: flex; gap: var(--space-2); align-items: center">
|
||||
<svg class="icon" viewBox="0 0 24 24" fill="none"
|
||||
stroke="var(--accent)" stroke-width="2.25"
|
||||
stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
||||
<path d="M5 13l4 4L19 7"/>
|
||||
</svg>
|
||||
Native integrations with Slack, Gmail, Jira
|
||||
</li>
|
||||
<li style="display: flex; gap: var(--space-2); align-items: center">
|
||||
<svg class="icon" viewBox="0 0 24 24" fill="none"
|
||||
stroke="var(--accent)" stroke-width="2.25"
|
||||
stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
||||
<path d="M5 13l4 4L19 7"/>
|
||||
</svg>
|
||||
SOC 2 Type II, HIPAA-ready on Enterprise
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<form class="form" onsubmit="event.preventDefault();">
|
||||
<div class="field">
|
||||
<label for="email">Work email</label>
|
||||
<input id="email" type="email" placeholder="you@company.com" autocomplete="email" required />
|
||||
<p class="field-help">We'll send the starter base and API keys to this address.</p>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="team">Team size</label>
|
||||
<input id="team" type="text" placeholder="e.g. 1–10, 50, 500+" />
|
||||
<p class="field-help">Helps us recommend the right plan and templates.</p>
|
||||
</div>
|
||||
<div class="form-actions">
|
||||
<button type="submit" class="btn btn-primary">Create my workspace</button>
|
||||
<button type="button" class="btn btn-secondary">Talk to sales</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
261
design-systems/airtable/tokens.css
Normal file
261
design-systems/airtable/tokens.css
Normal file
|
|
@ -0,0 +1,261 @@
|
|||
/* ─────────────────────────────────────────────────────────────────────────
|
||||
* design-systems/airtable/tokens.css
|
||||
*
|
||||
* Structured token bindings for "Design System Inspired by Airtable" —
|
||||
* the spreadsheet-database hybrid that talks to enterprise without
|
||||
* shouting. A white canvas with deep-navy text, Airtable Blue as the
|
||||
* single chromatic accent, Haas Swiss-precision typography, and a
|
||||
* signature blue-tinted multi-layer shadow that makes cards feel
|
||||
* built into the page rather than dropped onto it.
|
||||
*
|
||||
* Brand-specific schema decisions (each one bends a schema convention
|
||||
* to match Airtable's voice rather than the cross-brand default):
|
||||
*
|
||||
* 1. `--accent` is Airtable Blue (#1b61c9), not deep navy. Deep
|
||||
* navy is already `--fg`; the brand expresses primary CTAs by
|
||||
* using `--accent` as the button background and reserves
|
||||
* navy-on-white for headlines and body. `--accent-hover` binds
|
||||
* to the documented "Mid Blue" #254fad rather than a generic
|
||||
* color-mix darken — Airtable explicitly differentiates the
|
||||
* hover tier as a discrete brand tone, not a calculated shade.
|
||||
*
|
||||
* 2. `--fg` is `#181d26` (Deep Navy), not `#000000`. The faint
|
||||
* blue undertone at the top of the neutral ramp is part of the
|
||||
* brand — pure black would clash with Airtable Blue and make
|
||||
* the page read as harsh against the white canvas. `--fg-2`
|
||||
* drops to `#333333` (documented Dark Gray) for body
|
||||
* description; `--muted` binds to the documented Weak Text
|
||||
* `rgba(4, 14, 32, 0.69)` so cross-brand `var(--muted)` calls
|
||||
* land on Airtable's actual semitransparent navy tier instead
|
||||
* of a flat gray.
|
||||
*
|
||||
* 3. `--surface-warm` aliases to `--surface` because Airtable has
|
||||
* no warm tier — the canvas is white and the lifted surface is
|
||||
* the documented `#f8fafc` light-surface, both cool. Adding a
|
||||
* warm sibling would invent a tone the brand does not use.
|
||||
*
|
||||
* 4. `--elev-raised` is the full multi-layer Airtable shadow stack
|
||||
* reproduced VERBATIM from DESIGN.md §6: a 1px dark hairline, a
|
||||
* 2px ambient, a 3px blue-tinted (rgba(45,127,249,0.28))
|
||||
* "Airtable glow" at 1px y-offset, and a 0.5px inner ring. The
|
||||
* blue tint is the brand signature — it makes cards feel
|
||||
* authored by the same hand as the Airtable Blue CTAs, instead
|
||||
* of a generic gray-shadow drop. Never drop the third layer
|
||||
* when overriding this token.
|
||||
*
|
||||
* 5. Radius scale binds 12 / 16 / 24 / 9999 (sm / md / lg / pill)
|
||||
* per DESIGN.md §5. Buttons sit at 12px (documented), cards at
|
||||
* 16px, large feature containers at 24px. The 2px "sharp"
|
||||
* cookie-consent radius is component-specific and inlined
|
||||
* where needed, not promoted to the schema. The 32px "large"
|
||||
* radius is also section-specific; binding it to `--radius-lg`
|
||||
* would over-round the typical card surface.
|
||||
*
|
||||
* 6. `--leading-body` is 1.35 (DESIGN.md body 18px / 24px), not
|
||||
* the schema-conventional 1.5. Airtable runs body copy at a
|
||||
* slightly tighter rhythm so dense product UI (table rows,
|
||||
* record cards) does not feel sparse. `--leading-tight` is 1.2
|
||||
* for headings, matching the documented 1.15–1.25 band.
|
||||
*
|
||||
* 7. `--tracking-display` is `0` (normal), not negative. DESIGN.md
|
||||
* §3 explicitly lists display headings as `letter-spacing:
|
||||
* normal` and body text as POSITIVE tracking (0.08–0.28px).
|
||||
* The Haas family is engineered for positive tracking; mapping
|
||||
* display to a negative em would fight the typeface. Body-tier
|
||||
* positive tracking is applied at the component level (button,
|
||||
* caption) since `--tracking-display` only covers display sizes.
|
||||
*
|
||||
* 8. `--success` is `#006400` (documented `--theme_success-text`),
|
||||
* a darker forest green than the schema's #16a34a. Airtable's
|
||||
* semantic green is intentionally deeper so it reads as a
|
||||
* "confirmed / saved" tone against the white canvas rather
|
||||
* than a vibrant alert.
|
||||
*
|
||||
* 9. Section rhythm is 96 / 64 / 48 (desktop / tablet / phone).
|
||||
* DESIGN.md does not pin section padding numerically but the
|
||||
* site's airy enterprise voice asks for generous vertical
|
||||
* space — this matches the documented 8px-base spacing scale
|
||||
* (96 = 12 × 8) without inventing a new tier. Container caps
|
||||
* at 1200px, consistent with Airtable's documented
|
||||
* 425–1664px responsive band centered on a desktop sweet spot.
|
||||
*
|
||||
* Source contracts:
|
||||
* - Standard token names: design-systems/_schema/tokens.schema.ts
|
||||
* - A2 fallback parity: design-systems/_schema/defaults.css
|
||||
* - Lint enforcement: apps/daemon/src/lint-artifact.ts
|
||||
*
|
||||
* Keep this file additive: never invent token names not also
|
||||
* documented in DESIGN.md or the schema. The Haas family does not
|
||||
* need a CDN reference here — the font stack lists OS fallbacks
|
||||
* (`-apple-system, system-ui, Segoe UI, Roboto`) per DESIGN.md §3
|
||||
* so artifacts render acceptably even when Haas is not loaded.
|
||||
* ─────────────────────────────────────────────────────────────────── */
|
||||
|
||||
:root {
|
||||
/* ─── Surface ─────────────────────────────────────────────────────
|
||||
* White canvas, white lifted card. Airtable explicitly does not
|
||||
* vary background between sections — depth comes from the
|
||||
* blue-tinted shadow stack, not from tinting one surface against
|
||||
* another. `--surface-warm` aliases to surface because the brand
|
||||
* has no warm tier (DESIGN.md §2 lists only cool neutrals). */
|
||||
--bg: #ffffff; /* primary canvas */
|
||||
--surface: #ffffff; /* card / lifted container */
|
||||
--surface-warm: var(--surface); /* alias — Airtable has no warm tier */
|
||||
|
||||
/* ─── Foreground ──────────────────────────────────────────────────
|
||||
* Deep Navy → Dark Gray → Weak Text (semitransparent navy) →
|
||||
* tertiary alias. The four-tier ramp matches DESIGN.md §2 + §6
|
||||
* exactly: `#181d26` headings, `#333333` body, the documented
|
||||
* `rgba(4, 14, 32, 0.69)` weak tier for captions, and `--meta`
|
||||
* aliasing to muted because Airtable does not differentiate a
|
||||
* fourth tertiary tone. */
|
||||
--fg: #181d26; /* Deep Navy — primary text */
|
||||
--fg-2: #333333; /* Dark Gray — secondary text / body */
|
||||
--muted: rgba(4, 14, 32, 0.69); /* Weak Text — captions, meta */
|
||||
--meta: var(--muted); /* alias — no distinct tertiary tier */
|
||||
|
||||
/* ─── Border ──────────────────────────────────────────────────────
|
||||
* `#e0e2e6` is the documented card-border tone from DESIGN.md §4.
|
||||
* `--border-soft` drops to a lighter `#eef0f3` for inner row
|
||||
* separators (table rows, list dividers) that must not visually
|
||||
* compete with the outer card edge. */
|
||||
--border: #e0e2e6; /* card / input edge */
|
||||
--border-soft: #eef0f3; /* inner row separator */
|
||||
|
||||
/* ─── Accent ──────────────────────────────────────────────────────
|
||||
* Airtable Blue — the single chromatic accent across the brand.
|
||||
* Used for primary CTAs, links, focus rings, and active-state
|
||||
* highlights. Hard cap of ≤2 visible uses per screen (lint
|
||||
* enforced); decorative blue is forbidden because the brand reads
|
||||
* the accent as "this is the action".
|
||||
*
|
||||
* `--accent-hover` binds to the documented "Mid Blue" #254fad
|
||||
* rather than a generic color-mix darken — Airtable explicitly
|
||||
* differentiates the hover tier as a discrete brand tone. */
|
||||
--accent: #1b61c9; /* Airtable Blue — CTA, links */
|
||||
--accent-on: #ffffff; /* white label on blue */
|
||||
--accent-hover: #254fad; /* Mid Blue — documented hover tone */
|
||||
--accent-active: color-mix(in oklab, var(--accent), black 14%);
|
||||
|
||||
/* ─── Semantic ────────────────────────────────────────────────────
|
||||
* `--success` is the documented `--theme_success-text` (#006400),
|
||||
* a deeper forest green than the schema's #16a34a — Airtable's
|
||||
* green reads as "confirmed / saved" against white, not as a
|
||||
* vivid alert. Warn / danger inherit schema defaults because the
|
||||
* brand has no defined yellow / red. */
|
||||
--success: #006400; /* documented Success Green */
|
||||
--warn: #eab308;
|
||||
--danger: #dc2626;
|
||||
|
||||
/* ─── Typography ──────────────────────────────────────────────────
|
||||
* Haas (display + Groot Disp variant) is Airtable's proprietary
|
||||
* Swiss family. The OS fallbacks `-apple-system, system-ui,
|
||||
* Segoe UI, Roboto` are documented in DESIGN.md §3 and ensure
|
||||
* artifacts render legibly when Haas is not loaded. The Groot
|
||||
* Disp variant carries display headings; Haas regular carries
|
||||
* body and UI. */
|
||||
--font-display: "Haas Groot Disp", Haas, -apple-system, system-ui, "Segoe UI", Roboto, sans-serif;
|
||||
--font-body: Haas, -apple-system, system-ui, "Segoe UI", Roboto, sans-serif;
|
||||
--font-mono: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Monaco, Consolas, monospace;
|
||||
|
||||
/* Type scale — derived from DESIGN.md §3 hierarchy. The scale
|
||||
* tops out at 48px (display hero); Airtable reads as "sophisticated
|
||||
* simplicity" because hierarchy comes from weight + tracking
|
||||
* variation within the Haas family, not from inflated px counts. */
|
||||
--text-xs: 12px; /* metadata, tags */
|
||||
--text-sm: 14px; /* caption, small UI */
|
||||
--text-base: 16px; /* body medium, button */
|
||||
--text-lg: 20px; /* feature copy */
|
||||
--text-xl: 24px; /* card title */
|
||||
--text-2xl: 32px; /* sub-heading */
|
||||
--text-3xl: 40px; /* section heading */
|
||||
--text-4xl: 48px; /* display hero */
|
||||
|
||||
/* `--leading-body` is 1.35 (DESIGN.md body 18px / 24px), not 1.5.
|
||||
* Dense product UI (table rows, record cards) needs the tighter
|
||||
* rhythm so rows do not feel sparse. `--leading-tight` is 1.2
|
||||
* for headings, matching the documented 1.15–1.25 band.
|
||||
* `--tracking-display` is `0` because DESIGN.md §3 explicitly
|
||||
* lists display headings as `letter-spacing: normal`; positive
|
||||
* body tracking (0.08–0.28px) is applied per-component. */
|
||||
--leading-body: 1.35;
|
||||
--leading-tight: 1.2;
|
||||
--tracking-display: 0;
|
||||
|
||||
/* ─── Spacing ─────────────────────────────────────────────────────
|
||||
* 8px base unit per DESIGN.md §5 (1–48px range). The 4/8/12/16/
|
||||
* 20/24/32/48 tier covers structural rhythm; finer sub-tier
|
||||
* increments (1–3px micro-padding inside chips and badges) are
|
||||
* inlined per-component because they are too granular for the
|
||||
* shared schema. */
|
||||
--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 rhythm — Airtable's enterprise voice asks for generous
|
||||
* vertical space between marketing sections without going gallery-
|
||||
* empty. 96 desktop / 64 tablet / 48 phone matches the 8px base
|
||||
* (96 = 12 × 8) and stays within the documented responsive band. */
|
||||
--section-y-desktop: 96px;
|
||||
--section-y-tablet: 64px;
|
||||
--section-y-phone: 48px;
|
||||
|
||||
/* ─── Radius ──────────────────────────────────────────────────────
|
||||
* DESIGN.md §5 radius scale: 2 / 12 / 16 / 24 / 32 / 50%. We bind
|
||||
* the four schema tiers to: 12 (button/input, documented), 16
|
||||
* (card, documented), 24 (large feature container, documented),
|
||||
* 9999 (pill). The 2px "sharp" cookie-consent radius is
|
||||
* component-specific and inlined where it appears; the 32px
|
||||
* section radius would over-round typical surfaces and is
|
||||
* intentionally not bound here. */
|
||||
--radius-sm: 12px; /* buttons, inputs, chips */
|
||||
--radius-md: 16px; /* cards, modals */
|
||||
--radius-lg: 24px; /* featured containers */
|
||||
--radius-pill: 9999px; /* badges, avatars */
|
||||
|
||||
/* ─── Elevation ───────────────────────────────────────────────────
|
||||
* Airtable's signature: blue-tinted multi-layer shadow that makes
|
||||
* cards feel authored by the same hand as the Airtable Blue CTAs.
|
||||
* Four layers reproduced verbatim from DESIGN.md §6:
|
||||
* 1px dark hairline (defines the edge)
|
||||
* 2px ambient (soft surround)
|
||||
* 3px blue glow @ 1y (the Airtable signature — never drop)
|
||||
* 0.5px inset ring (subtle inner highlight) */
|
||||
--elev-flat: none;
|
||||
--elev-ring: 0 0 0 1px var(--border);
|
||||
--elev-raised:
|
||||
0 0 1px rgba(0, 0, 0, 0.32),
|
||||
0 0 2px rgba(0, 0, 0, 0.08),
|
||||
0 1px 3px rgba(45, 127, 249, 0.28),
|
||||
inset 0 0 0 0.5px rgba(0, 0, 0, 0.06);
|
||||
|
||||
/* ─── Focus ring ──────────────────────────────────────────────────
|
||||
* 3px Airtable-Blue alpha glow — keyboard focus reads as
|
||||
* "actionable" without competing with the saturated CTA fill.
|
||||
* Matches schema default formula so cross-brand focus styles
|
||||
* remain visually consistent. */
|
||||
--focus-ring: 0 0 0 3px color-mix(in oklab, var(--accent), transparent 70%);
|
||||
|
||||
/* ─── Motion ──────────────────────────────────────────────────────
|
||||
* Standard durations + curve. Airtable's product UI uses motion
|
||||
* to confirm direct manipulation (cell edits, drag-and-drop),
|
||||
* not to choreograph entrances — 150/200ms feels responsive
|
||||
* without dragging. */
|
||||
--motion-fast: 150ms;
|
||||
--motion-base: 200ms;
|
||||
--ease-standard: cubic-bezier(0.2, 0, 0, 1);
|
||||
|
||||
/* ─── Layout ──────────────────────────────────────────────────────
|
||||
* 1200px max content width — sits in the middle of Airtable's
|
||||
* documented 425–1664px responsive band. Gutters narrow on
|
||||
* mobile to preserve line length on dense marketing copy. */
|
||||
--container-max: 1200px;
|
||||
--container-gutter-desktop: 24px;
|
||||
--container-gutter-tablet: 16px;
|
||||
--container-gutter-phone: 12px;
|
||||
}
|
||||
551
design-systems/bmw/components.html
Normal file
551
design-systems/bmw/components.html
Normal file
|
|
@ -0,0 +1,551 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>BMW — reference components</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="Reference fixture for design-systems/bmw. White-and-dark showroom rhythm,
|
||||
BMW Blue (#1c69d4) as singular accent, zero border-radius, 60px uppercase display
|
||||
in BMWTypeNextLatin Light (weight 300)."
|
||||
/>
|
||||
|
||||
<style>
|
||||
:root {
|
||||
--bg: #ffffff;
|
||||
--surface: #f5f5f5;
|
||||
--surface-warm: var(--surface);
|
||||
|
||||
--fg: #262626;
|
||||
--fg-2: var(--fg);
|
||||
--muted: #757575;
|
||||
--meta: #bbbbbb;
|
||||
|
||||
--border: #e0e0e0;
|
||||
--border-soft: var(--border);
|
||||
|
||||
--accent: #1c69d4;
|
||||
--accent-on: #ffffff;
|
||||
--accent-hover: #0653b6;
|
||||
--accent-active: color-mix(in oklab, var(--accent), black 18%);
|
||||
|
||||
--success: #16a34a;
|
||||
--warn: #eab308;
|
||||
--danger: #dc2626;
|
||||
|
||||
--font-display: "BMWTypeNextLatin Light", Helvetica, Arial, "Hiragino Kaku Gothic ProN", "Hiragino Sans", Meiryo, sans-serif;
|
||||
--font-body: BMWTypeNextLatin, Helvetica, Arial, "Hiragino Kaku Gothic ProN", "Hiragino Sans", Meiryo, sans-serif;
|
||||
--font-mono: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Monaco, Consolas, monospace;
|
||||
|
||||
--text-xs: 12px;
|
||||
--text-sm: 14px;
|
||||
--text-base: 16px;
|
||||
--text-lg: 18px;
|
||||
--text-xl: 24px;
|
||||
--text-2xl: 32px;
|
||||
--text-3xl: 44px;
|
||||
--text-4xl: 60px;
|
||||
|
||||
--leading-body: 1.15;
|
||||
--leading-tight: 1.30;
|
||||
--tracking-display: 0;
|
||||
|
||||
--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: 96px;
|
||||
--section-y-tablet: 64px;
|
||||
--section-y-phone: 40px;
|
||||
|
||||
--radius-sm: 0;
|
||||
--radius-md: 0;
|
||||
--radius-lg: 0;
|
||||
--radius-pill: 0;
|
||||
|
||||
--elev-flat: none;
|
||||
--elev-ring: 0 0 0 1px var(--border);
|
||||
--elev-raised: none;
|
||||
|
||||
--focus-ring: 0 0 0 2px var(--accent-hover);
|
||||
|
||||
--motion-fast: 150ms;
|
||||
--motion-base: 200ms;
|
||||
--ease-standard: cubic-bezier(0.2, 0, 0, 1);
|
||||
|
||||
--container-max: 1440px;
|
||||
--container-gutter-desktop: 32px;
|
||||
--container-gutter-tablet: 24px;
|
||||
--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;
|
||||
}
|
||||
|
||||
/* ─── Layout ─────────────────────────────────────────────── */
|
||||
.container {
|
||||
max-width: var(--container-max);
|
||||
margin-inline: auto;
|
||||
padding-inline: var(--container-gutter-desktop);
|
||||
}
|
||||
section { padding-block: var(--section-y-desktop); }
|
||||
@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 — weight extremes only (300 / 400 / 700 / 900) ─ */
|
||||
h1, h2, h3 {
|
||||
font-family: var(--font-display);
|
||||
line-height: var(--leading-tight);
|
||||
margin: 0;
|
||||
}
|
||||
/* Display Hero — BMWTypeNextLatin Light, weight 300, uppercase */
|
||||
h1 {
|
||||
font-size: var(--text-4xl);
|
||||
font-weight: 300;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: var(--tracking-display);
|
||||
}
|
||||
/* Section Heading — weight 400, sentence case */
|
||||
h2 {
|
||||
font-size: var(--text-2xl);
|
||||
font-weight: 400;
|
||||
font-family: var(--font-body);
|
||||
}
|
||||
/* Card title — weight 400 */
|
||||
h3 {
|
||||
font-size: var(--text-lg);
|
||||
font-weight: 400;
|
||||
font-family: var(--font-body);
|
||||
}
|
||||
p { margin: 0; line-height: var(--leading-body); }
|
||||
.lead {
|
||||
font-size: var(--text-lg);
|
||||
color: var(--muted);
|
||||
line-height: var(--leading-tight);
|
||||
font-weight: 300;
|
||||
}
|
||||
.body-muted { color: var(--muted); }
|
||||
.body-sm { font-size: var(--text-sm); }
|
||||
/* Eyebrow — weight 900 uppercase, the BMW nav-emphasis weight */
|
||||
.eyebrow {
|
||||
font-size: var(--text-xs);
|
||||
color: var(--accent);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.12em;
|
||||
font-weight: 900;
|
||||
}
|
||||
.stack-3 > * + * { margin-block-start: var(--space-3); }
|
||||
.stack-4 > * + * { margin-block-start: var(--space-4); }
|
||||
.stack-5 > * + * { margin-block-start: var(--space-5); }
|
||||
.stack-6 > * + * { margin-block-start: var(--space-6); }
|
||||
|
||||
/* ─── Hero — full-bleed dark showroom band ──────────────── */
|
||||
/* Lit-from-above radial: suggests automotive photography lighting
|
||||
without shipping a binary image asset. */
|
||||
.hero {
|
||||
background:
|
||||
radial-gradient(120% 80% at 50% 0%, #1a1a1a 0%, #0a0a0a 55%, #000000 100%);
|
||||
color: #ffffff;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
.hero::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: auto 0 0 0;
|
||||
height: 1px;
|
||||
background: var(--accent);
|
||||
}
|
||||
.hero .container { position: relative; }
|
||||
.hero-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1.5fr 1fr;
|
||||
gap: var(--space-12);
|
||||
align-items: end;
|
||||
}
|
||||
@media (max-width: 1023px) {
|
||||
.hero-grid { grid-template-columns: 1fr; gap: var(--space-8); }
|
||||
}
|
||||
.hero h1 { color: #ffffff; }
|
||||
.hero .lead { color: rgba(255, 255, 255, 0.72); }
|
||||
.hero .eyebrow { color: var(--accent); }
|
||||
.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-4);
|
||||
padding: var(--space-5);
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.18);
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.18);
|
||||
color: rgba(255, 255, 255, 0.85);
|
||||
}
|
||||
.hero-meta-row {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
justify-content: space-between;
|
||||
gap: var(--space-3);
|
||||
}
|
||||
.hero-meta-row dt {
|
||||
font-size: var(--text-xs);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.12em;
|
||||
font-weight: 900;
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
}
|
||||
.hero-meta-row dd {
|
||||
margin: 0;
|
||||
font-size: var(--text-lg);
|
||||
font-weight: 300;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
/* ─── Buttons — sharp rectangles, weight 700 ──────────────── */
|
||||
.btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--space-2);
|
||||
padding: 14px 24px;
|
||||
border-radius: var(--radius-sm);
|
||||
font-family: var(--font-body);
|
||||
font-size: var(--text-base);
|
||||
font-weight: 700;
|
||||
line-height: 1.2;
|
||||
cursor: pointer;
|
||||
border: 1px solid transparent;
|
||||
text-transform: none;
|
||||
transition:
|
||||
background-color var(--motion-fast) var(--ease-standard),
|
||||
color var(--motion-fast) var(--ease-standard),
|
||||
border-color var(--motion-fast) var(--ease-standard);
|
||||
text-decoration: none;
|
||||
}
|
||||
.btn:focus-visible { outline: none; box-shadow: var(--focus-ring); }
|
||||
/* Primary: BMW Blue solid */
|
||||
.btn-primary {
|
||||
background: var(--accent);
|
||||
color: var(--accent-on);
|
||||
border-color: var(--accent);
|
||||
}
|
||||
.btn-primary:hover {
|
||||
background: var(--accent-hover);
|
||||
border-color: var(--accent-hover);
|
||||
}
|
||||
.btn-primary:active {
|
||||
background: var(--accent-active);
|
||||
border-color: var(--accent-active);
|
||||
}
|
||||
/* Secondary on dark surface: ghost with white hairline that solidifies on hover */
|
||||
.btn-ghost {
|
||||
background: transparent;
|
||||
color: #ffffff;
|
||||
border-color: rgba(255, 255, 255, 0.6);
|
||||
}
|
||||
.btn-ghost:hover {
|
||||
background: #ffffff;
|
||||
color: var(--fg);
|
||||
border-color: #ffffff;
|
||||
}
|
||||
/* Secondary on light surface: outline dark */
|
||||
.btn-outline {
|
||||
background: transparent;
|
||||
color: var(--fg);
|
||||
border-color: var(--fg);
|
||||
}
|
||||
.btn-outline:hover {
|
||||
background: var(--fg);
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
/* ─── Features — 3 cards on white ─────────────────────────── */
|
||||
.features-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: var(--space-6);
|
||||
}
|
||||
@media (max-width: 1023px) {
|
||||
.features-grid { grid-template-columns: 1fr 1fr; }
|
||||
}
|
||||
@media (max-width: 639px) {
|
||||
.features-grid { grid-template-columns: 1fr; }
|
||||
}
|
||||
.card {
|
||||
background: var(--surface);
|
||||
border-radius: var(--radius-md);
|
||||
padding: var(--space-6);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-4);
|
||||
position: relative;
|
||||
}
|
||||
.card-index {
|
||||
font-family: var(--font-display);
|
||||
font-weight: 300;
|
||||
font-size: var(--text-2xl);
|
||||
color: var(--accent);
|
||||
line-height: 1;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
.card-link {
|
||||
color: var(--accent);
|
||||
font-weight: 700;
|
||||
font-size: var(--text-sm);
|
||||
text-decoration: none;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.08em;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--space-2);
|
||||
}
|
||||
.card-link:hover { color: var(--accent-hover); }
|
||||
.card-link:focus-visible { outline: none; box-shadow: var(--focus-ring); }
|
||||
|
||||
/* ─── Inputs — sharp corners, dark hairline border ───────── */
|
||||
.field { display: flex; flex-direction: column; gap: var(--space-2); }
|
||||
.field label {
|
||||
font-size: var(--text-xs);
|
||||
font-weight: 900;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.12em;
|
||||
color: var(--fg);
|
||||
}
|
||||
.field input {
|
||||
padding: 14px 16px;
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid var(--border);
|
||||
background: var(--bg);
|
||||
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(--muted); }
|
||||
|
||||
/* ─── Status pill (uses border-radius: 50% inline; no pill token) ─ */
|
||||
.status {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--space-2);
|
||||
font-size: var(--text-xs);
|
||||
font-weight: 900;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.12em;
|
||||
color: var(--success);
|
||||
}
|
||||
.status-dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background: currentColor;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/* ─── Layout helpers ────────────────────────────────────── */
|
||||
.form-row {
|
||||
display: grid;
|
||||
grid-template-columns: 1.2fr 1fr;
|
||||
gap: var(--space-12);
|
||||
align-items: start;
|
||||
}
|
||||
@media (max-width: 1023px) {
|
||||
.form-row { grid-template-columns: 1fr; gap: var(--space-6); }
|
||||
}
|
||||
.form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-5);
|
||||
max-width: 480px;
|
||||
}
|
||||
.form-actions {
|
||||
display: flex;
|
||||
gap: var(--space-3);
|
||||
margin-block-start: var(--space-2);
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.icon { width: 16px; height: 16px; flex-shrink: 0; }
|
||||
.divider {
|
||||
height: 1px;
|
||||
background: var(--border);
|
||||
border: 0;
|
||||
margin: 0;
|
||||
}
|
||||
section + section { border-top: 1px solid var(--border); }
|
||||
.hero + section { border-top: 0; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- ─── Hero — dark showroom band ───────────────────────────── -->
|
||||
<section class="hero" data-od-id="hero">
|
||||
<div class="container">
|
||||
<div class="hero-grid">
|
||||
<div class="stack-5">
|
||||
<p class="eyebrow">Reference fixture · bmw</p>
|
||||
<h1>Sheer driving pleasure.</h1>
|
||||
<p class="lead" style="max-width: 56ch">
|
||||
The Ultimate Driving Machine, rendered as a design system.
|
||||
White-and-dark showroom rhythm, BMW Blue for the moments that
|
||||
matter, and every corner cut sharp at exactly ninety degrees.
|
||||
</p>
|
||||
<div class="hero-actions">
|
||||
<a href="./tokens.css" class="btn btn-primary">
|
||||
View tokens
|
||||
<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-ghost">Read the spec</a>
|
||||
</div>
|
||||
</div>
|
||||
<aside class="hero-meta" aria-label="Vehicle telemetry">
|
||||
<div class="hero-meta-row">
|
||||
<dt>0 — 100 km/h</dt>
|
||||
<dd>3.9 s</dd>
|
||||
</div>
|
||||
<div class="hero-meta-row">
|
||||
<dt>Top speed</dt>
|
||||
<dd>250 km/h</dd>
|
||||
</div>
|
||||
<div class="hero-meta-row">
|
||||
<dt>Output</dt>
|
||||
<dd>460 kW</dd>
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ─── Features — three cards on white ─────────────────────── -->
|
||||
<section data-od-id="features">
|
||||
<div class="container">
|
||||
<div class="stack-4" style="max-width: 64ch">
|
||||
<p class="eyebrow" style="color: var(--accent)">Engineering principles</p>
|
||||
<h2>Precision is a discipline, not a finish.</h2>
|
||||
<p class="body-muted">
|
||||
Every token below maps to a deliberate BMW decision — sharp
|
||||
corners, weight extremes, and a single chromatic move.
|
||||
</p>
|
||||
</div>
|
||||
<div class="features-grid" style="margin-block-start: var(--space-8)">
|
||||
<article class="card">
|
||||
<span class="card-index">01</span>
|
||||
<h3>Zero radius, everywhere.</h3>
|
||||
<p class="body-muted body-sm">
|
||||
--radius-sm, --radius-md, --radius-lg and --radius-pill all
|
||||
resolve to 0. Sharp rectangles are the BMW identity; rounded
|
||||
corners would soften the engineering posture.
|
||||
</p>
|
||||
<a href="./tokens.css" class="card-link">
|
||||
Inspect radius
|
||||
<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>
|
||||
</article>
|
||||
<article class="card">
|
||||
<span class="card-index">02</span>
|
||||
<h3>Weight 300 meets weight 900.</h3>
|
||||
<p class="body-muted body-sm">
|
||||
BMWTypeNextLatin Light whispers at 60px display; weight 900
|
||||
shouts in uppercase navigation. The 300↔900 tension is the
|
||||
signature typographic gesture — nothing lives in between.
|
||||
</p>
|
||||
<a href="./DESIGN.md" class="card-link">
|
||||
Read the rule
|
||||
<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>
|
||||
</article>
|
||||
<article class="card">
|
||||
<span class="card-index">03</span>
|
||||
<h3>BMW Blue, sparingly applied.</h3>
|
||||
<p class="body-muted body-sm">
|
||||
#1c69d4 lights up only what is interactive — buttons, links,
|
||||
focus rings. The rest of the page stays in pure white, near
|
||||
black, and the cinematic dark of the hero band.
|
||||
</p>
|
||||
<a href="./tokens.css" class="card-link">
|
||||
Inspect accent
|
||||
<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>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ─── Form — request a test drive ─────────────────────────── -->
|
||||
<section data-od-id="form">
|
||||
<div class="container">
|
||||
<div class="form-row">
|
||||
<div class="stack-4">
|
||||
<p class="eyebrow">Configurator</p>
|
||||
<h2>Book a test drive.</h2>
|
||||
<p class="body-muted" style="max-width: 48ch">
|
||||
Inputs in the BMW system carry the same rules as everything
|
||||
else — sharp 0-radius corners, hairline borders, and a focus
|
||||
ring drawn in BMW Focus Blue (#0653b6).
|
||||
</p>
|
||||
<span class="status" aria-live="polite">
|
||||
<span class="status-dot" aria-hidden="true"></span>
|
||||
Dealer network online
|
||||
</span>
|
||||
</div>
|
||||
<form class="form" onsubmit="event.preventDefault();">
|
||||
<div class="field">
|
||||
<label for="name">Full name</label>
|
||||
<input id="name" type="text" placeholder="Driver" autocomplete="name" required />
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="email">Email</label>
|
||||
<input id="email" type="email" placeholder="you@example.com" autocomplete="email" required />
|
||||
<p class="field-help">Your local dealer will reply within one business day.</p>
|
||||
</div>
|
||||
<div class="form-actions">
|
||||
<button type="submit" class="btn btn-primary">Reserve a slot</button>
|
||||
<button type="button" class="btn btn-outline">Browse the range</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</body>
|
||||
</html>
|
||||
179
design-systems/bmw/tokens.css
Normal file
179
design-systems/bmw/tokens.css
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
/* ─────────────────────────────────────────────────────────────────────────
|
||||
* design-systems/bmw/tokens.css
|
||||
*
|
||||
* Structured token bindings for "Design System Inspired by BMW".
|
||||
* German automotive precision: white-and-dark cinematic rhythm,
|
||||
* BMW Blue as singular interactive accent, zero border-radius,
|
||||
* BMWTypeNextLatin Light at 60px uppercase as the signature gesture.
|
||||
*
|
||||
* Key brand decisions encoded here:
|
||||
* - Pure White (#ffffff) canvas + cinematic dark hero sections
|
||||
* - BMW Blue (#1c69d4) — singular accent, used only for interactive elements
|
||||
* - BMW Focus Blue (#0653b6) — the documented darker tone, bound to
|
||||
* --accent-hover and reused inside --focus-ring
|
||||
* - Zero border-radius EVERYWHERE — including --radius-pill (DESIGN.md §5)
|
||||
* - BMWTypeNextLatin Light (300) for uppercase display — whispered authority
|
||||
* - Weight extremes only (300, 400, 700, 900) — never 500–600
|
||||
* - Tight line-heights throughout (body 1.15, display 1.30)
|
||||
* - No shadows — depth comes from dark/light section contrast
|
||||
*
|
||||
* Contract sources:
|
||||
* - Standard token names: design-systems/_schema/tokens.schema.ts
|
||||
* - A2 fallback values: design-systems/_schema/defaults.css
|
||||
* - Brand source of truth: design-systems/bmw/DESIGN.md
|
||||
* ─────────────────────────────────────────────────────────────────── */
|
||||
|
||||
:root {
|
||||
/* ─── Surface ─────────────────────────────────────────────────────
|
||||
* Pure white is the BMW canvas. Content sections are flat white; the
|
||||
* dramatic depth in BMW pages comes from alternating with full-bleed
|
||||
* dark photography bands, NOT from an internal surface ladder. We
|
||||
* bind --surface to a near-white (#f5f5f5) for cards/inputs that need
|
||||
* the faintest separation from page background without competing
|
||||
* with the showroom rhythm. --surface-warm aliases — BMW has no
|
||||
* warm tier; the identity is cool industrial. */
|
||||
--bg: #ffffff;
|
||||
--surface: #f5f5f5;
|
||||
--surface-warm: var(--surface);
|
||||
|
||||
/* ─── Foreground ──────────────────────────────────────────────────
|
||||
* Near Black (#262626) is slightly warmer than pure #000 — gives the
|
||||
* body reading texture BMW favors on white. Meta Gray (#757575) is
|
||||
* BMW's own documented `--site-context-metainfo-color`. Silver
|
||||
* (#bbbbbb) marks the most restrained tier (footer links, dividers).
|
||||
* --fg-2 aliases to --fg — BMW jumps from primary to meta with no
|
||||
* intermediate secondary-heading tier (the leap is part of the
|
||||
* stark voice). */
|
||||
--fg: #262626;
|
||||
--fg-2: var(--fg);
|
||||
--muted: #757575;
|
||||
--meta: #bbbbbb;
|
||||
|
||||
/* ─── Border ──────────────────────────────────────────────────────
|
||||
* Subtle gray hairlines on light surfaces. BMW pages have minimal
|
||||
* borders — depth and definition are carried by photography and
|
||||
* dark/light contrast, not edge-work. --border-soft aliases — no
|
||||
* inner-row separator distinction is needed. */
|
||||
--border: #e0e0e0;
|
||||
--border-soft: var(--border);
|
||||
|
||||
/* ─── Accent ──────────────────────────────────────────────────────
|
||||
* BMW Blue (#1c69d4) — the system's only persistent chromatic move
|
||||
* (DESIGN.md §2: `--site-context-highlight-color`). Used exclusively
|
||||
* for interactive elements (CTAs, links, focus signals). Never as a
|
||||
* background fill or decorative element.
|
||||
*
|
||||
* --accent-hover binds to BMW Focus Blue (#0653b6) — the documented
|
||||
* `--site-context-focus-color` — so primary buttons darken to the
|
||||
* brand-canonical "pressed" tone on hover rather than mixing toward
|
||||
* black. --accent-active darkens further with the schema formula. */
|
||||
--accent: #1c69d4;
|
||||
--accent-on: #ffffff;
|
||||
--accent-hover: #0653b6;
|
||||
--accent-active: color-mix(in oklab, var(--accent), black 18%);
|
||||
|
||||
/* ─── Semantic ────────────────────────────────────────────────────
|
||||
* BMW DESIGN.md documents no brand-specific semantic palette — the
|
||||
* page relies on automotive photography and BMW Blue for signal.
|
||||
* Schema defaults preserved; should occupy <5% of any surface. */
|
||||
--success: #16a34a;
|
||||
--warn: #eab308;
|
||||
--danger: #dc2626;
|
||||
|
||||
/* ─── Typography ──────────────────────────────────────────────────
|
||||
* BMWTypeNextLatin Light (weight 300) for display, BMWTypeNextLatin
|
||||
* for body. Fallback chain from DESIGN.md §3 — includes Japanese
|
||||
* fonts (Hiragino, Meiryo) reflecting BMW's global market presence.
|
||||
* Single typeface family at extreme weights (300 vs 900) is the
|
||||
* signature typographic tension. */
|
||||
--font-display: "BMWTypeNextLatin Light", Helvetica, Arial, "Hiragino Kaku Gothic ProN", "Hiragino Sans", Meiryo, sans-serif;
|
||||
--font-body: BMWTypeNextLatin, Helvetica, Arial, "Hiragino Kaku Gothic ProN", "Hiragino Sans", Meiryo, sans-serif;
|
||||
--font-mono: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Monaco, Consolas, monospace;
|
||||
|
||||
/* Type scale — DESIGN.md §3. 60px display is the defining BMW
|
||||
* gesture (uppercase, weight 300). 18px navigation tier sits
|
||||
* between body baseline (16) and section heading (32) — it's the
|
||||
* weight 900 nav-emphasis size that creates the 300↔900 contrast. */
|
||||
--text-xs: 12px;
|
||||
--text-sm: 14px;
|
||||
--text-base: 16px;
|
||||
--text-lg: 18px;
|
||||
--text-xl: 24px;
|
||||
--text-2xl: 32px;
|
||||
--text-3xl: 44px;
|
||||
--text-4xl: 60px;
|
||||
|
||||
/* Leading inverts the usual web convention here: body is tighter
|
||||
* (1.15) than display (1.30). DESIGN.md §3 — "Tight everything,
|
||||
* German engineering"; no line in the system breathes loose. */
|
||||
--leading-body: 1.15;
|
||||
--leading-tight: 1.30;
|
||||
--tracking-display: 0;
|
||||
|
||||
/* ─── Spacing ─────────────────────────────────────────────────────
|
||||
* 8px base unit. DESIGN.md §5 documents the full BMW scale (1, 5, 8,
|
||||
* 10, 12, 15, 16, 20, 24, 30, 32, 40, 45, 56, 60); mapped to the
|
||||
* schema's 4-8-12-16-20-24-32-48 tier where each tier aligns. */
|
||||
--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 rhythm ──────────────────────────────────────────────
|
||||
* Showroom pacing — generous vertical envelope so each content band
|
||||
* feels spotlit between dark hero sections. Sized above the schema
|
||||
* default of 80px to honor BMW's premium breathing room. */
|
||||
--section-y-desktop: 96px;
|
||||
--section-y-tablet: 64px;
|
||||
--section-y-phone: 40px;
|
||||
|
||||
/* ─── Radius ──────────────────────────────────────────────────────
|
||||
* ZERO border-radius — non-negotiable BMW identity. DESIGN.md §5:
|
||||
* "BMW uses sharp corners exclusively — every element is a precise
|
||||
* rectangle. This is the most angular design system analyzed."
|
||||
* --radius-pill is also bound to 0 to honor the rule literally;
|
||||
* components needing circular dots use `border-radius: 50%` inline
|
||||
* at the call site rather than borrow the (square) pill token. */
|
||||
--radius-sm: 0;
|
||||
--radius-md: 0;
|
||||
--radius-lg: 0;
|
||||
--radius-pill: 0;
|
||||
|
||||
/* ─── Elevation ───────────────────────────────────────────────────
|
||||
* No shadows. DESIGN.md §6 — "BMW uses virtually no shadows. Depth
|
||||
* is created entirely through the contrast between dark photographic
|
||||
* sections and white content sections." --elev-raised collapses to
|
||||
* `none`; --elev-ring stays available for input outlines and dense
|
||||
* configurator surfaces where hairline containment is still useful. */
|
||||
--elev-flat: none;
|
||||
--elev-ring: 0 0 0 1px var(--border);
|
||||
--elev-raised: none;
|
||||
|
||||
/* ─── Focus ring ──────────────────────────────────────────────────
|
||||
* Sharp 2px solid ring at BMW Focus Blue (--accent-hover = #0653b6).
|
||||
* No transparency, no glow — engineered precision instead of the
|
||||
* schema's soft accent-tinted halo. */
|
||||
--focus-ring: 0 0 0 2px var(--accent-hover);
|
||||
|
||||
/* ─── Motion ──────────────────────────────────────────────────────
|
||||
* Brisk, deliberate transitions. BMW's motion expresses precision,
|
||||
* not personality; schema defaults match the voice unchanged. */
|
||||
--motion-fast: 150ms;
|
||||
--motion-base: 200ms;
|
||||
--ease-standard: cubic-bezier(0.2, 0, 0, 1);
|
||||
|
||||
/* ─── Layout ──────────────────────────────────────────────────────
|
||||
* 1440px content max — fits BMW's wide-format layouts and the
|
||||
* "Large Desktop 1280–1440" breakpoint from DESIGN.md §8. Full-bleed
|
||||
* automotive photography extends beyond this; the container
|
||||
* constrains text columns only. Gutters step generously
|
||||
* (32 → 24 → 16) to keep the showroom margin even at mobile. */
|
||||
--container-max: 1440px;
|
||||
--container-gutter-desktop: 32px;
|
||||
--container-gutter-tablet: 24px;
|
||||
--container-gutter-phone: 16px;
|
||||
}
|
||||
512
design-systems/nike/components.html
Normal file
512
design-systems/nike/components.html
Normal file
|
|
@ -0,0 +1,512 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Nike — reference components</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="Reference fixture for design-systems/nike. Monochromatic UI
|
||||
(black / white / grey only), massive uppercase Nike Futura ND display
|
||||
type at 96px / 0.90 line-height, pill-shaped buttons (30px radius),
|
||||
zero card shadows. Just keep going."
|
||||
/>
|
||||
|
||||
<style>
|
||||
:root {
|
||||
--bg: #ffffff;
|
||||
--surface: #f5f5f5;
|
||||
--surface-warm: #fafafa;
|
||||
|
||||
--fg: #111111;
|
||||
--fg-2: var(--fg);
|
||||
--muted: #707072;
|
||||
--meta: #9e9ea0;
|
||||
|
||||
--border: #cacacb;
|
||||
--border-soft: #e5e5e5;
|
||||
|
||||
--accent: #111111;
|
||||
--accent-on: #ffffff;
|
||||
--accent-hover: #707072;
|
||||
--accent-active: #000000;
|
||||
|
||||
--success: #007d48;
|
||||
--warn: #fca600;
|
||||
--danger: #d30005;
|
||||
|
||||
--font-display:
|
||||
"Nike Futura ND", "Helvetica Now Display Medium", "Helvetica Now Display", "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
--font-body:
|
||||
"Helvetica Now Text Medium", "Helvetica Now Text", "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
--font-mono:
|
||||
ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Monaco, Consolas, monospace;
|
||||
|
||||
--text-xs: 12px;
|
||||
--text-sm: 14px;
|
||||
--text-base: 16px;
|
||||
--text-lg: 20px;
|
||||
--text-xl: 24px;
|
||||
--text-2xl: 32px;
|
||||
--text-3xl: 48px;
|
||||
--text-4xl: 96px;
|
||||
|
||||
--leading-body: 1.75;
|
||||
--leading-tight: 0.9;
|
||||
|
||||
--tracking-display: -0.02em;
|
||||
|
||||
--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: 80px;
|
||||
--section-y-tablet: 48px;
|
||||
--section-y-phone: 32px;
|
||||
|
||||
--radius-sm: 8px;
|
||||
--radius-md: 20px;
|
||||
--radius-lg: 24px;
|
||||
--radius-pill: 30px;
|
||||
|
||||
--elev-flat: none;
|
||||
--elev-ring: 0 0 0 1px var(--border);
|
||||
--elev-raised: 0 0 0 1px var(--border);
|
||||
|
||||
--focus-ring: 0 0 0 2px rgba(39, 93, 197, 1);
|
||||
|
||||
--motion-fast: 150ms;
|
||||
--motion-base: 200ms;
|
||||
--ease-standard: cubic-bezier(0.2, 0, 0, 1);
|
||||
|
||||
--container-max: 1440px;
|
||||
--container-gutter-desktop: 48px;
|
||||
--container-gutter-tablet: 24px;
|
||||
--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);
|
||||
font-weight: 500; /* Nike's body weight — Helvetica Now Text Medium */
|
||||
line-height: var(--leading-body);
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
/* ─── Promo bar — Nike's signature dark band atop every page ─
|
||||
§4: "Top banner: promotional message bar with dark background
|
||||
(#111111) and white text", 8–12px vertical padding, 12px/500
|
||||
centered text. */
|
||||
.promo-bar {
|
||||
background: var(--fg);
|
||||
color: var(--bg);
|
||||
font-size: var(--text-xs);
|
||||
font-weight: 500;
|
||||
text-align: center;
|
||||
padding: var(--space-2) var(--space-4);
|
||||
letter-spacing: 0.02em;
|
||||
}
|
||||
.promo-bar a { color: var(--bg); text-decoration: underline; text-underline-offset: 2px; }
|
||||
|
||||
/* ─── 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-soft); }
|
||||
@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 ─────────────────────────────────────────────
|
||||
Nike's display layer is uppercase Nike Futura ND at 96px with
|
||||
0.90 line-height — the "stadium scoreboard" §3 calls for.
|
||||
Below the display layer, Helvetica Now (body) handles
|
||||
everything at weight 500 (the brand's documented dominant
|
||||
weight for interactive copy). */
|
||||
h1, h2, h3 {
|
||||
margin: 0;
|
||||
color: var(--fg);
|
||||
}
|
||||
/* h1 — Hero Display, uppercase Futura, ascenders nearly touch
|
||||
descenders. Nike Futura ND below 24px is forbidden (§7), so
|
||||
h1 is the ONLY place this size and treatment appear. */
|
||||
h1 {
|
||||
font-family: var(--font-display);
|
||||
font-size: var(--text-4xl);
|
||||
font-weight: 500;
|
||||
line-height: var(--leading-tight);
|
||||
letter-spacing: var(--tracking-display);
|
||||
text-transform: uppercase;
|
||||
}
|
||||
/* h2 — section heading, Helvetica Now Display Medium tier
|
||||
(the second slot in --font-display falls onto it when
|
||||
Futura ND is absent). Title-case, NOT uppercase. */
|
||||
h2 {
|
||||
font-family: var(--font-display);
|
||||
font-size: var(--text-2xl);
|
||||
font-weight: 500;
|
||||
line-height: 1.2;
|
||||
}
|
||||
/* h3 — card title at the body baseline, weight 500. */
|
||||
h3 {
|
||||
font-family: var(--font-body);
|
||||
font-size: var(--text-base);
|
||||
font-weight: 500;
|
||||
line-height: 1.5;
|
||||
}
|
||||
p { margin: 0; }
|
||||
|
||||
.lead {
|
||||
font-size: var(--text-lg);
|
||||
line-height: 1.5;
|
||||
color: var(--muted);
|
||||
font-weight: 500;
|
||||
}
|
||||
.body-muted { color: var(--muted); }
|
||||
.body-meta { color: var(--meta); font-size: var(--text-sm); }
|
||||
.body-sm { font-size: var(--text-sm); }
|
||||
|
||||
/* `.eyebrow` — small uppercase label above section heads. The
|
||||
only place Nike pushes positive tracking (uppercase needs the
|
||||
breathing room); display copy below uses negative tracking. */
|
||||
.eyebrow {
|
||||
font-family: var(--font-body);
|
||||
font-size: var(--text-xs);
|
||||
font-weight: 500;
|
||||
line-height: 1;
|
||||
color: var(--muted);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.12em;
|
||||
}
|
||||
|
||||
.stack-3 > * + * { margin-block-start: var(--space-3); }
|
||||
.stack-4 > * + * { margin-block-start: var(--space-4); }
|
||||
.stack-6 > * + * { margin-block-start: var(--space-6); }
|
||||
.stack-8 > * + * { margin-block-start: var(--space-8); }
|
||||
|
||||
/* ─── Buttons — pill geometry, monochrome only ───────────────
|
||||
Nike's pill (30px radius) is the SOLE button silhouette across
|
||||
the whole brand (§4). Primary uses --accent (Nike Black) with
|
||||
the hover shifting to grey-500 — the documented behaviour, not
|
||||
a darken. Secondary is the 1.5px outlined variant from §4. */
|
||||
.btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--space-2);
|
||||
padding: 12px 24px;
|
||||
border: none;
|
||||
border-radius: var(--radius-pill);
|
||||
font-family: var(--font-body);
|
||||
font-size: var(--text-base);
|
||||
font-weight: 500; /* §7 — never weight 400 on buttons */
|
||||
line-height: 1.2;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
transition:
|
||||
background-color var(--motion-base) var(--ease-standard),
|
||||
color var(--motion-base) var(--ease-standard),
|
||||
border-color var(--motion-base) var(--ease-standard),
|
||||
box-shadow var(--motion-fast) var(--ease-standard);
|
||||
}
|
||||
.btn:focus-visible { outline: none; box-shadow: var(--focus-ring); }
|
||||
.btn:active { opacity: 0.85; }
|
||||
|
||||
.btn-primary {
|
||||
background: var(--accent);
|
||||
color: var(--accent-on);
|
||||
}
|
||||
.btn-primary:hover { background: var(--accent-hover); }
|
||||
.btn-primary:active { background: var(--accent-active); }
|
||||
|
||||
/* §4 — secondary outlined: 1.5px solid grey-300, 30px pill. */
|
||||
.btn-secondary {
|
||||
background: transparent;
|
||||
color: var(--fg);
|
||||
border: 1.5px solid var(--border);
|
||||
padding: 10.5px 22.5px; /* compensates for the 1.5px border */
|
||||
}
|
||||
.btn-secondary:hover {
|
||||
border-color: var(--muted);
|
||||
background: var(--surface);
|
||||
}
|
||||
|
||||
/* ─── Inputs — fill-led, soft hairline ───────────────────────
|
||||
§4 — non-search inputs use grey-100 fill, 8px radius
|
||||
(--radius-sm), border-active darkens to Nike Black on focus,
|
||||
and the focus ring is the documented blue 2px halo. */
|
||||
.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: 14px 16px;
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid var(--border);
|
||||
background: var(--surface);
|
||||
color: var(--fg);
|
||||
font-family: var(--font-body);
|
||||
font-size: var(--text-base);
|
||||
font-weight: 500;
|
||||
line-height: 1.2;
|
||||
outline: none;
|
||||
transition:
|
||||
border-color var(--motion-base) var(--ease-standard),
|
||||
background-color var(--motion-base) var(--ease-standard),
|
||||
box-shadow var(--motion-fast) var(--ease-standard);
|
||||
}
|
||||
.field input::placeholder { color: var(--muted); font-weight: 500; }
|
||||
.field input:hover { border-color: var(--muted); }
|
||||
.field input:focus-visible {
|
||||
border-color: var(--fg); /* Border Active = Nike Black, §2 */
|
||||
background: var(--bg);
|
||||
box-shadow: var(--focus-ring);
|
||||
}
|
||||
.field-help { font-size: var(--text-xs); color: var(--muted); }
|
||||
|
||||
/* ─── Cards — flat, surface-defined, NO shadow ───────────────
|
||||
§6 — "Nike's elevation philosophy is radically flat. There
|
||||
are no card shadows, no hover lifts, no floating elements."
|
||||
Cards differentiate from --bg by sitting on --surface, with
|
||||
a 20px (--radius-md) corner for "interactive containers"
|
||||
per §5. */
|
||||
.card {
|
||||
background: var(--surface);
|
||||
border-radius: var(--radius-md);
|
||||
padding: var(--space-6);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-3);
|
||||
/* No box-shadow, no border, no hover lift — by Nike contract. */
|
||||
}
|
||||
|
||||
/* ─── Badges — pill-shaped status indicators ─────────────── */
|
||||
.badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--space-2);
|
||||
padding: 4px var(--space-3);
|
||||
border-radius: var(--radius-pill);
|
||||
font-family: var(--font-body);
|
||||
font-size: var(--text-xs);
|
||||
font-weight: 500;
|
||||
line-height: 1.4;
|
||||
}
|
||||
.badge-success {
|
||||
color: var(--success);
|
||||
background: color-mix(in oklab, var(--success), transparent 88%);
|
||||
}
|
||||
.badge-muted {
|
||||
color: var(--muted);
|
||||
background: var(--surface);
|
||||
}
|
||||
.badge-dot {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
background: currentColor;
|
||||
}
|
||||
|
||||
/* ─── Links — black text, hover underline, kbd ─────────────
|
||||
§4 nav: "text color shifts to Grey-500 (#707072)" on hover.
|
||||
No blue inline links here — Nike keeps reading copy
|
||||
monochrome and reserves --focus-ring blue for keyboard focus
|
||||
alone. */
|
||||
a { color: var(--fg); text-decoration: none; transition: color var(--motion-base) var(--ease-standard); }
|
||||
a:hover { color: var(--muted); 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);
|
||||
}
|
||||
|
||||
/* ─── Layout helpers — hero, features, form ───────────────── */
|
||||
.hero-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1.4fr 1fr;
|
||||
gap: var(--space-12);
|
||||
align-items: end;
|
||||
}
|
||||
@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-5);
|
||||
background: var(--surface);
|
||||
border-radius: var(--radius-md);
|
||||
/* No border, no shadow — surface contrast does the work. */
|
||||
}
|
||||
|
||||
.features-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: var(--space-2); /* Nike's "tight 4-12px" product-grid gap */
|
||||
}
|
||||
@media (max-width: 1023px) { .features-grid { grid-template-columns: 1fr 1fr; } }
|
||||
@media (max-width: 639px) { .features-grid { grid-template-columns: 1fr; } }
|
||||
|
||||
.form-row {
|
||||
display: grid;
|
||||
grid-template-columns: 1.4fr 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); flex-wrap: wrap; }
|
||||
|
||||
.icon { width: 16px; height: 16px; flex-shrink: 0; }
|
||||
.row-between { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); }
|
||||
|
||||
/* `.swoosh` — a stylized swoosh divider rule that anchors the
|
||||
hero. Pure CSS, no real Nike marks, just a visual nod to the
|
||||
brand's geometric language. */
|
||||
.swoosh {
|
||||
width: 64px;
|
||||
height: 4px;
|
||||
background: var(--fg);
|
||||
border-radius: var(--radius-pill);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="promo-bar" role="region" aria-label="Promotional banner">
|
||||
Free shipping for members. <a href="#join">Join us.</a>
|
||||
</div>
|
||||
|
||||
<main class="container">
|
||||
<section data-od-id="hero">
|
||||
<div class="hero-grid">
|
||||
<div class="stack-6">
|
||||
<div class="stack-3">
|
||||
<span class="swoosh" aria-hidden="true"></span>
|
||||
<p class="eyebrow">Reference fixture · nike</p>
|
||||
</div>
|
||||
<h1>Just keep going.</h1>
|
||||
<p class="lead" style="max-width: 44ch">
|
||||
Built for the ones who don't quit. From dawn miles to sold-out
|
||||
stadiums, every stitch earns its place — and every pixel of
|
||||
this page is here to sell sport, nothing else.
|
||||
</p>
|
||||
<div class="hero-actions">
|
||||
<a href="./tokens.css" class="btn btn-primary">
|
||||
Shop new arrivals
|
||||
<svg class="icon" viewBox="0 0 24 24" fill="none"
|
||||
stroke="currentColor" stroke-width="1.75"
|
||||
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">Read the spec</a>
|
||||
</div>
|
||||
</div>
|
||||
<aside class="hero-meta" aria-label="Member benefits">
|
||||
<div class="row-between">
|
||||
<span class="body-sm" style="font-weight: 500">Member status</span>
|
||||
<span class="badge badge-success">
|
||||
<span class="badge-dot" aria-hidden="true"></span>
|
||||
Active
|
||||
</span>
|
||||
</div>
|
||||
<p class="body-meta">Last reviewed <time datetime="2026-05-15">2026-05-15</time> · v1.0</p>
|
||||
<p class="body-meta">Press <kbd>⌘</kbd> <kbd>K</kbd> to search the spec.</p>
|
||||
</aside>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section data-od-id="features">
|
||||
<div class="stack-3">
|
||||
<p class="eyebrow">What this fixture exercises</p>
|
||||
<h2 style="max-width: 22ch">Restraint as an athletic discipline.</h2>
|
||||
</div>
|
||||
<div class="features-grid" style="margin-block-start: var(--space-8)">
|
||||
<article class="card">
|
||||
<h3>Monochrome on purpose</h3>
|
||||
<p class="body-muted body-sm">
|
||||
--bg (#ffffff) → --surface (#f5f5f5) → --fg (#111111).
|
||||
The UI carries no color so product photography can. Red, green,
|
||||
blue are reserved for semantic moments only.
|
||||
</p>
|
||||
<a href="./tokens.css" class="body-sm" style="font-weight: 500">Inspect tokens →</a>
|
||||
</article>
|
||||
<article class="card">
|
||||
<h3>Display type that punches</h3>
|
||||
<p class="body-muted body-sm">
|
||||
Nike Futura ND at 96px / 0.90 line-height, uppercase, tightened
|
||||
tracking. A typographic shockwave reserved exclusively for the
|
||||
hero — never below 24px.
|
||||
</p>
|
||||
<a href="./DESIGN.md" class="body-sm" style="font-weight: 500">Read the rule →</a>
|
||||
</article>
|
||||
<article class="card">
|
||||
<h3>Pills, no shadows</h3>
|
||||
<p class="body-muted body-sm">
|
||||
--radius-pill is bound to 30px (the literal Nike value, not
|
||||
9999). --elev-raised collapses onto --elev-ring because the
|
||||
brand refuses card shadows outright.
|
||||
</p>
|
||||
<a href="./tokens.css" class="body-sm" style="font-weight: 500">Inspect radius →</a>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section data-od-id="form" id="join">
|
||||
<div class="form-row">
|
||||
<div class="stack-4">
|
||||
<p class="eyebrow">Become a member</p>
|
||||
<h2>Free shipping. First access. No nonsense.</h2>
|
||||
<p class="body-muted" style="max-width: 48ch">
|
||||
Members hear about drops first, get free shipping on every order,
|
||||
and unlock the Nike Run Club / Training Club apps. One account,
|
||||
every sport.
|
||||
</p>
|
||||
</div>
|
||||
<form class="form" onsubmit="event.preventDefault();">
|
||||
<div class="field">
|
||||
<label for="email">Email address</label>
|
||||
<input id="email" type="email" placeholder="you@nike.com" autocomplete="email" required />
|
||||
<p class="field-help">We'll send membership perks. No marketing noise.</p>
|
||||
</div>
|
||||
<div class="form-actions">
|
||||
<button type="submit" class="btn btn-primary">Sign me up</button>
|
||||
<button type="button" class="btn btn-secondary">Learn more</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
304
design-systems/nike/tokens.css
Normal file
304
design-systems/nike/tokens.css
Normal file
|
|
@ -0,0 +1,304 @@
|
|||
/* ─────────────────────────────────────────────────────────────────────────
|
||||
* design-systems/nike/tokens.css
|
||||
*
|
||||
* Structured token bindings for "Design System Inspired by Nike" —
|
||||
* the kinetic retail cathedral. A monochromatic UI (black/white/grey
|
||||
* only) so that athletic photography and product color carry the
|
||||
* emotional weight, with massive uppercase Nike Futura ND display
|
||||
* typography that punches through hero imagery like a typographic
|
||||
* shockwave. This file is the *machine-readable* form of the values
|
||||
* described in `DESIGN.md`. Agents paste the `:root { … }` block
|
||||
* verbatim into the first `<style>` of every artifact, then reference
|
||||
* everything via var(--name) from then on.
|
||||
*
|
||||
* Brand-specific schema decisions (non-obvious bindings worth flagging
|
||||
* for reviewers and downstream brand authors):
|
||||
* 1. --bg is pure white (#FFFFFF) and --fg is Nike Black (#111111),
|
||||
* not pure black. The fractional warmth at #111111 is a
|
||||
* brand-determining choice ("Don't use #000000 for text" — §7
|
||||
* do's-and-don'ts) so we honor it strictly even though the gap
|
||||
* from #000 is small.
|
||||
* 2. --surface and --surface-warm bind to two real tiers of Nike's
|
||||
* grey ladder: --surface → #F5F5F5 (Light Gray, Podium grey-100,
|
||||
* the search-fill / placeholder / skeleton tier) and
|
||||
* --surface-warm → #FAFAFA (Snow, grey-50, the lightest near-
|
||||
* white differentiation tier). Collapsing surface-warm to an
|
||||
* alias would erase a real brand feature — Nike walks the full
|
||||
* 50/100/200 surface ramp.
|
||||
* 3. --accent is bound to Nike Black (#111111), the same value as
|
||||
* --fg. Nike's UI accent IS black: §9 ("Quick Color Reference")
|
||||
* lists "Primary CTA: Nike Black (#111111)" and §7 forbids
|
||||
* brand colors beyond the grey scale for UI elements ("product
|
||||
* photography is the color"). The schema's ≤2 visible accent
|
||||
* uses per screen aligns naturally because Nike already self-
|
||||
* polices its UI to a single chromatic move.
|
||||
* 4. --accent-hover is bound to #707072 (Grey-500 / Secondary Text),
|
||||
* not a black-mix darkening. Nike's documented primary-button
|
||||
* hover SHIFTS to grey rather than darkening (§4 — "Hover:
|
||||
* background shifts to Grey-500"); the schema's default formula
|
||||
* would fight that motion. --accent-active darkens to true
|
||||
* black (#000000) — the only place pure black appears in the
|
||||
* system, reserved for the press-feedback moment.
|
||||
* 5. --fg-2 aliases to --fg, while --muted (#707072) and --meta
|
||||
* (#9E9EA0) bind to independent values. Nike's text hierarchy
|
||||
* is essentially binary at the primary tier (no intermediate
|
||||
* between Nike Black and Secondary Text), but it does keep a
|
||||
* separate tertiary / disabled tier for metadata.
|
||||
* 6. --border-soft binds to #E5E5E5 (Hover Gray / grey-200) — Nike's
|
||||
* §6 documents the only sanctioned divider as
|
||||
* `0px -1px 0px 0px #E5E5E5 inset`. The hairline #CACACB
|
||||
* (--border) reads as a card edge; #E5E5E5 reads as a row-
|
||||
* separator whisper.
|
||||
* 7. --radius-pill is bound to 30px, NOT 9999px. Nike's signature
|
||||
* pill button uses 30px literally (§4 — "fully rounded pill
|
||||
* (30px)") and the number is itself a brand-recognisable
|
||||
* detail. Apple makes the same kind of choice with 980px; we
|
||||
* follow that precedent rather than collapsing to the schema
|
||||
* default.
|
||||
* 8. --elev-raised is ring-only (`0 0 0 1px var(--border)`),
|
||||
* identical to --elev-ring. §6 declares Nike's elevation model
|
||||
* "radically flat — no card shadows, no hover lifts, no
|
||||
* floating elements." Binding raised to a blur shadow would
|
||||
* contradict the brand; we bind it to the ring so artifacts
|
||||
* that reference --elev-raised still resolve, but produce the
|
||||
* flat / hairline look Nike intends.
|
||||
* 9. --focus-ring is the documented 2px solid blue ring
|
||||
* `0 0 0 2px rgba(39, 93, 197, 1)` from §4 / §6, NOT the
|
||||
* schema's transparent-mixed accent halo. Nike's focus indicator
|
||||
* is a deliberate chromatic break — the only blue moment in the
|
||||
* whole UI — and we keep it verbatim.
|
||||
* 10. --tracking-display is -0.02em. The DESIGN.md hierarchy table
|
||||
* leaves display letter-spacing as "—", but the user-facing
|
||||
* brief calls for "tight tracking" on the BIG uppercase
|
||||
* condensed Futura display; -0.02em (≈ -1.92px on 96px)
|
||||
* compresses the Futura ND silhouette into the monolithic
|
||||
* stadium-scoreboard block §3 describes without crushing
|
||||
* smaller display tiers.
|
||||
* 11. --leading-tight is 0.90, the most aggressive tight-leading
|
||||
* binding in any brand on the repo. §3 calls it "impossibly
|
||||
* tight" and the typographic identity depends on it; ascenders
|
||||
* and descenders nearly touch on the 96px display.
|
||||
*
|
||||
* Contract sources:
|
||||
* - Standard token names: design-systems/_schema/tokens.schema.ts
|
||||
* (TOKEN_SCHEMA — every name below appears there or as an
|
||||
* explicit B-slot.)
|
||||
* - A2 fallback values: design-systems/_schema/defaults.css
|
||||
* (We override --accent-on, --accent-hover, --accent-active,
|
||||
* --success, --warn, --danger, --radius-md, --radius-lg,
|
||||
* --radius-pill, --elev-raised, --focus-ring; the rest match
|
||||
* defaults.)
|
||||
*
|
||||
* Keep this file additive: never invent token names not also documented
|
||||
* in DESIGN.md or the shared schema.
|
||||
* ─────────────────────────────────────────────────────────────────── */
|
||||
|
||||
:root {
|
||||
/* ─── Surface (3 levels) ──────────────────────────────────────────
|
||||
* Nike's surface ladder runs 50 → 100 → 200 across grey-50/100/200.
|
||||
* --bg is the white retail canvas (the most common default page
|
||||
* background); --surface is the Light Gray feature/skeleton tier;
|
||||
* --surface-warm is the Snow near-white differentiation tier that
|
||||
* appears between hero bands and merchandise grids on nike.com.
|
||||
* All three bind to real values — collapsing surface-warm would
|
||||
* erase a real brand feature. */
|
||||
--bg: #ffffff; /* Nike White — primary page canvas */
|
||||
--surface: #f5f5f5; /* Light Gray (Podium grey-100) — fill / skeleton */
|
||||
--surface-warm: #fafafa; /* Snow (Podium grey-50) — lightest tier */
|
||||
|
||||
/* ─── Foreground ramp (4 levels) ─────────────────────────────────
|
||||
* Nike's text neutrals are explicitly named in DESIGN.md §2:
|
||||
* Nike Black (primary text — #111111, deliberately not #000000),
|
||||
* Secondary Text (#707072, descriptive copy / metadata / price
|
||||
* labels), and Disabled Text (#9E9EA0, inactive elements). The
|
||||
* brand's hierarchy is binary at the primary tier (no #2 stop
|
||||
* between #111111 and #707072), so --fg-2 collapses to var(--fg);
|
||||
* --muted and --meta bind to independent values. */
|
||||
--fg: #111111; /* Nike Black — primary text, headings, nav links */
|
||||
--fg-2: var(--fg); /* alias — no intermediate between primary and muted */
|
||||
--muted: #707072; /* Secondary Text (grey-500) — descriptive copy */
|
||||
--meta: #9e9ea0; /* Disabled Text (grey-400) — tertiary / metadata */
|
||||
|
||||
/* ─── Border (2 levels) ──────────────────────────────────────────
|
||||
* Nike's border palette is restrained: --border (#CACACB, grey-300)
|
||||
* is the standard input / divider weight; --border-soft (#E5E5E5,
|
||||
* grey-200) is the documented inset-divider color that whispers
|
||||
* between rows in dense product grids without competing with the
|
||||
* #CACACB hairline. */
|
||||
--border: #cacacb; /* Border Secondary (grey-300) — input borders, dividers */
|
||||
--border-soft: #e5e5e5; /* Hover Gray (grey-200) — inset row separator */
|
||||
|
||||
/* ─── Accent ──────────────────────────────────────────────────────
|
||||
* Nike's UI accent IS black. §9 ("Quick Color Reference") lists
|
||||
* Primary CTA as Nike Black (#111111); §7 ("Don't introduce brand
|
||||
* colors beyond the grey scale for UI elements") forbids using a
|
||||
* chromatic accent. Product photography carries all the color, so
|
||||
* the schema's "≤2 visible uses per screen" cap aligns naturally
|
||||
* with Nike's already-monochrome UI policy. */
|
||||
--accent: #111111; /* Nike Black — primary action color */
|
||||
--accent-on: #ffffff; /* white label on black fill */
|
||||
|
||||
/* ─── Accent states ───────────────────────────────────────────────
|
||||
* Nike's documented primary-button hover shifts to Grey-500 (§4 —
|
||||
* "Hover: background shifts to Grey-500 (#707072)") rather than
|
||||
* darkening, so we override the schema's black-mix formula with
|
||||
* the literal grey-500 value. --accent-active darkens to pure
|
||||
* black — the ONLY place #000000 appears in the entire system,
|
||||
* reserved for the press-feedback moment when Nike Black
|
||||
* compresses one stop further. */
|
||||
--accent-hover: #707072; /* Grey-500 — documented hover, not a darken */
|
||||
--accent-active: #000000; /* True black — the only #000000 in the system */
|
||||
|
||||
/* ─── Semantic ────────────────────────────────────────────────────
|
||||
* Nike reserves color exclusively for semantic meaning (red=error,
|
||||
* green=success, yellow=warning) — the only chromatic moves in the
|
||||
* whole UI besides product photography. Values come straight from
|
||||
* §2's "Semantic & Accent" block. */
|
||||
--success: #007d48; /* Success Green — confirmation, availability */
|
||||
--warn: #fca600; /* Yellow-500 — warning state */
|
||||
--danger: #d30005; /* Nike Red — critical errors, sale badges */
|
||||
|
||||
/* ─── Typography ──────────────────────────────────────────────────
|
||||
* Nike's documented split is THREE families that we collapse onto
|
||||
* the schema's two slots via fallback chains:
|
||||
* - Nike Futura ND: custom condensed Futura, 96px hero display
|
||||
* ONLY (§7 forbids it below 24px). Falls back to Helvetica Now
|
||||
* Display Medium for headings.
|
||||
* - Helvetica Now Display Medium: 24–32px section heads.
|
||||
* - Helvetica Now Text: navigation, body, controls.
|
||||
*
|
||||
* --font-display chains Nike Futura ND → Helvetica Now Display
|
||||
* Medium → Helvetica Neue, so hero h1 lands on Futura when
|
||||
* available and gracefully degrades to Helvetica Now Display
|
||||
* (Nike's documented heading face) on every other surface.
|
||||
* --font-body holds the workhorse Helvetica Now Text stack. */
|
||||
--font-display:
|
||||
"Nike Futura ND", "Helvetica Now Display Medium", "Helvetica Now Display", "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
--font-body:
|
||||
"Helvetica Now Text Medium", "Helvetica Now Text", "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
--font-mono:
|
||||
ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Monaco, Consolas, monospace;
|
||||
|
||||
/* Type scale (px) — derived from DESIGN.md §3 hierarchy table.
|
||||
* Nike's documented sizes are 12 / 14 / 16 / 24 / 32 / 96; the
|
||||
* --text-lg and --text-3xl slots interpolate cleanly into the
|
||||
* existing system (20px = emphasized body / promo links;
|
||||
* 48px = mid-display, between H1 and Hero). The 96px display is
|
||||
* the brand ceiling — the typographic shockwave §3 describes. */
|
||||
--text-xs: 12px; /* Small / Tiny — timestamps, legal text */
|
||||
--text-sm: 14px; /* Caption / Link Small / Button Small */
|
||||
--text-base: 16px; /* Body / Link / Button / H3 — Nike's reading baseline */
|
||||
--text-lg: 20px; /* Promoted body — interpolation tier */
|
||||
--text-xl: 24px; /* Heading 2 — subsection titles */
|
||||
--text-2xl: 32px; /* Heading 1 — section heads */
|
||||
--text-3xl: 48px; /* Mid-display — interpolation tier */
|
||||
--text-4xl: 96px; /* Display Hero — typographic shockwave */
|
||||
|
||||
/* Nike's leading envelope is unusually airy in the body (1.75 —
|
||||
* generous for product browsing) and uniquely tight at the top
|
||||
* (0.90 — the "impossibly tight" display ceiling §3 calls a
|
||||
* stadium scoreboard). Both numbers come straight from §3. */
|
||||
--leading-body: 1.75; /* Body — comfortable product-browsing rhythm */
|
||||
--leading-tight: 0.9; /* Hero Display — ascenders nearly touch descenders */
|
||||
|
||||
/* Display tracking compresses to -0.02em — condensed Futura ND
|
||||
* wants slight letter-spacing tightening to read as the monolithic
|
||||
* uppercase block §3 describes (≈ -1.92px on the 96px hero).
|
||||
* Body type stays at 0 / component-controlled. */
|
||||
--tracking-display: -0.02em;
|
||||
|
||||
/* ─── Spacing ─────────────────────────────────────────────────────
|
||||
* Nike's grid is 4px base / 8px primary multiples (§5). The shared
|
||||
* schema's 4-8-12-16-20-24-32-48 scale is verbatim Nike — no
|
||||
* overrides needed. Component-internal values (the 4–12px product-
|
||||
* grid gaps that create Nike's "dense superstore" feel) stay
|
||||
* inline at the call site. */
|
||||
--space-1: 4px; /* tight icon gaps, inline spacing */
|
||||
--space-2: 8px; /* base unit, button icon gaps */
|
||||
--space-3: 12px; /* card internal padding, tight margins */
|
||||
--space-4: 16px; /* standard padding, nav spacing */
|
||||
--space-5: 20px; /* product card gaps */
|
||||
--space-6: 24px; /* section internal padding, grid gaps */
|
||||
--space-8: 32px; /* section breaks */
|
||||
--space-12: 48px; /* major section padding */
|
||||
|
||||
/* ─── Section rhythm ──────────────────────────────────────────────
|
||||
* Nike's section padding scales 80 → 48 → 32 across desktop /
|
||||
* tablet / phone (§5 / §8 — "Section padding: 80px → 48px → 32px
|
||||
* → 24px as viewport narrows"). The 80px desktop top is the hero-
|
||||
* section padding from the §5 spacing scale (--space-10 in Nike's
|
||||
* native vocabulary). */
|
||||
--section-y-desktop: 80px;
|
||||
--section-y-tablet: 48px;
|
||||
--section-y-phone: 32px;
|
||||
|
||||
/* ─── Radius ──────────────────────────────────────────────────────
|
||||
* Nike uses purposeful radius tiers (§5): 0px on product imagery,
|
||||
* 8px on form inputs (non-search), 20px on UI containers, 24px on
|
||||
* search inputs, 30px on buttons (full pill), 50% on circular
|
||||
* icons. Mapped onto the schema:
|
||||
* --radius-sm → 8px compact controls and form fields
|
||||
* --radius-md → 20px interactive containers (Nike's card)
|
||||
* --radius-lg → 24px search inputs / medium pills
|
||||
* --radius-pill → 30px Nike's signature button capsule —
|
||||
* we keep the literal value rather than
|
||||
* collapse to 9999px because the number
|
||||
* itself is a brand-recognisable detail
|
||||
* (same precedent as Apple's 980px). */
|
||||
--radius-sm: 8px;
|
||||
--radius-md: 20px;
|
||||
--radius-lg: 24px;
|
||||
--radius-pill: 30px;
|
||||
|
||||
/* ─── Elevation (3 levels) ────────────────────────────────────────
|
||||
* Nike's elevation philosophy is radically flat (§6 — "no card
|
||||
* shadows, no hover lifts, no floating elements"). Three sanctioned
|
||||
* levels:
|
||||
* - flat → none (the default; tonal contrast does the work)
|
||||
* - ring → hairline 1px box-shadow border (the only sanctioned
|
||||
* container outline)
|
||||
* - raised → SAME as ring. We deliberately collapse raised onto
|
||||
* the ring tier because Nike refuses blur shadows.
|
||||
* Components that reference var(--elev-raised) still
|
||||
* resolve, but produce the flat / hairline result Nike
|
||||
* intends — never the Material-flavoured float that
|
||||
* would betray the brand. */
|
||||
--elev-flat: none;
|
||||
--elev-ring: 0 0 0 1px var(--border);
|
||||
--elev-raised: 0 0 0 1px var(--border);
|
||||
|
||||
/* ─── Focus ring ──────────────────────────────────────────────────
|
||||
* Nike's keyboard-focus signal is the documented 2px solid blue
|
||||
* ring at rgba(39, 93, 197, 1) — §4 / §6. This is the ONLY blue
|
||||
* moment in the entire UI, a deliberate chromatic break from the
|
||||
* monochrome canvas to satisfy WCAG focus visibility. We bind the
|
||||
* literal value rather than the schema's transparent-mixed accent
|
||||
* formula because Nike's focus is intentionally NOT the accent
|
||||
* (which is black, indistinguishable from the surface it would
|
||||
* outline). */
|
||||
--focus-ring: 0 0 0 2px rgba(39, 93, 197, 1);
|
||||
|
||||
/* ─── Motion ──────────────────────────────────────────────────────
|
||||
* Nike's transitions are all 200ms ease (§4 — "Transition:
|
||||
* background 200ms ease", "border-color 200ms ease", "opacity
|
||||
* 200ms ease for image swap on hover"). The schema defaults match
|
||||
* already; we keep them verbatim. --motion-fast (150ms) covers
|
||||
* micro-states like hover-tints and focus; --motion-base (200ms)
|
||||
* is the documented hero-state duration. */
|
||||
--motion-fast: 150ms;
|
||||
--motion-base: 200ms;
|
||||
--ease-standard: cubic-bezier(0.2, 0, 0, 1);
|
||||
|
||||
/* ─── Layout ──────────────────────────────────────────────────────
|
||||
* Nike's content scaffolding (§5) is 1920px max container with
|
||||
* 1440px standard content width. We bind --container-max to 1440px
|
||||
* because that is the documented STANDARD content width, the one
|
||||
* artifacts should target — the 1920px ceiling is reserved for
|
||||
* full-bleed hero photography that breaks out of the container.
|
||||
* Gutters step 48 → 24 → 16 across desktop / tablet / phone, the
|
||||
* exact horizontal-padding values from §5. */
|
||||
--container-max: 1440px;
|
||||
--container-gutter-desktop: 48px;
|
||||
--container-gutter-tablet: 24px;
|
||||
--container-gutter-phone: 16px;
|
||||
}
|
||||
865
design-systems/pinterest/components.html
Normal file
865
design-systems/pinterest/components.html
Normal file
|
|
@ -0,0 +1,865 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Pinterest — reference components</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="Reference fixture for design-systems/pinterest. Warm visual-
|
||||
discovery canvas, olive/sand neutrals, Pinterest Red (#e60023) as
|
||||
the single bold accent, Pin Sans across the entire hierarchy,
|
||||
generous 12–28px radii, photography-first masonry layouts."
|
||||
/>
|
||||
|
||||
<style>
|
||||
:root {
|
||||
--bg: #ffffff;
|
||||
--surface: #f6f6f3;
|
||||
--surface-warm: #e5e5e0;
|
||||
|
||||
--fg: #211922;
|
||||
--fg-2: #000000;
|
||||
--muted: #62625b;
|
||||
--meta: #91918c;
|
||||
|
||||
--border: #c8c8c1;
|
||||
--border-soft: #e0e0d9;
|
||||
|
||||
--accent: #e60023;
|
||||
--accent-on: #000000;
|
||||
--accent-hover: color-mix(in oklab, var(--accent), black 8%);
|
||||
--accent-active: color-mix(in oklab, var(--accent), black 14%);
|
||||
|
||||
--success: #103c25;
|
||||
--warn: #c47700;
|
||||
--danger: #9e0a0a;
|
||||
|
||||
--font-display:
|
||||
"Pin Sans", -apple-system, system-ui, "Segoe UI", Roboto, "Oxygen-Sans",
|
||||
"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", Ubuntu, Cantarell,
|
||||
"Fira Sans", "Droid Sans", "Helvetica Neue", Helvetica,
|
||||
"ヒラギノ角ゴ Pro W3", メイリオ, Meiryo, "MS Pゴシック", Arial,
|
||||
sans-serif;
|
||||
--font-body:
|
||||
"Pin Sans", -apple-system, system-ui, "Segoe UI", Roboto, "Oxygen-Sans",
|
||||
"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", Ubuntu, Cantarell,
|
||||
"Fira Sans", "Droid Sans", "Helvetica Neue", Helvetica,
|
||||
"ヒラギノ角ゴ Pro W3", メイリオ, Meiryo, "MS Pゴシック", Arial,
|
||||
sans-serif;
|
||||
--font-mono:
|
||||
ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Monaco,
|
||||
Consolas, monospace;
|
||||
|
||||
--text-xs: 12px;
|
||||
--text-sm: 14px;
|
||||
--text-base: 16px;
|
||||
--text-lg: 18px;
|
||||
--text-xl: 22px;
|
||||
--text-2xl: 28px;
|
||||
--text-3xl: 44px;
|
||||
--text-4xl: 70px;
|
||||
|
||||
--leading-body: 1.4;
|
||||
--leading-tight: 1.15;
|
||||
--tracking-display: -0.02em;
|
||||
|
||||
--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: 80px;
|
||||
--section-y-tablet: 48px;
|
||||
--section-y-phone: 32px;
|
||||
|
||||
--radius-sm: 12px;
|
||||
--radius-md: 16px;
|
||||
--radius-lg: 28px;
|
||||
--radius-pill: 9999px;
|
||||
|
||||
--elev-flat: none;
|
||||
--elev-ring: 0 0 0 1px var(--border);
|
||||
--elev-raised: 0 4px 16px rgba(33, 25, 34, 0.06);
|
||||
|
||||
--focus-ring: 0 0 0 3px color-mix(in oklab, #435ee5, transparent 70%);
|
||||
|
||||
--motion-fast: 150ms;
|
||||
--motion-base: 200ms;
|
||||
--ease-standard: cubic-bezier(0.2, 0, 0, 1);
|
||||
|
||||
--container-max: 1280px;
|
||||
--container-gutter-desktop: 32px;
|
||||
--container-gutter-tablet: 24px;
|
||||
--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);
|
||||
font-weight: 400;
|
||||
line-height: var(--leading-body);
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
/* ─── Layout primitives ─────────────────────────────────── */
|
||||
.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-soft); }
|
||||
@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 — Pin Sans, plum-black, friendly weights ─ */
|
||||
h1, h2, h3 {
|
||||
font-family: var(--font-display);
|
||||
line-height: var(--leading-tight);
|
||||
color: var(--fg);
|
||||
margin: 0;
|
||||
}
|
||||
h1 {
|
||||
font-size: var(--text-4xl);
|
||||
font-weight: 600;
|
||||
letter-spacing: var(--tracking-display);
|
||||
}
|
||||
h2 {
|
||||
font-size: var(--text-2xl);
|
||||
font-weight: 700;
|
||||
letter-spacing: var(--tracking-display);
|
||||
}
|
||||
h3 {
|
||||
font-size: var(--text-lg);
|
||||
font-weight: 700;
|
||||
line-height: 1.3;
|
||||
}
|
||||
p { margin: 0; }
|
||||
.lede {
|
||||
font-size: var(--text-base);
|
||||
line-height: var(--leading-body);
|
||||
color: var(--muted);
|
||||
}
|
||||
.body-muted { color: var(--muted); }
|
||||
.body-meta { color: var(--meta); font-size: var(--text-sm); }
|
||||
.body-sm { font-size: var(--text-sm); }
|
||||
|
||||
.eyebrow {
|
||||
font-family: var(--font-display);
|
||||
font-size: var(--text-xs);
|
||||
font-weight: 700;
|
||||
line-height: 1.3;
|
||||
color: var(--accent);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.1em;
|
||||
}
|
||||
|
||||
.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 — 16px radius, generous but never pill ────── */
|
||||
.btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: var(--space-2);
|
||||
padding: 12px 20px;
|
||||
border: 2px solid transparent;
|
||||
border-radius: var(--radius-md);
|
||||
font-family: var(--font-display);
|
||||
font-weight: 700;
|
||||
font-size: var(--text-sm);
|
||||
line-height: 1;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
transition:
|
||||
background-color var(--motion-fast) var(--ease-standard),
|
||||
color var(--motion-fast) var(--ease-standard),
|
||||
border-color var(--motion-fast) var(--ease-standard),
|
||||
box-shadow var(--motion-fast) var(--ease-standard);
|
||||
}
|
||||
.btn:focus-visible { outline: none; box-shadow: var(--focus-ring); }
|
||||
.btn:active { transform: translateY(1px); }
|
||||
|
||||
/* Primary: Pinterest Red — DESIGN.md §4 carries BLACK text on red */
|
||||
.btn-primary {
|
||||
background: var(--accent);
|
||||
color: var(--accent-on);
|
||||
}
|
||||
.btn-primary:hover { background: var(--accent-hover); }
|
||||
.btn-primary:active { background: var(--accent-active); }
|
||||
|
||||
/* Secondary: warm Sand Gray — Pinterest's signature secondary surface */
|
||||
.btn-secondary {
|
||||
background: var(--surface-warm);
|
||||
color: var(--fg-2);
|
||||
}
|
||||
.btn-secondary:hover {
|
||||
background: color-mix(in oklab, var(--surface-warm), black 6%);
|
||||
}
|
||||
|
||||
/* Ghost / transparent — DESIGN.md §4 tertiary action */
|
||||
.btn-ghost {
|
||||
background: transparent;
|
||||
color: var(--fg-2);
|
||||
}
|
||||
.btn-ghost:hover {
|
||||
background: color-mix(in oklab, var(--surface-warm), transparent 50%);
|
||||
}
|
||||
|
||||
/* Circular action — DESIGN.md §4: --radius-pill, warm light bg */
|
||||
.btn-icon {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
padding: 0;
|
||||
border: none;
|
||||
border-radius: var(--radius-pill);
|
||||
background: var(--border-soft);
|
||||
color: var(--fg);
|
||||
cursor: pointer;
|
||||
transition:
|
||||
background-color var(--motion-fast) var(--ease-standard),
|
||||
transform var(--motion-fast) var(--ease-standard);
|
||||
}
|
||||
.btn-icon:hover {
|
||||
background: color-mix(in oklab, var(--border-soft), black 8%);
|
||||
}
|
||||
.btn-icon:active { transform: scale(0.94); }
|
||||
.btn-icon:focus-visible { outline: none; box-shadow: var(--focus-ring); }
|
||||
.btn-icon svg { width: 18px; height: 18px; }
|
||||
|
||||
/* ─── Inputs — 16px radius, Warm Silver border ──────────── */
|
||||
.field {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-2);
|
||||
}
|
||||
.field label {
|
||||
font-family: var(--font-display);
|
||||
font-size: var(--text-sm);
|
||||
font-weight: 700;
|
||||
color: var(--fg);
|
||||
}
|
||||
.field input {
|
||||
padding: 11px 15px;
|
||||
border-radius: var(--radius-md);
|
||||
border: 1px solid var(--meta);
|
||||
background: var(--bg);
|
||||
color: var(--fg);
|
||||
font-family: inherit;
|
||||
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::placeholder { color: var(--meta); }
|
||||
.field input:focus-visible {
|
||||
border-color: var(--fg);
|
||||
box-shadow: var(--focus-ring);
|
||||
}
|
||||
.field-help {
|
||||
font-size: var(--text-xs);
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
/* ─── Cards — Pinterest pin shape, soft warm shadow ─────── */
|
||||
.pin {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: var(--surface);
|
||||
border-radius: var(--radius-lg);
|
||||
overflow: hidden;
|
||||
transition:
|
||||
transform var(--motion-base) var(--ease-standard),
|
||||
box-shadow var(--motion-base) var(--ease-standard);
|
||||
}
|
||||
.pin:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: var(--elev-raised);
|
||||
}
|
||||
.pin-photo {
|
||||
position: relative;
|
||||
aspect-ratio: 4 / 5;
|
||||
background: var(--surface-warm);
|
||||
overflow: hidden;
|
||||
}
|
||||
.pin-photo::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background:
|
||||
radial-gradient(
|
||||
ellipse at 30% 25%,
|
||||
color-mix(in oklab, var(--accent), transparent 70%) 0%,
|
||||
transparent 55%
|
||||
),
|
||||
linear-gradient(
|
||||
150deg,
|
||||
var(--surface-warm) 0%,
|
||||
var(--border-soft) 45%,
|
||||
var(--surface) 100%
|
||||
);
|
||||
}
|
||||
.pin-photo.alt-2::before {
|
||||
background:
|
||||
radial-gradient(
|
||||
ellipse at 70% 75%,
|
||||
color-mix(in oklab, #103c25, transparent 60%) 0%,
|
||||
transparent 60%
|
||||
),
|
||||
linear-gradient(
|
||||
210deg,
|
||||
var(--border-soft) 0%,
|
||||
var(--surface-warm) 60%,
|
||||
var(--surface) 100%
|
||||
);
|
||||
}
|
||||
.pin-photo.alt-3::before {
|
||||
background:
|
||||
radial-gradient(
|
||||
ellipse at 50% 35%,
|
||||
color-mix(in oklab, var(--accent), transparent 80%) 0%,
|
||||
transparent 50%
|
||||
),
|
||||
linear-gradient(
|
||||
330deg,
|
||||
var(--surface) 0%,
|
||||
var(--surface-warm) 45%,
|
||||
var(--border-soft) 100%
|
||||
);
|
||||
}
|
||||
.pin-overlay {
|
||||
position: absolute;
|
||||
top: var(--space-3);
|
||||
right: var(--space-3);
|
||||
z-index: 1;
|
||||
}
|
||||
.pin-badge {
|
||||
position: absolute;
|
||||
bottom: var(--space-3);
|
||||
left: var(--space-3);
|
||||
z-index: 1;
|
||||
}
|
||||
.pin-meta {
|
||||
padding: var(--space-4) var(--space-5) var(--space-5);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-2);
|
||||
}
|
||||
.pin-title {
|
||||
font-family: var(--font-display);
|
||||
font-size: var(--text-base);
|
||||
font-weight: 700;
|
||||
color: var(--fg);
|
||||
line-height: 1.3;
|
||||
}
|
||||
.pin-subtitle {
|
||||
font-size: var(--text-sm);
|
||||
color: var(--muted);
|
||||
line-height: var(--leading-body);
|
||||
}
|
||||
.pin-author {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-2);
|
||||
font-size: var(--text-xs);
|
||||
color: var(--muted);
|
||||
margin-block-start: var(--space-2);
|
||||
}
|
||||
.pin-avatar {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: var(--radius-pill);
|
||||
background: var(--border-soft);
|
||||
color: var(--fg);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-family: var(--font-display);
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* ─── Badges ────────────────────────────────────────────── */
|
||||
.badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--space-2);
|
||||
padding: 5px var(--space-3);
|
||||
font-family: var(--font-display);
|
||||
font-size: var(--text-xs);
|
||||
font-weight: 700;
|
||||
line-height: 1.2;
|
||||
border-radius: var(--radius-pill);
|
||||
}
|
||||
.badge-soft {
|
||||
background: hsla(60, 20%, 98%, 0.85);
|
||||
color: var(--fg);
|
||||
backdrop-filter: blur(8px);
|
||||
-webkit-backdrop-filter: blur(8px);
|
||||
}
|
||||
.badge-warm {
|
||||
background: var(--surface-warm);
|
||||
color: var(--fg);
|
||||
}
|
||||
.badge-status {
|
||||
background: color-mix(in oklab, var(--success), transparent 88%);
|
||||
color: var(--success);
|
||||
}
|
||||
.badge-dot {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: var(--radius-pill);
|
||||
background: currentColor;
|
||||
}
|
||||
|
||||
/* ─── Links ────────────────────────────────────────────── */
|
||||
a {
|
||||
color: var(--fg);
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 3px;
|
||||
text-decoration-color: var(--border);
|
||||
transition: text-decoration-color var(--motion-fast) var(--ease-standard);
|
||||
}
|
||||
a:hover { text-decoration-color: var(--fg); }
|
||||
a:focus-visible {
|
||||
outline: none;
|
||||
box-shadow: var(--focus-ring);
|
||||
border-radius: var(--radius-sm);
|
||||
}
|
||||
|
||||
/* ─── Kbd ─────────────────────────────────────────────── */
|
||||
kbd {
|
||||
display: inline-block;
|
||||
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);
|
||||
line-height: 1.2;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
/* ─── Hero / nav / grids ───────────────────────────────── */
|
||||
.nav-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding-block: var(--space-4);
|
||||
gap: var(--space-4);
|
||||
}
|
||||
.nav-logo {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--space-2);
|
||||
font-family: var(--font-display);
|
||||
font-size: var(--text-lg);
|
||||
font-weight: 700;
|
||||
color: var(--accent);
|
||||
letter-spacing: var(--tracking-display);
|
||||
text-decoration: none;
|
||||
}
|
||||
.nav-logo svg { width: 24px; height: 24px; }
|
||||
.nav-search {
|
||||
flex: 1;
|
||||
max-width: 540px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-2);
|
||||
padding: 10px var(--space-4);
|
||||
background: var(--surface);
|
||||
border-radius: var(--radius-pill);
|
||||
border: 1px solid transparent;
|
||||
transition: border-color var(--motion-fast) var(--ease-standard);
|
||||
}
|
||||
.nav-search:focus-within { border-color: var(--fg); box-shadow: var(--focus-ring); }
|
||||
.nav-search svg { width: 16px; height: 16px; color: var(--muted); flex-shrink: 0; }
|
||||
.nav-search input {
|
||||
flex: 1;
|
||||
border: none;
|
||||
background: transparent;
|
||||
outline: none;
|
||||
font: inherit;
|
||||
color: var(--fg);
|
||||
min-width: 0;
|
||||
}
|
||||
.nav-search input::placeholder { color: var(--muted); }
|
||||
.nav-cluster {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-2);
|
||||
}
|
||||
|
||||
.hero-grid {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1.3fr) minmax(0, 1fr);
|
||||
gap: var(--space-12);
|
||||
align-items: start;
|
||||
}
|
||||
@media (max-width: 1023px) {
|
||||
.hero-grid { grid-template-columns: 1fr; gap: var(--space-8); }
|
||||
}
|
||||
.hero-actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: var(--space-3);
|
||||
margin-block-start: var(--space-6);
|
||||
}
|
||||
.hero-aside {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: var(--space-3);
|
||||
align-content: start;
|
||||
}
|
||||
.hero-aside .pin:nth-child(1) { transform: translateY(0); }
|
||||
.hero-aside .pin:nth-child(2) { transform: translateY(var(--space-8)); }
|
||||
|
||||
.pins-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: var(--space-5);
|
||||
margin-block-start: var(--space-8);
|
||||
align-items: start;
|
||||
}
|
||||
.pins-grid .pin:nth-child(2) { margin-block-start: var(--space-6); }
|
||||
.pins-grid .pin:nth-child(3) { margin-block-start: var(--space-12); }
|
||||
@media (max-width: 1023px) {
|
||||
.pins-grid { grid-template-columns: repeat(2, 1fr); }
|
||||
.pins-grid .pin:nth-child(3) { margin-block-start: 0; }
|
||||
}
|
||||
@media (max-width: 639px) {
|
||||
.pins-grid { grid-template-columns: 1fr; gap: var(--space-4); }
|
||||
.pins-grid .pin:nth-child(2),
|
||||
.pins-grid .pin:nth-child(3) { margin-block-start: 0; }
|
||||
}
|
||||
|
||||
.form-row {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1.2fr) minmax(0, 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);
|
||||
padding: var(--space-6);
|
||||
background: var(--surface);
|
||||
border-radius: var(--radius-lg);
|
||||
}
|
||||
.form-actions {
|
||||
display: flex;
|
||||
gap: var(--space-3);
|
||||
flex-wrap: wrap;
|
||||
margin-block-start: var(--space-2);
|
||||
}
|
||||
.form-meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: var(--space-3);
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.icon { width: 16px; height: 16px; flex-shrink: 0; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main class="container">
|
||||
<nav class="nav-row" aria-label="Primary">
|
||||
<a href="#" class="nav-logo" aria-label="Pinterest">
|
||||
<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
|
||||
<path d="M12 2a10 10 0 0 0-3.6 19.32c-.07-.74-.13-1.86.03-2.66.14-.71 1.06-4.52 1.06-4.52s-.27-.54-.27-1.35c0-1.27.74-2.21 1.66-2.21.78 0 1.16.59 1.16 1.29 0 .79-.5 1.96-.76 3.05-.21.91.46 1.65 1.36 1.65 1.63 0 2.88-1.72 2.88-4.2 0-2.2-1.58-3.74-3.83-3.74-2.61 0-4.14 1.96-4.14 3.98 0 .79.3 1.63.68 2.09.08.09.09.17.06.27-.07.27-.21.85-.24.97-.04.16-.13.19-.29.12-1.1-.51-1.78-2.11-1.78-3.4 0-2.77 2.01-5.31 5.81-5.31 3.05 0 5.42 2.17 5.42 5.08 0 3.03-1.91 5.47-4.57 5.47-.89 0-1.73-.46-2.02-1.01l-.55 2.09c-.2.77-.74 1.73-1.1 2.32A10 10 0 1 0 12 2z" />
|
||||
</svg>
|
||||
Pinterest
|
||||
</a>
|
||||
<label class="nav-search" aria-label="Search Pinterest">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
||||
stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
|
||||
aria-hidden="true">
|
||||
<circle cx="11" cy="11" r="7" />
|
||||
<path d="M21 21l-4.3-4.3" />
|
||||
</svg>
|
||||
<input type="search" placeholder="Search for ideas" />
|
||||
</label>
|
||||
<div class="nav-cluster">
|
||||
<button class="btn-icon" type="button" aria-label="Notifications">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
||||
stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
|
||||
aria-hidden="true">
|
||||
<path d="M18 16v-5a6 6 0 0 0-12 0v5l-2 2h16z" />
|
||||
<path d="M10 20a2 2 0 0 0 4 0" />
|
||||
</svg>
|
||||
</button>
|
||||
<button class="btn-icon" type="button" aria-label="Messages">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
||||
stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
|
||||
aria-hidden="true">
|
||||
<path d="M21 12a8 8 0 1 1-3.3-6.5L21 4l-1.5 3.5A7.9 7.9 0 0 1 21 12z" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<section data-od-id="hero">
|
||||
<div class="hero-grid">
|
||||
<div class="stack-4">
|
||||
<p class="eyebrow">Reference fixture · pinterest</p>
|
||||
<h1 style="max-width: 14ch">Find the next thing you love.</h1>
|
||||
<p class="lede" style="max-width: 56ch">
|
||||
A warm-canvas token system distilled from Pinterest's 2026 brand —
|
||||
Pin Sans across the entire hierarchy, olive/sand neutrals that
|
||||
feel handcrafted, and Pinterest Red used once per screen as the
|
||||
only confident accent. The masonry below uses the same
|
||||
<code style="font-family: var(--font-mono); font-size: var(--text-sm)">:root</code>
|
||||
block agents paste into every artifact.
|
||||
</p>
|
||||
<div class="hero-actions">
|
||||
<a href="./tokens.css" class="btn btn-primary">
|
||||
Explore tokens
|
||||
<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">Read the brand</a>
|
||||
<button type="button" class="btn btn-ghost">Save for later</button>
|
||||
</div>
|
||||
<div class="form-meta" style="margin-block-start: var(--space-6)">
|
||||
<span class="badge badge-status">
|
||||
<span class="badge-dot" aria-hidden="true"></span>
|
||||
56 tokens passing schema
|
||||
</span>
|
||||
<span class="body-meta">
|
||||
Press <kbd>⌘</kbd> <kbd>K</kbd> to jump to the token grep.
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<aside class="hero-aside" aria-label="Sample pins">
|
||||
<article class="pin">
|
||||
<div class="pin-photo">
|
||||
<button class="btn-icon pin-overlay" type="button"
|
||||
aria-label="Save this pin"
|
||||
style="background: var(--accent); color: var(--accent-on)">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
||||
stroke-width="2.25" stroke-linecap="round"
|
||||
stroke-linejoin="round" aria-hidden="true">
|
||||
<path d="M12 5v14M5 12h14" />
|
||||
</svg>
|
||||
</button>
|
||||
<span class="badge badge-soft pin-badge">Inspiration</span>
|
||||
</div>
|
||||
<div class="pin-meta">
|
||||
<p class="pin-title">Soft palettes for warm rooms</p>
|
||||
<div class="pin-author">
|
||||
<span class="pin-avatar" aria-hidden="true">SA</span>
|
||||
Sand & Olive
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
<article class="pin">
|
||||
<div class="pin-photo alt-2">
|
||||
<button class="btn-icon pin-overlay" type="button" aria-label="More">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
||||
stroke-width="2" stroke-linecap="round"
|
||||
stroke-linejoin="round" aria-hidden="true">
|
||||
<circle cx="5" cy="12" r="1.4" />
|
||||
<circle cx="12" cy="12" r="1.4" />
|
||||
<circle cx="19" cy="12" r="1.4" />
|
||||
</svg>
|
||||
</button>
|
||||
<span class="badge badge-warm pin-badge">Idea pin</span>
|
||||
</div>
|
||||
<div class="pin-meta">
|
||||
<p class="pin-title">Green hour, plum dusk</p>
|
||||
<div class="pin-author">
|
||||
<span class="pin-avatar" aria-hidden="true">PL</span>
|
||||
Plum Studio
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</aside>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section data-od-id="features">
|
||||
<div class="stack-3">
|
||||
<p class="eyebrow">What the fixture exercises</p>
|
||||
<h2 style="max-width: 24ch">Three pins, one Pinterest Red, no cool grays.</h2>
|
||||
<p class="body-muted" style="max-width: 58ch">
|
||||
Every visible value resolves through
|
||||
<code style="font-family: var(--font-mono); font-size: var(--text-sm)">var(--*)</code>.
|
||||
The masonry offsets, the warm card surface, the 16px button radius,
|
||||
the plum-tinted whisper shadow on hover — all driven by the same
|
||||
paste block above.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="pins-grid">
|
||||
<article class="pin">
|
||||
<div class="pin-photo">
|
||||
<button class="btn-icon pin-overlay" type="button"
|
||||
aria-label="Save this pin"
|
||||
style="background: var(--accent); color: var(--accent-on)">
|
||||
<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
|
||||
<path d="M12 21s-7-4.5-7-10a4 4 0 0 1 7-2.7A4 4 0 0 1 19 11c0 5.5-7 10-7 10z" />
|
||||
</svg>
|
||||
</button>
|
||||
<span class="badge badge-soft pin-badge">Warmth as a system</span>
|
||||
</div>
|
||||
<div class="pin-meta">
|
||||
<h3>Olive & sand neutrals</h3>
|
||||
<p class="pin-subtitle">
|
||||
Surface, border, and meta colors all lean warm. The page never
|
||||
feels clinical — even the disabled state carries an olive
|
||||
undertone.
|
||||
</p>
|
||||
<div class="pin-author">
|
||||
<span class="pin-avatar" aria-hidden="true">OD</span>
|
||||
<a href="./tokens.css">Inspect the neutrals</a>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="pin">
|
||||
<div class="pin-photo alt-2">
|
||||
<button class="btn-icon pin-overlay" type="button"
|
||||
aria-label="Save this pin"
|
||||
style="background: var(--accent); color: var(--accent-on)">
|
||||
<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
|
||||
<path d="M12 21s-7-4.5-7-10a4 4 0 0 1 7-2.7A4 4 0 0 1 19 11c0 5.5-7 10-7 10z" />
|
||||
</svg>
|
||||
</button>
|
||||
<span class="badge badge-warm pin-badge">Pin Sans, end to end</span>
|
||||
</div>
|
||||
<div class="pin-meta">
|
||||
<h3>One font, twelve to seventy</h3>
|
||||
<p class="pin-subtitle">
|
||||
Pin Sans handles every label — 12px caption, 16px body, 70px
|
||||
hero. No secondary display family, no monospace beyond
|
||||
<kbd>⌘</kbd> hints.
|
||||
</p>
|
||||
<div class="pin-author">
|
||||
<span class="pin-avatar" aria-hidden="true">PS</span>
|
||||
<a href="./DESIGN.md">Read the rule</a>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="pin">
|
||||
<div class="pin-photo alt-3">
|
||||
<button class="btn-icon pin-overlay" type="button"
|
||||
aria-label="Save this pin"
|
||||
style="background: var(--accent); color: var(--accent-on)">
|
||||
<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
|
||||
<path d="M12 21s-7-4.5-7-10a4 4 0 0 1 7-2.7A4 4 0 0 1 19 11c0 5.5-7 10-7 10z" />
|
||||
</svg>
|
||||
</button>
|
||||
<span class="badge badge-soft pin-badge">Generous geometry</span>
|
||||
</div>
|
||||
<div class="pin-meta">
|
||||
<h3>12, 16, 28 — never pill on text</h3>
|
||||
<p class="pin-subtitle">
|
||||
Buttons land at 16px, pin cards at 28px, chips at 12px. Pinterest's
|
||||
soft geometry sits between sharp and pill — friendly, never
|
||||
corporate.
|
||||
</p>
|
||||
<div class="pin-author">
|
||||
<span class="pin-avatar" aria-hidden="true">RA</span>
|
||||
<a href="./tokens.css">Inspect the radii</a>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section data-od-id="form">
|
||||
<div class="form-row">
|
||||
<div class="stack-4">
|
||||
<p class="eyebrow">Form components</p>
|
||||
<h2 style="max-width: 22ch">Save ideas to your private board.</h2>
|
||||
<p class="body-muted" style="max-width: 52ch">
|
||||
Inputs sit on the white canvas with a 1px Warm Silver edge and
|
||||
the brand's 16px button radius. Focus rings switch to Focus Blue
|
||||
so they survive both the warm secondary surface and the red CTA —
|
||||
an accent-tinted ring would vanish into Pinterest Red.
|
||||
</p>
|
||||
<p class="body-meta">
|
||||
Full reference at <a href="./tokens.css">tokens.css</a> ·
|
||||
brand at <a href="./DESIGN.md">DESIGN.md</a>.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<form class="form" onsubmit="event.preventDefault();">
|
||||
<div class="field">
|
||||
<label for="email">Email</label>
|
||||
<input
|
||||
id="email"
|
||||
type="email"
|
||||
placeholder="you@somewhere.studio"
|
||||
autocomplete="email"
|
||||
required
|
||||
/>
|
||||
<p class="field-help">
|
||||
We’ll send a single weekly idea digest — never more.
|
||||
</p>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="board">Board name</label>
|
||||
<input
|
||||
id="board"
|
||||
type="text"
|
||||
placeholder="Warm rooms, soft light"
|
||||
autocomplete="off"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-actions">
|
||||
<button type="submit" class="btn btn-primary">Create board</button>
|
||||
<button type="button" class="btn btn-secondary">Add later</button>
|
||||
</div>
|
||||
<div class="form-meta" style="margin-block-start: var(--space-2)">
|
||||
<span class="badge badge-warm">
|
||||
<svg viewBox="0 0 24 24" fill="currentColor"
|
||||
style="width: 12px; height: 12px" aria-hidden="true">
|
||||
<path d="M12 2l2.9 6.9 7.1.6-5.4 4.7 1.7 7L12 17l-6.3 4.2 1.7-7L2 9.5l7.1-.6z" />
|
||||
</svg>
|
||||
Private board
|
||||
</span>
|
||||
<span class="body-meta">No one sees this but you.</span>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
262
design-systems/pinterest/tokens.css
Normal file
262
design-systems/pinterest/tokens.css
Normal file
|
|
@ -0,0 +1,262 @@
|
|||
/* ─────────────────────────────────────────────────────────────────────────
|
||||
* design-systems/pinterest/tokens.css
|
||||
*
|
||||
* Structured token bindings for "Design System Inspired by Pinterest".
|
||||
* Warm visual-discovery canvas: olive/sand neutrals, signature Pinterest
|
||||
* Red (#e60023) as the singular bold accent, Pin Sans across the entire
|
||||
* type hierarchy, generous 12–28px radii, and minimal shadow because
|
||||
* the photography is the elevation.
|
||||
*
|
||||
* This file pre-compiles the values described in `DESIGN.md` into the
|
||||
* schema shared with every OD design system. Agents should paste the
|
||||
* `:root { … }` block verbatim into the first `<style>` of an artifact,
|
||||
* then resolve every value via `var(--*)` from that point on.
|
||||
*
|
||||
* Key brand decisions encoded here:
|
||||
*
|
||||
* #Bind 1 — --bg is Canvas White (#ffffff) per DESIGN.md §9. The
|
||||
* warm-white reading the brand gives off comes from its
|
||||
* olive/sand neutrals (#e5e5e0, #e0e0d9, #91918c), never
|
||||
* from tinting the page background itself. --surface is
|
||||
* Fog (#f6f6f3) — the warm light tier used for cards,
|
||||
* search bars, and elevated containers; one subtle step
|
||||
* warmer than the page so masonry pin cards earn a
|
||||
* recognizable shape even without a border.
|
||||
*
|
||||
* #Bind 2 — --surface-warm is Sand Gray (#e5e5e0), Pinterest's
|
||||
* warmest published surface — the secondary button
|
||||
* background, the saved-pin chip background, the warm
|
||||
* shelf behind clustered metadata (DESIGN.md §2 Surface).
|
||||
*
|
||||
* #Bind 3 — --fg is Plum Black (#211922), NOT pure black. DESIGN.md
|
||||
* §7 explicitly forbids #000000 as primary text because
|
||||
* Pinterest's near-black carries a plum/warm undertone
|
||||
* that holds together with the olive neutrals. --fg-2
|
||||
* binds to #000000 — the brand reserves pure black for
|
||||
* button labels and high-emphasis secondary text only.
|
||||
*
|
||||
* #Bind 4 — --accent-on is #000000, not white. Pinterest's primary
|
||||
* red button (#e60023) carries black text — DESIGN.md §4
|
||||
* calls this out as "an unusual choice for contrast on
|
||||
* red". White-on-red would over-saturate the canvas and
|
||||
* read as alarm; black-on-red reads as confident, lifestyle-
|
||||
* magazine punch. Honor the brand value over schema default.
|
||||
*
|
||||
* #Bind 5 — Radius scale follows the brand's signature generous
|
||||
* soft-circle geometry. 12px (--radius-sm) is the brand's
|
||||
* "Standard" radius for small chips and links; 16px
|
||||
* (--radius-md) is the workhorse — every button, every
|
||||
* input, every secondary card; 28px (--radius-lg) is the
|
||||
* "Large" container radius for tab elements and hero
|
||||
* panels. The brand also publishes a 20px "Comfortable"
|
||||
* and a 40px "Hero" tier but those collapse into the
|
||||
* shared md/lg slots without losing identity (§5).
|
||||
*
|
||||
* #Bind 6 — --focus-ring uses Focus Blue (#435ee5) at 30% opacity,
|
||||
* NOT the schema's accent-tinted default. DESIGN.md §2
|
||||
* binds focus to a dedicated blue (--comp-button-color-
|
||||
* border-focus-outer-transparent: #435ee5) so the ring
|
||||
* survives both the warm-red CTA fill and the warm-sand
|
||||
* secondary surface — a red-tinted ring would disappear
|
||||
* into Pinterest Red.
|
||||
*
|
||||
* #Bind 7 — --leading-body is 1.40 (DESIGN.md §3 Body) — tighter
|
||||
* than the schema default 1.5 because Pinterest's
|
||||
* information-dense pin grid pairs short body strings
|
||||
* with image-first layouts; longer leading would shred
|
||||
* the rhythm of pin metadata under photographs.
|
||||
*
|
||||
* #Bind 8 — --elev-raised is a plum-tinted whisper rather than a
|
||||
* cool gray drop. DESIGN.md §6 commits to "minimal shadows
|
||||
* — Pinterest is flat by design, depth from content".
|
||||
* When a shadow is necessary (sticky panels, overlays),
|
||||
* the tint matches the warm plum --fg so the elevation
|
||||
* reads as part of the warm system instead of a cold
|
||||
* halo.
|
||||
*
|
||||
* Brand-specific extensions: none in this revision. The Performance
|
||||
* Purple (#6845ab) and Recommendation Purple (#7e238b) product-tier
|
||||
* accents from DESIGN.md §2 stay inline at the tier-specific
|
||||
* components that need them; promoting them to schema slots would
|
||||
* encourage non-tier surfaces to reach for them.
|
||||
* ─────────────────────────────────────────────────────────────────── */
|
||||
|
||||
:root {
|
||||
/* ─── Surface (3 levels) ────────────────────────────────────────
|
||||
* Canvas White is the page; Fog (#f6f6f3) is the warm light card
|
||||
* tier; Sand Gray (#e5e5e0) is the warm secondary surface (the
|
||||
* brand's named "secondary button" background). The neutrals lean
|
||||
* olive/sand, never cool steel — that warmth is the identity. */
|
||||
--bg: #ffffff;
|
||||
--surface: #f6f6f3;
|
||||
--surface-warm: #e5e5e0;
|
||||
|
||||
/* ─── Foreground ramp (4 levels) ────────────────────────────────
|
||||
* Plum Black (#211922) carries every heading and primary body
|
||||
* string — a warm near-black with a plum undertone. Pure black
|
||||
* (#000000) is reserved for the secondary tier (button labels,
|
||||
* high-contrast captions). Olive Gray (#62625b) is the muted
|
||||
* description tone; Warm Silver (#91918c) is the disabled /
|
||||
* metadata tone — also the brand's input-border color. */
|
||||
--fg: #211922;
|
||||
--fg-2: #000000;
|
||||
--muted: #62625b;
|
||||
--meta: #91918c;
|
||||
|
||||
/* ─── Border (2 levels) ─────────────────────────────────────────
|
||||
* Pinterest is flat-by-design; borders appear sparingly. The
|
||||
* default --border is the published "Border Disabled" tone
|
||||
* (#c8c8c1) — soft, warm, doesn't compete with the photography.
|
||||
* --border-soft is Warm Light (#e0e0d9), an even softer hairline
|
||||
* for inner row separators. */
|
||||
--border: #c8c8c1;
|
||||
--border-soft: #e0e0d9;
|
||||
|
||||
/* ─── Accent ────────────────────────────────────────────────────
|
||||
* Pinterest Red — the only brand accent, used for primary CTAs
|
||||
* and the highest-signal brand moments. Cap at two visible uses
|
||||
* per screen; when a third red element appears, neutralize one.
|
||||
* Note that --accent-on is #000000 (not white): the brand's
|
||||
* primary red button carries black text per DESIGN.md §4. */
|
||||
--accent: #e60023;
|
||||
--accent-on: #000000;
|
||||
--accent-hover: color-mix(in oklab, var(--accent), black 8%);
|
||||
--accent-active: color-mix(in oklab, var(--accent), black 14%);
|
||||
|
||||
/* ─── Semantic ──────────────────────────────────────────────────
|
||||
* Error Red is the published Pinterest error value (DESIGN.md §2,
|
||||
* #9e0a0a). Success and warn are not specified by the brand;
|
||||
* they bind to desaturated warm values that survive the olive/
|
||||
* sand canvas without competing with Pinterest Red. Keep total
|
||||
* semantic-color pixels under five percent of any page. */
|
||||
--success: #103c25; /* Green 700 — the brand's published green */
|
||||
--warn: #c47700; /* burnt amber — warm-aligned, never tailwind yellow */
|
||||
--danger: #9e0a0a; /* Error Red — DESIGN.md §2 */
|
||||
|
||||
/* ─── Typography — fonts ────────────────────────────────────────
|
||||
* Pin Sans is the proprietary face that carries everything from
|
||||
* 12px caption to 70px hero — no secondary display family, no
|
||||
* monospace need beyond kbd. The fallback chain mirrors the
|
||||
* brand's global stack including Japanese fallbacks
|
||||
* (ヒラギノ角ゴ / メイリオ / MS Pゴシック) reflecting
|
||||
* Pinterest's CJK reach. Display and body share the same stack
|
||||
* — visual identity comes from the family itself. */
|
||||
--font-display:
|
||||
"Pin Sans", -apple-system, system-ui, "Segoe UI", Roboto, "Oxygen-Sans",
|
||||
"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", Ubuntu, Cantarell,
|
||||
"Fira Sans", "Droid Sans", "Helvetica Neue", Helvetica,
|
||||
"ヒラギノ角ゴ Pro W3", メイリオ, Meiryo, "MS Pゴシック", Arial,
|
||||
sans-serif;
|
||||
--font-body:
|
||||
"Pin Sans", -apple-system, system-ui, "Segoe UI", Roboto, "Oxygen-Sans",
|
||||
"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", Ubuntu, Cantarell,
|
||||
"Fira Sans", "Droid Sans", "Helvetica Neue", Helvetica,
|
||||
"ヒラギノ角ゴ Pro W3", メイリオ, Meiryo, "MS Pゴシック", Arial,
|
||||
sans-serif;
|
||||
--font-mono:
|
||||
ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Monaco,
|
||||
Consolas, monospace;
|
||||
|
||||
/* ─── Typography — type scale (px) ──────────────────────────────
|
||||
* Derived from DESIGN.md §3 Hierarchy. The system clusters
|
||||
* tightly between 12 and 16px for app-like density, jumps to
|
||||
* 28px for the published Section Heading, and explodes to 70px
|
||||
* for the Display Hero — the dramatic gap is the brand's voice.
|
||||
* The intermediate lg/xl/3xl tiers fill in the curve so generic
|
||||
* components have somewhere to land. */
|
||||
--text-xs: 12px; /* Caption — small tags, footer disclaimer */
|
||||
--text-sm: 14px; /* Caption Bold — metadata, button labels */
|
||||
--text-base: 16px; /* Body — standard reading, nav links */
|
||||
--text-lg: 18px; /* H3 / featured body */
|
||||
--text-xl: 22px; /* H2 / subsection title */
|
||||
--text-2xl: 28px; /* Section Heading — DESIGN.md §3 */
|
||||
--text-3xl: 44px; /* H1 — hero subtitles, large numerals */
|
||||
--text-4xl: 70px; /* Display Hero — DESIGN.md §3 */
|
||||
|
||||
/* ─── Typography — leading & tracking ───────────────────────────
|
||||
* Body leads at 1.40 per DESIGN.md §3 — tighter than the schema
|
||||
* default 1.5 because Pinterest's pin metadata is short and
|
||||
* sits under photography. Display headings carry negative
|
||||
* tracking (DESIGN.md "-1.2px at 28px" → -0.02em scales evenly
|
||||
* across the display sizes); body and caption hold at zero. */
|
||||
--leading-body: 1.4;
|
||||
--leading-tight: 1.15;
|
||||
--tracking-display: -0.02em;
|
||||
|
||||
/* ─── Spacing — base scale ──────────────────────────────────────
|
||||
* 8px base unit per DESIGN.md §5. The brand publishes off-grid
|
||||
* sub-pixel values (5.5, 7, 10, 11, 22) for fine icon alignment;
|
||||
* the shared scale stays on the standard 4px grid — off-grid
|
||||
* spacing belongs inline at the component level. */
|
||||
--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 rhythm ────────────────────────────────────────────
|
||||
* DESIGN.md §5 commits to "Large jumps: 32px → 80px → 100px for
|
||||
* section spacing". 80px is the desktop default — generous
|
||||
* breathing room between content blocks; the pin grid itself
|
||||
* stays dense within the block. 32px is the phone floor. */
|
||||
--section-y-desktop: 80px;
|
||||
--section-y-tablet: 48px;
|
||||
--section-y-phone: 32px;
|
||||
|
||||
/* ─── Radius ────────────────────────────────────────────────────
|
||||
* The brand's signature soft-circle geometry. 16px is the
|
||||
* workhorse (every button, every input — DESIGN.md §4) and
|
||||
* lands on --radius-md. 12px (--radius-sm) is the brand's
|
||||
* "Standard" small-card radius; 28px (--radius-lg) is the brand
|
||||
* "Large" container radius for tab elements and hero panels.
|
||||
* --radius-pill (9999px) carries circular action buttons and
|
||||
* avatars (DESIGN.md §5 "Circle: 50%"). */
|
||||
--radius-sm: 12px;
|
||||
--radius-md: 16px;
|
||||
--radius-lg: 28px;
|
||||
--radius-pill: 9999px;
|
||||
|
||||
/* ─── Elevation ─────────────────────────────────────────────────
|
||||
* Pinterest is flat-by-design — most surfaces sit on the canvas
|
||||
* with no shadow at all (DESIGN.md §6). When elevation is
|
||||
* necessary (overlays, sticky panels), the shadow is a plum-
|
||||
* tinted whisper rather than a cool gray drop — the warmth of
|
||||
* the system carries through into the elevation. */
|
||||
--elev-flat: none;
|
||||
--elev-ring: 0 0 0 1px var(--border);
|
||||
--elev-raised: 0 4px 16px rgba(33, 25, 34, 0.06);
|
||||
|
||||
/* ─── Focus ring ────────────────────────────────────────────────
|
||||
* Focus Blue (#435ee5) at 30% opacity — the brand's published
|
||||
* focus color (DESIGN.md §2 --comp-button-color-border-focus-
|
||||
* outer-transparent). A blue ring survives both the warm-red
|
||||
* primary CTA fill and the warm-sand secondary surface; an
|
||||
* accent-tinted ring would vanish into Pinterest Red. */
|
||||
--focus-ring: 0 0 0 3px color-mix(in oklab, #435ee5, transparent 70%);
|
||||
|
||||
/* ─── Motion ────────────────────────────────────────────────────
|
||||
* DESIGN.md did not specify transition timings (static
|
||||
* extraction scope). We bind defaults consistent with the
|
||||
* brand's friendly tactile feel: 150ms for micro-states, 200ms
|
||||
* for general state changes. Standard easing — short,
|
||||
* purposeful, no overshoot. */
|
||||
--motion-fast: 150ms;
|
||||
--motion-base: 200ms;
|
||||
--ease-standard: cubic-bezier(0.2, 0, 0, 1);
|
||||
|
||||
/* ─── Layout ────────────────────────────────────────────────────
|
||||
* 1280px container — the standard magazine-y content width for
|
||||
* hero, feature, and form sections. The masonry pin grid itself
|
||||
* breathes wider on ultra-wide screens via its own column rules,
|
||||
* but the shared container stays at 1280 because that is the
|
||||
* geometry agents will most often generate (lifestyle landing
|
||||
* pages, signup flows, marketing sections). Gutters tighten
|
||||
* progressively per DESIGN.md §8 small-mobile clause. */
|
||||
--container-max: 1280px;
|
||||
--container-gutter-desktop: 32px;
|
||||
--container-gutter-tablet: 24px;
|
||||
--container-gutter-phone: 16px;
|
||||
}
|
||||
414
design-systems/playstation/components.html
Normal file
414
design-systems/playstation/components.html
Normal file
|
|
@ -0,0 +1,414 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>PlayStation — reference components</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="Reference fixture for design-systems/playstation. Console Black hero,
|
||||
Paper White content panels, SST weight 300 display type, 1.2× scale-on-hover
|
||||
with PlayStation Cyan fill and a 2px PlayStation Blue ring."
|
||||
/>
|
||||
|
||||
<style>
|
||||
:root {
|
||||
--bg: #000000;
|
||||
--surface: #ffffff;
|
||||
--surface-warm: #f5f7fa;
|
||||
|
||||
--fg: #ffffff;
|
||||
--fg-2: var(--fg);
|
||||
--muted: #cccccc;
|
||||
--meta: var(--muted);
|
||||
|
||||
--border: #cccccc;
|
||||
--border-soft: #f3f3f3;
|
||||
|
||||
--accent: #0070cc;
|
||||
--accent-on: #ffffff;
|
||||
--accent-hover: #1eaedb;
|
||||
--accent-active: #0068bd;
|
||||
|
||||
--success: #16a34a;
|
||||
--warn: #eab308;
|
||||
--danger: #c81b3a;
|
||||
|
||||
--font-display: "SST", "Playstation SST", Arial, Helvetica, sans-serif;
|
||||
--font-body: "SST", "Playstation SST", Arial, Helvetica, sans-serif;
|
||||
--font-mono: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Monaco, Consolas, monospace;
|
||||
|
||||
--text-xs: 12px;
|
||||
--text-sm: 14px;
|
||||
--text-base: 18px;
|
||||
--text-lg: 22px;
|
||||
--text-xl: 28px;
|
||||
--text-2xl: 35px;
|
||||
--text-3xl: 44px;
|
||||
--text-4xl: 54px;
|
||||
|
||||
--leading-body: 1.5;
|
||||
--leading-tight: 1.25;
|
||||
--tracking-display: -0.1px;
|
||||
|
||||
--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: 96px;
|
||||
--section-y-tablet: 64px;
|
||||
--section-y-phone: 48px;
|
||||
|
||||
--radius-sm: 6px;
|
||||
--radius-md: 12px;
|
||||
--radius-lg: 24px;
|
||||
--radius-pill: 999px;
|
||||
|
||||
--elev-flat: none;
|
||||
--elev-ring: 0 0 0 1px var(--border);
|
||||
--elev-raised: rgba(0, 0, 0, 0.08) 0 5px 9px 0;
|
||||
|
||||
--focus-ring: 0 0 0 2px #0070cc;
|
||||
|
||||
--motion-fast: 180ms;
|
||||
--motion-base: 200ms;
|
||||
--ease-standard: cubic-bezier(0.2, 0, 0, 1);
|
||||
|
||||
--container-max: 1600px;
|
||||
--container-gutter-desktop: 64px;
|
||||
--container-gutter-tablet: 32px;
|
||||
--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);
|
||||
font-weight: 400;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
/* ─── Layout ─────────────────────────────────────────────── */
|
||||
.container { max-width: var(--container-max); margin-inline: auto; padding-inline: var(--container-gutter-desktop); }
|
||||
section { padding-block: var(--section-y-desktop); }
|
||||
@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); }
|
||||
}
|
||||
|
||||
/* ─── Surface alternation — three-channel layout ────────── */
|
||||
.panel-light {
|
||||
background: var(--surface);
|
||||
color: #1f1f1f; /* Deep Charcoal — body on Paper White */
|
||||
}
|
||||
.panel-light h1,
|
||||
.panel-light h2,
|
||||
.panel-light h3 { color: #000000; } /* Display Ink */
|
||||
.panel-light .lead { color: #6b6b6b; } /* Body Gray */
|
||||
.panel-light .body-muted { color: #6b6b6b; }
|
||||
.panel-light .eyebrow { color: #6b6b6b; }
|
||||
|
||||
/* ─── Typography — SST weight 300 at display sizes ──────── */
|
||||
h1, h2, h3 {
|
||||
font-family: var(--font-display);
|
||||
font-weight: 300; /* Quiet-authority — the PlayStation voice */
|
||||
line-height: var(--leading-tight);
|
||||
margin: 0;
|
||||
letter-spacing: var(--tracking-display);
|
||||
}
|
||||
h1 { font-size: var(--text-4xl); }
|
||||
h2 { font-size: var(--text-2xl); letter-spacing: 0; }
|
||||
h3 { font-size: var(--text-lg); letter-spacing: 0.1px; }
|
||||
p { margin: 0; }
|
||||
.lead { font-size: var(--text-base); color: var(--muted); line-height: var(--leading-body); }
|
||||
.body-muted { color: var(--muted); }
|
||||
.body-sm { font-size: var(--text-sm); }
|
||||
/* No ALL-CAPS — sentence case eyebrow */
|
||||
.eyebrow {
|
||||
font-size: var(--text-sm);
|
||||
color: var(--muted);
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.1px;
|
||||
}
|
||||
.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 — full-pill, 1.2× cyan power-on hover ─────── */
|
||||
.btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--space-2);
|
||||
padding: 12px 24px;
|
||||
border-radius: var(--radius-pill);
|
||||
font-family: var(--font-body);
|
||||
font-size: var(--text-base);
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.4px;
|
||||
line-height: 1.25;
|
||||
cursor: pointer;
|
||||
border: 2px solid transparent; /* Reserved gutter so hover border doesn't shift */
|
||||
text-decoration: none;
|
||||
transform-origin: center;
|
||||
transition: background-color var(--motion-fast) var(--ease-standard),
|
||||
color var(--motion-fast) var(--ease-standard),
|
||||
border-color var(--motion-fast) var(--ease-standard),
|
||||
box-shadow var(--motion-fast) var(--ease-standard),
|
||||
transform var(--motion-fast) var(--ease-standard);
|
||||
}
|
||||
.btn:active { opacity: 0.6; }
|
||||
.btn:focus-visible { outline: none; box-shadow: var(--focus-ring); }
|
||||
|
||||
/* Primary — PlayStation Blue → Cyan power-on signature */
|
||||
.btn-primary {
|
||||
background: var(--accent);
|
||||
color: var(--accent-on);
|
||||
}
|
||||
.btn-primary:hover {
|
||||
background: var(--accent-hover);
|
||||
color: var(--accent-on);
|
||||
border-color: #ffffff;
|
||||
box-shadow: 0 0 0 2px var(--accent);
|
||||
transform: scale(1.2);
|
||||
}
|
||||
|
||||
/* Secondary on dark — White surface, blue ink */
|
||||
.btn-secondary {
|
||||
background: var(--surface);
|
||||
color: var(--accent);
|
||||
border-color: var(--surface);
|
||||
}
|
||||
.btn-secondary:hover {
|
||||
background: var(--accent-hover);
|
||||
color: var(--accent-on);
|
||||
border-color: #ffffff;
|
||||
box-shadow: 0 0 0 2px var(--accent);
|
||||
transform: scale(1.2);
|
||||
}
|
||||
/* On Paper White panels, secondary inverts to outline ink */
|
||||
.panel-light .btn-secondary {
|
||||
background: transparent;
|
||||
color: var(--accent);
|
||||
border-color: var(--accent);
|
||||
}
|
||||
.panel-light .btn-secondary:hover {
|
||||
background: var(--accent-hover);
|
||||
color: var(--accent-on);
|
||||
border-color: #ffffff;
|
||||
box-shadow: 0 0 0 2px var(--accent);
|
||||
transform: scale(1.2);
|
||||
}
|
||||
|
||||
/* ─── Inputs — 3px radius, 2px blue focus ring ──────────── */
|
||||
.field { display: flex; flex-direction: column; gap: var(--space-2); }
|
||||
.field label { font-size: var(--text-sm); font-weight: 600; color: #1f1f1f; }
|
||||
.field input {
|
||||
padding: 12px 14px;
|
||||
border-radius: 3px; /* Tighter than the system — DESIGN.md §Inputs */
|
||||
border: 1px solid var(--border);
|
||||
background: #ffffff;
|
||||
color: #1f1f1f;
|
||||
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 {
|
||||
box-shadow: var(--focus-ring); /* Ring does the work — no border-color shift */
|
||||
}
|
||||
.field input::placeholder { color: rgba(0, 0, 0, 0.6); }
|
||||
.field-help { font-size: var(--text-sm); color: #6b6b6b; }
|
||||
|
||||
/* ─── Cards — feature tile, 24px radius, feather lift ───── */
|
||||
.card {
|
||||
background: var(--surface);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: var(--space-8);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-4);
|
||||
box-shadow: var(--elev-raised);
|
||||
border: 1px solid var(--border-soft);
|
||||
color: #1f1f1f;
|
||||
}
|
||||
.card h3 { color: #000000; }
|
||||
.card a { color: var(--accent); }
|
||||
.card a:hover { color: #1883fd; }
|
||||
|
||||
/* ─── Badges ────────────────────────────────────────────── */
|
||||
.badge {
|
||||
display: inline-flex; align-items: center; gap: var(--space-2);
|
||||
padding: 4px var(--space-3);
|
||||
border-radius: var(--radius-pill);
|
||||
font-size: var(--text-xs); font-weight: 600; line-height: 1.5;
|
||||
}
|
||||
.badge-success { color: var(--success); background: color-mix(in oklab, var(--success), transparent 86%); }
|
||||
.badge-dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
|
||||
|
||||
/* Platform pill — distinctive game-store white-on-dark tag */
|
||||
.pill {
|
||||
display: inline-flex; align-items: center;
|
||||
padding: 6px 14px;
|
||||
border-radius: var(--radius-pill);
|
||||
background: var(--surface);
|
||||
color: #000000;
|
||||
font-size: var(--text-sm); font-weight: 500;
|
||||
}
|
||||
|
||||
/* ─── Links & inline ────────────────────────────────────── */
|
||||
a { color: #53b1ff; text-decoration: none; }
|
||||
a:hover { color: #1883fd; }
|
||||
.panel-light a { color: var(--accent); }
|
||||
.panel-light a:hover { color: #1883fd; }
|
||||
|
||||
kbd {
|
||||
font-family: var(--font-mono); font-size: var(--text-xs);
|
||||
padding: 2px 6px; border-radius: 3px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.16);
|
||||
background: rgba(255, 255, 255, 0.08); color: var(--muted);
|
||||
}
|
||||
|
||||
/* ─── Layout helpers ────────────────────────────────────── */
|
||||
.hero-grid { display: grid; grid-template-columns: 1.5fr 1fr; gap: var(--space-12); align-items: end; }
|
||||
@media (max-width: 1023px) { .hero-grid { grid-template-columns: 1fr; gap: var(--space-8); } }
|
||||
.hero-actions { display: flex; gap: var(--space-4); margin-block-start: var(--space-8); flex-wrap: wrap; }
|
||||
.hero-meta {
|
||||
display: flex; flex-direction: column; gap: var(--space-3);
|
||||
padding: var(--space-5);
|
||||
border-radius: var(--radius-md);
|
||||
background: rgba(255, 255, 255, 0.06);
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
backdrop-filter: blur(8px);
|
||||
}
|
||||
.features-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-6); }
|
||||
@media (max-width: 1023px) { .features-grid { grid-template-columns: 1fr 1fr; } }
|
||||
@media (max-width: 639px) { .features-grid { grid-template-columns: 1fr; } }
|
||||
.form-row { display: grid; grid-template-columns: 1.2fr 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: 460px; }
|
||||
.form-actions { display: flex; gap: var(--space-3); margin-block-start: var(--space-3); flex-wrap: wrap; }
|
||||
.icon { width: 16px; height: 16px; flex-shrink: 0; }
|
||||
.row-between { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); }
|
||||
.pill-row { display: flex; gap: var(--space-2); flex-wrap: wrap; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main class="container">
|
||||
<section data-od-id="hero">
|
||||
<div class="hero-grid">
|
||||
<div class="stack-6">
|
||||
<p class="eyebrow">Reference fixture · playstation</p>
|
||||
<h1>Play has no limits.</h1>
|
||||
<p class="lead" style="max-width: 52ch">
|
||||
From masthead to footer, PlayStation moves like a console powering on —
|
||||
quiet-weight SST headlines lead the eye, and every primary button
|
||||
scales 1.2× into PlayStation Cyan on hover.
|
||||
</p>
|
||||
<div class="pill-row" aria-label="Supported platforms">
|
||||
<span class="pill">PS5</span>
|
||||
<span class="pill">PS4</span>
|
||||
<span class="pill">PSVR2</span>
|
||||
</div>
|
||||
<div class="hero-actions">
|
||||
<a href="./tokens.css" class="btn btn-primary">
|
||||
Enter the system
|
||||
<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">Read the spec</a>
|
||||
</div>
|
||||
</div>
|
||||
<aside class="hero-meta" aria-label="System status">
|
||||
<div class="row-between">
|
||||
<span class="body-sm">PSN status</span>
|
||||
<span class="badge badge-success">
|
||||
<span class="badge-dot" aria-hidden="true"></span>
|
||||
All services online
|
||||
</span>
|
||||
</div>
|
||||
<p class="body-sm" style="color: var(--muted)">Network reviewed <time datetime="2026-05-15">2026-05-15</time> · v1.0</p>
|
||||
<p class="body-sm" style="color: var(--muted)">Press <kbd>⌘</kbd> <kbd>K</kbd> to browse tokens.</p>
|
||||
</aside>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section data-od-id="features" class="panel-light">
|
||||
<div class="stack-3">
|
||||
<p class="eyebrow">What this fixture exercises</p>
|
||||
<h2 style="max-width: 24ch">Three surfaces. One vertical channel.</h2>
|
||||
</div>
|
||||
<div class="features-grid" style="margin-block-start: var(--space-12)">
|
||||
<article class="card">
|
||||
<h3>Quiet-authority headlines</h3>
|
||||
<p class="body-sm" style="color: #6b6b6b">
|
||||
SST weight 300 from 22 to 54px — the typographic
|
||||
signature that lets product photography lead while
|
||||
the chrome stays restrained.
|
||||
</p>
|
||||
<a href="./tokens.css" class="body-sm">Inspect tokens →</a>
|
||||
</article>
|
||||
<article class="card">
|
||||
<h3>Cyan power-on hover</h3>
|
||||
<p class="body-sm" style="color: #6b6b6b">
|
||||
Hover any primary button: fill snaps to PlayStation
|
||||
Cyan, a 2px white border appears, a blue ring blooms,
|
||||
and the button scales 1.2× — all in 180ms.
|
||||
</p>
|
||||
<a href="./DESIGN.md" class="body-sm">Read the rule →</a>
|
||||
</article>
|
||||
<article class="card">
|
||||
<h3>Eleven-radius system</h3>
|
||||
<p class="body-sm" style="color: #6b6b6b">
|
||||
3px on inputs, 12px on covers, 24px on hero cards,
|
||||
999px on pill CTAs. Square corners are forbidden —
|
||||
every surface lands on a declared tier.
|
||||
</p>
|
||||
<a href="./tokens.css" class="body-sm">Inspect radius →</a>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section data-od-id="form" class="panel-light">
|
||||
<div class="form-row">
|
||||
<div class="stack-4">
|
||||
<p class="eyebrow">Form components</p>
|
||||
<h2>Inputs in the Paper White panel.</h2>
|
||||
<p class="body-sm" style="color: #6b6b6b; max-width: 48ch">
|
||||
Input borders sit at 1px Mute Gray with a 3px radius —
|
||||
tighter than the rest of the system. The focus indicator
|
||||
is a 2px PlayStation Blue ring; no border-color change.
|
||||
</p>
|
||||
</div>
|
||||
<form class="form" onsubmit="event.preventDefault();">
|
||||
<div class="field">
|
||||
<label for="psn">PSN ID</label>
|
||||
<input id="psn" type="text" placeholder="enter your PSN ID" autocomplete="username" required />
|
||||
<p class="field-help">We'll pair this with your console on first sign-in.</p>
|
||||
</div>
|
||||
<div class="form-actions">
|
||||
<button type="submit" class="btn btn-primary">Sign in</button>
|
||||
<button type="button" class="btn btn-secondary">Create an account</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
150
design-systems/playstation/tokens.css
Normal file
150
design-systems/playstation/tokens.css
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
/* ─────────────────────────────────────────────────────────────────────────
|
||||
* design-systems/playstation/tokens.css
|
||||
*
|
||||
* Structured token bindings for "Design System Inspired by PlayStation".
|
||||
* Console-grade retail: a three-surface channel (black hero → white content
|
||||
* → cobalt footer), SST weight 300 at display sizes, and a signature
|
||||
* 1.2× scale-on-hover that fires across the page like a power-on chime.
|
||||
*
|
||||
* Key brand decisions encoded here:
|
||||
* - Console Black (#000000) is the dominant brand canvas; Paper White
|
||||
* is the lifted --surface tier where editorial content lives
|
||||
* - PlayStation Blue (#0070cc) is the immovable anchor — primary CTAs,
|
||||
* focus ring, footer
|
||||
* - PlayStation Cyan (#1eaedb) lives only in --accent-hover — the
|
||||
* "cyan never appears at rest" rule encoded into the token layer
|
||||
* - SST weight 300 at 22–54px is the typographic voice (component layer
|
||||
* enforces the weight; tokens carry sizes)
|
||||
* - 180ms is the canonical interaction window for hover/focus/scale
|
||||
* - Eleven-radius system collapsed onto the schema spine: 6/12/24/999
|
||||
* ─────────────────────────────────────────────────────────────────── */
|
||||
|
||||
:root {
|
||||
/* ─── Surface ─────────────────────────────────────────────────────
|
||||
* Three-surface channel. Console Black anchors the masthead and hero
|
||||
* zones; Paper White is the editorial gallery panel; Ice Mist is the
|
||||
* gradient end-stop that quietly lifts panels off pure white. */
|
||||
--bg: #000000; /* Console Black — masthead / hero canvas */
|
||||
--surface: #ffffff; /* Paper White — primary content panel */
|
||||
--surface-warm: #f5f7fa; /* Ice Mist — atmospheric panel lift */
|
||||
|
||||
/* ─── Foreground ──────────────────────────────────────────────────
|
||||
* On Console Black the primary text is Inverse White. Body Gray
|
||||
* (#6b6b6b) is reserved for white-panel context (set at component
|
||||
* layer); Mute Gray (#cccccc) is the muted tier visible on dark. */
|
||||
--fg: #ffffff; /* Inverse White — text on dark canvas */
|
||||
--fg-2: var(--fg); /* alias — single display weight throughout */
|
||||
--muted: #cccccc; /* Mute Gray — tertiary labels on dark */
|
||||
--meta: var(--muted); /* alias — one muted tier */
|
||||
|
||||
/* ─── Border ──────────────────────────────────────────────────────
|
||||
* Borders sit quiet — the brand prefers spacing and feather shadow to
|
||||
* define edges. #cccccc matches the input border spec from DESIGN.md
|
||||
* §Inputs; Divider Tint is the editorial row separator. */
|
||||
--border: #cccccc; /* Mute Gray — input edges, default rule */
|
||||
--border-soft: #f3f3f3; /* Divider Tint — quiet horizontal rule */
|
||||
|
||||
/* ─── Accent ──────────────────────────────────────────────────────
|
||||
* PlayStation Blue is the immovable anchor. PlayStation Cyan lives
|
||||
* exclusively in --accent-hover — never as a resting background.
|
||||
* Active state lands on Dark Link Blue. */
|
||||
--accent: #0070cc; /* PlayStation Blue — primary CTA / anchor */
|
||||
--accent-on: #ffffff;
|
||||
--accent-hover: #1eaedb; /* PlayStation Cyan — hover/focus only */
|
||||
--accent-active: #0068bd; /* Dark Link Blue — pressed state */
|
||||
|
||||
/* ─── Semantic ────────────────────────────────────────────────────
|
||||
* Warning Red is the only semantic color the brand declares. Success
|
||||
* and warn ride the schema defaults — PlayStation reserves its warm
|
||||
* palette for Commerce Orange, not for semantic green/yellow. */
|
||||
--success: #16a34a;
|
||||
--warn: #eab308;
|
||||
--danger: #c81b3a; /* Warning Red — form errors */
|
||||
|
||||
/* ─── Typography ──────────────────────────────────────────────────
|
||||
* SST is Sony's proprietary global typeface. Fallback chain:
|
||||
* Arial → Helvetica → system sans. The weight 300 voice is enforced
|
||||
* at the component layer — type tokens carry sizes, not weights. */
|
||||
--font-display: "SST", "Playstation SST", Arial, Helvetica, sans-serif;
|
||||
--font-body: "SST", "Playstation SST", Arial, Helvetica, sans-serif;
|
||||
--font-mono: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Monaco, Consolas, monospace;
|
||||
|
||||
/* Type scale — DESIGN.md §3 Typography.
|
||||
* 54px hero → 12px legal microcopy. SST weight 300 occupies 22–54px;
|
||||
* body lives at 18px; mini CTAs and captions at 14px. */
|
||||
--text-xs: 12px; /* Micro Caption — footer / legal */
|
||||
--text-sm: 14px; /* Caption Body / Mini CTA */
|
||||
--text-base: 18px; /* Body Relaxed — standard reading */
|
||||
--text-lg: 22px; /* Compact Display — module titles */
|
||||
--text-xl: 28px; /* Mid Display — section headings */
|
||||
--text-2xl: 35px; /* Large Display — feature headlines */
|
||||
--text-3xl: 44px; /* Hero Display L — secondary hero */
|
||||
--text-4xl: 54px; /* Hero Display XL — biggest SST moment */
|
||||
|
||||
--leading-body: 1.5; /* Body Relaxed — 1.5 throughout reading */
|
||||
--leading-tight: 1.25; /* Display rhythm — 1.25 on 22–54px */
|
||||
--tracking-display: -0.1px; /* 54px hero — barely-there tightening */
|
||||
|
||||
/* ─── Spacing ─────────────────────────────────────────────────────
|
||||
* 8px base unit per DESIGN.md §5. Component-level micro-scale (3/6/
|
||||
* 9/10px) lives inside individual rules; the section-y tokens below
|
||||
* carry the gallery rhythm. */
|
||||
--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 rhythm — gallery pace. DESIGN.md collapsing strategy:
|
||||
* 96px → 64px → 48px as the viewport narrows. */
|
||||
--section-y-desktop: 96px;
|
||||
--section-y-tablet: 64px;
|
||||
--section-y-phone: 48px;
|
||||
|
||||
/* ─── Radius ──────────────────────────────────────────────────────
|
||||
* Eleven-value radius system collapsed onto the schema spine:
|
||||
* 6px → compact buttons / inline images
|
||||
* 12px → standard game cover images & content media
|
||||
* 24px → hero cards, primary feature frames
|
||||
* 999px → full-pill primary buttons (the CTA signature)
|
||||
* Inputs override --radius-sm with their own 3px at component layer. */
|
||||
--radius-sm: 6px;
|
||||
--radius-md: 12px;
|
||||
--radius-lg: 24px;
|
||||
--radius-pill: 999px;
|
||||
|
||||
/* ─── Elevation ───────────────────────────────────────────────────
|
||||
* Whisper-or-shout ladder per DESIGN.md §6: shadow opacity lands on
|
||||
* 0.06 / 0.08 / 0.16 / 0.8 with no middle ground. --elev-raised
|
||||
* holds the standard 0.08 grid-tile feather. */
|
||||
--elev-flat: none;
|
||||
--elev-ring: 0 0 0 1px var(--border);
|
||||
--elev-raised: rgba(0, 0, 0, 0.08) 0 5px 9px 0; /* Feather tile lift */
|
||||
|
||||
/* ─── Focus ring ──────────────────────────────────────────────────
|
||||
* 2px PlayStation Blue ring — the exact spec from DESIGN.md §Inputs
|
||||
* and §Buttons. Brand blue does focus; cyan does hover; the two
|
||||
* never overlap at rest. */
|
||||
--focus-ring: 0 0 0 2px #0070cc;
|
||||
|
||||
/* ─── Motion ──────────────────────────────────────────────────────
|
||||
* 180ms is the canonical PlayStation interaction window — every
|
||||
* hover / focus / scale transition lands here. The brand's "power-
|
||||
* on" feel comes from synchronizing four properties at 180ms, not
|
||||
* from a longer duration. */
|
||||
--motion-fast: 180ms;
|
||||
--motion-base: 200ms;
|
||||
--ease-standard: cubic-bezier(0.2, 0, 0, 1);
|
||||
|
||||
/* ─── Layout ──────────────────────────────────────────────────────
|
||||
* 1600px container — PlayStation explicitly tunes through 4K-TV
|
||||
* browsing contexts (breakpoints reach 2120px). Outer gutters follow
|
||||
* DESIGN.md §5: 64/32/16 from desktop to phone. */
|
||||
--container-max: 1600px;
|
||||
--container-gutter-desktop: 64px;
|
||||
--container-gutter-tablet: 32px;
|
||||
--container-gutter-phone: 16px;
|
||||
}
|
||||
496
design-systems/spacex/components.html
Normal file
496
design-systems/spacex/components.html
Normal file
|
|
@ -0,0 +1,496 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>SpaceX — reference components</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="Reference fixture for design-systems/spacex. Pure black canvas,
|
||||
spectral white type, full-viewport cinematic sections, D-DIN industrial
|
||||
uppercase, ghost-button as the sole interactive element, zero shadows."
|
||||
/>
|
||||
|
||||
<style>
|
||||
:root {
|
||||
--bg: #000000;
|
||||
--surface: #000000;
|
||||
--surface-warm: var(--surface);
|
||||
|
||||
--fg: #f0f0fa;
|
||||
--fg-2: var(--fg);
|
||||
--muted: rgba(240, 240, 250, 0.7);
|
||||
--meta: var(--muted);
|
||||
|
||||
--border: rgba(240, 240, 250, 0.35);
|
||||
--border-soft: rgba(240, 240, 250, 0.1);
|
||||
|
||||
--accent: #f0f0fa;
|
||||
--accent-on: #000000;
|
||||
--accent-hover: #ffffff;
|
||||
--accent-active: #d8d8e6;
|
||||
|
||||
--success: #16a34a;
|
||||
--warn: #eab308;
|
||||
--danger: #dc2626;
|
||||
|
||||
--font-display: "D-DIN-Bold", "D-DIN", "DIN Alternate", "Helvetica Neue", Arial, Verdana, sans-serif;
|
||||
--font-body: "D-DIN", "DIN Alternate", "Helvetica Neue", Arial, Verdana, sans-serif;
|
||||
--font-mono: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Monaco, Consolas, monospace;
|
||||
|
||||
--text-xs: 10px;
|
||||
--text-sm: 12px;
|
||||
--text-base: 16px;
|
||||
--text-lg: 20px;
|
||||
--text-xl: 24px;
|
||||
--text-2xl: 32px;
|
||||
--text-3xl: 40px;
|
||||
--text-4xl: 48px;
|
||||
|
||||
--leading-body: 1.5;
|
||||
--leading-tight: 1.0;
|
||||
--tracking-display: 0.02em;
|
||||
|
||||
--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: 120px;
|
||||
--section-y-tablet: 80px;
|
||||
--section-y-phone: 56px;
|
||||
|
||||
--radius-sm: 4px;
|
||||
--radius-md: 4px;
|
||||
--radius-lg: 4px;
|
||||
--radius-pill: 32px;
|
||||
|
||||
--elev-flat: none;
|
||||
--elev-ring: 0 0 0 1px var(--border);
|
||||
--elev-raised: none;
|
||||
|
||||
--focus-ring: 0 0 0 2px var(--accent);
|
||||
|
||||
--motion-fast: 150ms;
|
||||
--motion-base: 200ms;
|
||||
--ease-standard: cubic-bezier(0.2, 0, 0, 1);
|
||||
|
||||
--container-max: 1440px;
|
||||
--container-gutter-desktop: 24px;
|
||||
--container-gutter-tablet: 20px;
|
||||
--container-gutter-phone: 18px;
|
||||
}
|
||||
|
||||
/* ─── 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;
|
||||
}
|
||||
|
||||
/* ─── Layout ─────────────────────────────────────────────── */
|
||||
.container { max-width: var(--container-max); margin-inline: auto; padding-inline: var(--container-gutter-desktop); }
|
||||
section { padding-block: var(--section-y-desktop); }
|
||||
/* Sharp 4px ghost dividers between cinematic scenes — never decorative */
|
||||
section + section { border-top: 1px solid var(--border-soft); }
|
||||
@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 — universal uppercase + positive tracking ─ */
|
||||
h1, h2, h3 {
|
||||
font-family: var(--font-display);
|
||||
font-weight: 700;
|
||||
line-height: var(--leading-tight);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: var(--tracking-display);
|
||||
margin: 0;
|
||||
}
|
||||
h1 { font-size: var(--text-4xl); }
|
||||
h2 { font-size: var(--text-2xl); }
|
||||
h3 { font-size: var(--text-base); letter-spacing: 0.04em; }
|
||||
p { margin: 0; }
|
||||
.lead {
|
||||
font-size: var(--text-base);
|
||||
color: var(--fg);
|
||||
line-height: var(--leading-body);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.02em;
|
||||
font-weight: 400;
|
||||
}
|
||||
.body-muted { color: var(--muted); }
|
||||
/* Caption Bold / Nav Link Bold tier — DESIGN.md §3 (13px / 1.17px) */
|
||||
.caption-bold {
|
||||
font-family: var(--font-body);
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
line-height: 0.94;
|
||||
letter-spacing: 1.17px;
|
||||
text-transform: uppercase;
|
||||
color: var(--fg);
|
||||
}
|
||||
/* Eyebrow — micro tier; DESIGN.md §3 Micro = 10px / 1px tracking */
|
||||
.eyebrow {
|
||||
font-size: var(--text-xs);
|
||||
font-weight: 400;
|
||||
line-height: 0.94;
|
||||
letter-spacing: 1px;
|
||||
text-transform: uppercase;
|
||||
color: var(--muted);
|
||||
}
|
||||
.stack-3 > * + * { margin-block-start: var(--space-3); }
|
||||
.stack-4 > * + * { margin-block-start: var(--space-4); }
|
||||
.stack-6 > * + * { margin-block-start: var(--space-6); }
|
||||
.stack-8 > * + * { margin-block-start: var(--space-8); }
|
||||
|
||||
/* ─── Buttons — Ghost is the only variant ────────────────
|
||||
* DESIGN.md §4: rgba(240,240,250,0.1) bg, 1px spectral border,
|
||||
* 32px radius, 18px padding. The sole interactive element. */
|
||||
.btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--space-2);
|
||||
padding: 18px var(--space-6);
|
||||
border-radius: var(--radius-pill);
|
||||
font-family: var(--font-body);
|
||||
font-size: var(--text-sm);
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
letter-spacing: 1.17px;
|
||||
text-transform: uppercase;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
transition:
|
||||
background-color var(--motion-fast) var(--ease-standard),
|
||||
color var(--motion-fast) var(--ease-standard),
|
||||
border-color var(--motion-fast) var(--ease-standard);
|
||||
}
|
||||
.btn:focus-visible { outline: none; box-shadow: var(--focus-ring); }
|
||||
/* Ghost — the canonical SpaceX button */
|
||||
.btn-ghost {
|
||||
background: rgba(240, 240, 250, 0.1);
|
||||
color: var(--fg);
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
.btn-ghost:hover {
|
||||
background: rgba(240, 240, 250, 0.18);
|
||||
color: var(--accent-hover);
|
||||
border-color: var(--accent-hover);
|
||||
}
|
||||
.btn-ghost:active { background: rgba(240, 240, 250, 0.24); }
|
||||
/* Solid spectral — used sparingly for the highest-priority CTA */
|
||||
.btn-solid {
|
||||
background: var(--accent);
|
||||
color: var(--accent-on);
|
||||
border: 1px solid var(--accent);
|
||||
}
|
||||
.btn-solid:hover { background: var(--accent-hover); border-color: var(--accent-hover); }
|
||||
.btn-solid:active { background: var(--accent-active); border-color: var(--accent-active); }
|
||||
|
||||
/* ─── Inputs — spectral hairline only, no surface fill ──── */
|
||||
.field { display: flex; flex-direction: column; gap: var(--space-2); }
|
||||
.field label {
|
||||
font-size: var(--text-xs);
|
||||
font-weight: 700;
|
||||
letter-spacing: 1px;
|
||||
text-transform: uppercase;
|
||||
color: var(--muted);
|
||||
}
|
||||
.field input {
|
||||
padding: 14px var(--space-3);
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid var(--border);
|
||||
background: transparent;
|
||||
color: var(--fg);
|
||||
font-family: var(--font-body);
|
||||
font-size: var(--text-base);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.02em;
|
||||
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-hover);
|
||||
box-shadow: var(--focus-ring);
|
||||
}
|
||||
.field input::placeholder {
|
||||
color: var(--muted);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.02em;
|
||||
}
|
||||
.field-help {
|
||||
font-size: var(--text-xs);
|
||||
font-weight: 400;
|
||||
letter-spacing: 1px;
|
||||
text-transform: uppercase;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
/* ─── Hero — full-viewport cinematic scene ──────────────
|
||||
* DESIGN.md §1: each section is a film frame. No external
|
||||
* image lives in the fixture; we evoke the cosmic black
|
||||
* sky / planetary curve with layered radial gradients +
|
||||
* the canonical rgba(0,0,0,0.5) legibility overlay. */
|
||||
.hero {
|
||||
position: relative;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
padding-block: var(--section-y-desktop);
|
||||
background:
|
||||
linear-gradient(180deg, rgba(0, 0, 0, 0.2) 0%, rgba(0, 0, 0, 0.5) 100%),
|
||||
radial-gradient(2px 2px at 12% 22%, rgba(240, 240, 250, 0.85), transparent 60%),
|
||||
radial-gradient(1px 1px at 28% 64%, rgba(240, 240, 250, 0.7), transparent 60%),
|
||||
radial-gradient(2px 2px at 47% 18%, rgba(240, 240, 250, 0.6), transparent 60%),
|
||||
radial-gradient(1px 1px at 63% 41%, rgba(240, 240, 250, 0.85), transparent 60%),
|
||||
radial-gradient(1.5px 1.5px at 78% 12%, rgba(240, 240, 250, 0.5), transparent 60%),
|
||||
radial-gradient(1px 1px at 88% 73%, rgba(240, 240, 250, 0.6), transparent 60%),
|
||||
radial-gradient(1px 1px at 38% 88%, rgba(240, 240, 250, 0.4), transparent 60%),
|
||||
radial-gradient(2px 2px at 6% 84%, rgba(240, 240, 250, 0.5), transparent 60%),
|
||||
radial-gradient(ellipse 120% 70% at 50% 100%, rgba(36, 32, 64, 0.85) 0%, rgba(0, 0, 0, 0) 70%),
|
||||
radial-gradient(ellipse 90% 60% at 80% 18%, rgba(80, 60, 110, 0.25) 0%, rgba(0, 0, 0, 0) 65%),
|
||||
#000000;
|
||||
}
|
||||
.hero-inner { max-width: 720px; }
|
||||
.hero h1 {
|
||||
font-size: var(--text-4xl);
|
||||
letter-spacing: var(--tracking-display);
|
||||
}
|
||||
.hero .lead { max-width: 56ch; }
|
||||
.hero-actions { display: flex; gap: var(--space-4); margin-block-start: var(--space-8); flex-wrap: wrap; }
|
||||
|
||||
/* Mission status strip — uppercase, hairline-divided, no card */
|
||||
.mission-strip {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 0;
|
||||
margin-block-start: var(--space-12);
|
||||
border-top: 1px solid var(--border-soft);
|
||||
border-bottom: 1px solid var(--border-soft);
|
||||
}
|
||||
.mission-strip > * {
|
||||
padding: var(--space-5) var(--space-4);
|
||||
}
|
||||
.mission-strip > * + * { border-left: 1px solid var(--border-soft); }
|
||||
@media (max-width: 1023px) {
|
||||
.mission-strip { grid-template-columns: 1fr 1fr; }
|
||||
.mission-strip > *:nth-child(2n+1) { border-left: none; }
|
||||
}
|
||||
@media (max-width: 639px) {
|
||||
.mission-strip { grid-template-columns: 1fr; }
|
||||
.mission-strip > * + * { border-left: none; border-top: 1px solid var(--border-soft); }
|
||||
}
|
||||
.mission-strip dt { color: var(--muted); margin: 0; }
|
||||
.mission-strip dd {
|
||||
margin: var(--space-2) 0 0;
|
||||
font-family: var(--font-display);
|
||||
font-size: var(--text-xl);
|
||||
font-weight: 700;
|
||||
line-height: var(--leading-tight);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.02em;
|
||||
color: var(--fg);
|
||||
}
|
||||
|
||||
/* ─── Features — text-on-void, no cards ─────────────────
|
||||
* DESIGN.md §4: no panels, no containers, no cards. Three
|
||||
* vertical text blocks sit directly on the void, separated
|
||||
* only by hairline ghost dividers. */
|
||||
.features-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 0;
|
||||
border-top: 1px solid var(--border-soft);
|
||||
}
|
||||
.features-grid > article {
|
||||
padding: var(--space-12) var(--space-6) var(--space-12) 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-4);
|
||||
}
|
||||
.features-grid > article + article {
|
||||
border-left: 1px solid var(--border-soft);
|
||||
padding-inline-start: var(--space-6);
|
||||
}
|
||||
@media (max-width: 1023px) {
|
||||
.features-grid { grid-template-columns: 1fr; }
|
||||
.features-grid > article + article {
|
||||
border-left: none;
|
||||
border-top: 1px solid var(--border-soft);
|
||||
padding-inline-start: 0;
|
||||
}
|
||||
}
|
||||
.feature-index {
|
||||
font-family: var(--font-display);
|
||||
font-size: var(--text-sm);
|
||||
font-weight: 700;
|
||||
letter-spacing: 1.17px;
|
||||
text-transform: uppercase;
|
||||
color: var(--muted);
|
||||
}
|
||||
.feature h3 {
|
||||
font-size: var(--text-xl);
|
||||
line-height: var(--leading-tight);
|
||||
letter-spacing: var(--tracking-display);
|
||||
}
|
||||
.feature p { color: var(--muted); }
|
||||
.feature a {
|
||||
color: var(--fg);
|
||||
font-size: var(--text-sm);
|
||||
font-weight: 700;
|
||||
letter-spacing: 1.17px;
|
||||
text-transform: uppercase;
|
||||
text-decoration: none;
|
||||
border-bottom: 1px solid var(--border);
|
||||
padding-block-end: 4px;
|
||||
align-self: flex-start;
|
||||
transition: color var(--motion-fast) var(--ease-standard),
|
||||
border-color var(--motion-fast) var(--ease-standard);
|
||||
}
|
||||
.feature a:hover { color: var(--accent-hover); border-color: var(--accent-hover); }
|
||||
|
||||
/* ─── Form — mission briefing, spectral hairlines ──────── */
|
||||
.form-row {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: var(--space-12);
|
||||
align-items: start;
|
||||
}
|
||||
@media (max-width: 1023px) { .form-row { grid-template-columns: 1fr; gap: var(--space-8); } }
|
||||
.form { display: flex; flex-direction: column; gap: var(--space-5); max-width: 480px; }
|
||||
.form-actions { display: flex; gap: var(--space-3); margin-block-start: var(--space-3); flex-wrap: wrap; }
|
||||
.icon { width: 14px; height: 14px; flex-shrink: 0; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main class="container">
|
||||
<section class="hero" data-od-id="hero">
|
||||
<div class="hero-inner stack-6">
|
||||
<p class="caption-bold">SpaceX · Mission 2026</p>
|
||||
<h1>Making life multi‑planetary.</h1>
|
||||
<p class="lead">
|
||||
From low‑Earth orbit to the surface of Mars — we build the
|
||||
rockets, the spacecraft, and the operating cadence that turn
|
||||
humanity into a multi‑planet species.
|
||||
</p>
|
||||
<div class="hero-actions">
|
||||
<a href="./tokens.css" class="btn btn-ghost">
|
||||
Learn more
|
||||
<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-ghost">View the spec</a>
|
||||
</div>
|
||||
|
||||
<dl class="mission-strip" aria-label="Mission counters">
|
||||
<div>
|
||||
<dt class="eyebrow">Total Launches</dt>
|
||||
<dd>438</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="eyebrow">Total Landings</dt>
|
||||
<dd>406</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="eyebrow">Reflights</dt>
|
||||
<dd>377</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="eyebrow">Status</dt>
|
||||
<dd>Nominal</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section data-od-id="features">
|
||||
<div class="stack-4">
|
||||
<p class="eyebrow">What this fixture exercises</p>
|
||||
<h2 style="max-width: 24ch">A type system for engineering on the edge of the atmosphere.</h2>
|
||||
<p class="body-muted" style="max-width: 60ch; text-transform: uppercase; letter-spacing: 0.02em;">
|
||||
Three principles, no cards, no shadows — text sits directly on the void.
|
||||
</p>
|
||||
</div>
|
||||
<div class="features-grid" style="margin-block-start: var(--space-12)">
|
||||
<article class="feature stack-4">
|
||||
<p class="feature-index">01 / Atmosphere</p>
|
||||
<h3>Pure black canvas, full‑viewport imagery.</h3>
|
||||
<p>
|
||||
--bg (#000000) is the void. There is no card surface, no warm
|
||||
tier — every section is text on photograph, every photograph
|
||||
is one cinematic frame.
|
||||
</p>
|
||||
<a href="./tokens.css">Inspect surface tokens</a>
|
||||
</article>
|
||||
<article class="feature stack-4">
|
||||
<p class="feature-index">02 / Voice</p>
|
||||
<h3>Universal uppercase, positive tracking.</h3>
|
||||
<p>
|
||||
D‑DIN at 0.02em (= 0.96px at 48px). Every label, every
|
||||
counter, every CTA is stenciled like a serial number on a
|
||||
spacecraft hull.
|
||||
</p>
|
||||
<a href="./DESIGN.md">Read the type rules</a>
|
||||
</article>
|
||||
<article class="feature stack-4">
|
||||
<p class="feature-index">03 / Restraint</p>
|
||||
<h3>Zero shadows, one ghost button.</h3>
|
||||
<p>
|
||||
--elev-raised resolves to <code style="font-family: var(--font-mono); font-size: var(--text-sm);">none</code>.
|
||||
Depth comes from the photography. The lone ghost CTA at 32px
|
||||
radius is the entire interactive surface area.
|
||||
</p>
|
||||
<a href="./tokens.css">Inspect elevation</a>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section data-od-id="form">
|
||||
<div class="form-row">
|
||||
<div class="stack-6">
|
||||
<p class="eyebrow">Mission Briefing</p>
|
||||
<h2>Receive launch and re‑entry telemetry.</h2>
|
||||
<p class="body-muted" style="text-transform: uppercase; letter-spacing: 0.02em; max-width: 48ch;">
|
||||
We’ll send pre‑flight readiness reviews and
|
||||
post‑landing recovery summaries. Hairline borders only —
|
||||
no surface fills, no shadows, no cool grays.
|
||||
</p>
|
||||
</div>
|
||||
<form class="form" onsubmit="event.preventDefault();">
|
||||
<div class="field">
|
||||
<label for="callsign">Callsign</label>
|
||||
<input id="callsign" type="text" placeholder="Mission control" autocomplete="username" required />
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="email">Operator email</label>
|
||||
<input id="email" type="email" placeholder="ops@spacex.com" autocomplete="email" required />
|
||||
<p class="field-help">For mission summaries only.</p>
|
||||
</div>
|
||||
<div class="form-actions">
|
||||
<button type="submit" class="btn btn-ghost">Confirm uplink</button>
|
||||
<button type="button" class="btn btn-ghost">Stand down</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
154
design-systems/spacex/tokens.css
Normal file
154
design-systems/spacex/tokens.css
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
/* ─────────────────────────────────────────────────────────────────────────
|
||||
* design-systems/spacex/tokens.css
|
||||
*
|
||||
* Structured token bindings for "Design System Inspired by SpaceX".
|
||||
* Cinematic aerospace minimalism: pure black canvas, full-viewport
|
||||
* photography, D-DIN industrial type, universal uppercase + positive
|
||||
* tracking, zero shadows / cards / decoration. The interface disappears
|
||||
* behind the imagery — only type, photography, and a single ghost button.
|
||||
*
|
||||
* Key brand decisions encoded here:
|
||||
* - Pure Space Black (#000000) — the void of space; never a soft black
|
||||
* - Spectral White (#f0f0fa) — slight blue-violet tint that mimics starlight
|
||||
* - --surface aliases to --bg — SpaceX has NO cards, panels, or containers
|
||||
* - Ghost border (rgba(240,240,250,0.35)) — only visible interactive edge
|
||||
* - D-DIN industrial typeface — DIN heritage, stenciled aerospace voice
|
||||
* - Display tracking 0.02em (= 0.96px at 48px) — DESIGN.md §3 verbatim
|
||||
* - Tight leading 1.0 — compressed mission-briefing rhythm
|
||||
* - --elev-raised: none — zero shadows; depth comes from photography
|
||||
* - --radius-pill: 32px — the ghost button is the SOLE rounded element
|
||||
* ─────────────────────────────────────────────────────────────────── */
|
||||
|
||||
:root {
|
||||
/* ─── Surface ─────────────────────────────────────────────────────
|
||||
* The void of space. There is no card tier, no warm tier — every
|
||||
* SpaceX section is text-on-photograph, not text-on-surface. We
|
||||
* still declare --surface so cross-brand components that paint a
|
||||
* card still resolve, but it deliberately matches --bg. */
|
||||
--bg: #000000; /* Space Black — pure void, the foundation canvas */
|
||||
--surface: #000000; /* No card surfaces — content sits directly on the void */
|
||||
--surface-warm: var(--surface); /* alias — no warm tier in an achromatic system */
|
||||
|
||||
/* ─── Foreground ──────────────────────────────────────────────────
|
||||
* Spectral White, never pure white. The slight blue-violet tint
|
||||
* mimics starlight on a spacecraft hull and prevents the clinical
|
||||
* feel of #ffffff against pure black. */
|
||||
--fg: #f0f0fa; /* Spectral White — primary text, mission-critical signal */
|
||||
--fg-2: var(--fg); /* alias — single text weight throughout the system */
|
||||
--muted: rgba(240, 240, 250, 0.7); /* Dimmed spectral — captions, secondary labels */
|
||||
--meta: var(--muted); /* alias — tertiary FG identical to muted on dark canvas */
|
||||
|
||||
/* ─── Border ──────────────────────────────────────────────────────
|
||||
* The Ghost Border (35% spectral) is the only edge that ever
|
||||
* appears — on the lone ghost CTA button. Inner separators borrow
|
||||
* the 10% spectral surface tint so they barely register. */
|
||||
--border: rgba(240, 240, 250, 0.35); /* Ghost Border — DESIGN.md §2 verbatim */
|
||||
--border-soft: rgba(240, 240, 250, 0.1); /* Inner separators / barely-visible dividers */
|
||||
|
||||
/* ─── Accent ──────────────────────────────────────────────────────
|
||||
* SpaceX has no chromatic accent — Spectral White IS the accent.
|
||||
* Hover lifts to pure white (Hover White / --white-100 in DESIGN.md);
|
||||
* active gently dims. The full ghost-button affordance is
|
||||
* implemented in components, not in the accent token. */
|
||||
--accent: #f0f0fa; /* Spectral White stands in for any brand accent */
|
||||
--accent-on: #000000; /* Black text when accent is the bg */
|
||||
--accent-hover: #ffffff; /* Hover White — pure white per DESIGN.md §2 */
|
||||
--accent-active: #d8d8e6; /* Dimmed spectral — pressed state */
|
||||
|
||||
/* ─── Semantic ────────────────────────────────────────────────────
|
||||
* Schema defaults preserved. SpaceX's homepage is purely
|
||||
* presentational and almost never surfaces semantic state, but the
|
||||
* tokens must resolve when shared components reference them. */
|
||||
--success: #16a34a;
|
||||
--warn: #eab308;
|
||||
--danger: #dc2626;
|
||||
|
||||
/* ─── Typography ──────────────────────────────────────────────────
|
||||
* D-DIN — industrial geometric, German DIN heritage. Two weights
|
||||
* only: 700 (Bold) for display + nav emphasis, 400 for body.
|
||||
* Fallbacks per DESIGN.md §3: Arial, Verdana. */
|
||||
--font-display: "D-DIN-Bold", "D-DIN", "DIN Alternate", "Helvetica Neue", Arial, Verdana, sans-serif;
|
||||
--font-body: "D-DIN", "DIN Alternate", "Helvetica Neue", Arial, Verdana, sans-serif;
|
||||
--font-mono: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Monaco, Consolas, monospace;
|
||||
|
||||
/* Type scale — DESIGN.md §3 anchors: 10px (Micro), 12px (Caption /
|
||||
* Nav), 16px (Body), 48px (Display Hero). Intermediate tiers
|
||||
* (20/24/32/40) are interpolated for the few moments a SpaceX-style
|
||||
* layout needs them; the actual brand uses very few sizes. */
|
||||
--text-xs: 10px; /* Micro — uppercase ghost labels */
|
||||
--text-sm: 12px; /* Caption / Nav Link — uppercase */
|
||||
--text-base: 16px; /* Body — standard reading text */
|
||||
--text-lg: 20px;
|
||||
--text-xl: 24px;
|
||||
--text-2xl: 32px;
|
||||
--text-3xl: 40px;
|
||||
--text-4xl: 48px; /* Display Hero — D-DIN-Bold uppercase, "MISSION" weight */
|
||||
|
||||
--leading-body: 1.5; /* DESIGN.md says 1.5–1.7 for reading body */
|
||||
--leading-tight: 1.0; /* Display / mission-briefing rhythm */
|
||||
--tracking-display: 0.02em; /* = 0.96px at 48px — DESIGN.md §3 hero spec verbatim */
|
||||
|
||||
/* ─── Spacing ─────────────────────────────────────────────────────
|
||||
* 8px base unit per DESIGN.md §5. Schema-default scale preserved —
|
||||
* SpaceX's irregular published scale (3/5/12/15/18/20/24/30) is a
|
||||
* brand-specific rhythm; cross-brand components paint at 4/8/12/16. */
|
||||
--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 rhythm ──────────────────────────────────────────────
|
||||
* DESIGN.md §5: each section is one full viewport. We can't bind
|
||||
* "100vh" to padding (it would collapse on short content), so
|
||||
* section-y is generous to evoke the cinematic single-viewport
|
||||
* rhythm in fixture renderings. */
|
||||
--section-y-desktop: 120px;
|
||||
--section-y-tablet: 80px;
|
||||
--section-y-phone: 56px;
|
||||
|
||||
/* ─── Radius ──────────────────────────────────────────────────────
|
||||
* Sharp 4px on dividers / utility — DESIGN.md §5 "Sharp (4px)".
|
||||
* --radius-pill rebound to 32px because SpaceX's ghost button IS
|
||||
* the only rounded element in the system, and "pill" semantically
|
||||
* maps to that lone interactive surface. */
|
||||
--radius-sm: 4px;
|
||||
--radius-md: 4px; /* No medium tier exists — same as sm */
|
||||
--radius-lg: 4px; /* No large tier exists — same as sm */
|
||||
--radius-pill: 32px; /* Ghost button radius — the only rounded element */
|
||||
|
||||
/* ─── Elevation (ZERO shadows) ────────────────────────────────────
|
||||
* DESIGN.md §6: "SpaceX uses ZERO shadows." Every surface is
|
||||
* already a photograph with natural lighting; CSS shadows would
|
||||
* compete with the cinematic content. --elev-raised resolves to
|
||||
* `none` so any shared component requesting a raised surface
|
||||
* silently flattens instead of painting a synthetic drop shadow. */
|
||||
--elev-flat: none;
|
||||
--elev-ring: 0 0 0 1px var(--border); /* Hairline spectral ring — ghost-button edge */
|
||||
--elev-raised: none; /* Zero shadows — photography carries depth */
|
||||
|
||||
/* ─── Focus ring ──────────────────────────────────────────────────
|
||||
* Solid 2px spectral ring — high contrast on black canvas. No
|
||||
* blurred glow (consistent with the zero-shadow philosophy). */
|
||||
--focus-ring: 0 0 0 2px var(--accent);
|
||||
|
||||
/* ─── Motion ──────────────────────────────────────────────────────
|
||||
* Standard schema durations. SpaceX's drama is in the photography
|
||||
* and type, not in animated state changes. */
|
||||
--motion-fast: 150ms;
|
||||
--motion-base: 200ms;
|
||||
--ease-standard: cubic-bezier(0.2, 0, 0, 1);
|
||||
|
||||
/* ─── Layout ──────────────────────────────────────────────────────
|
||||
* 1440px container — wide-format layouts honour the cinematic
|
||||
* full-bleed feel without letting hero copy stretch beyond
|
||||
* readable on ultra-wide displays. Generous gutters keep the
|
||||
* "edge-to-edge imagery" feel even at smaller breakpoints. */
|
||||
--container-max: 1440px;
|
||||
--container-gutter-desktop: 24px;
|
||||
--container-gutter-tablet: 20px;
|
||||
--container-gutter-phone: 18px;
|
||||
}
|
||||
493
design-systems/starbucks/components.html
Normal file
493
design-systems/starbucks/components.html
Normal file
|
|
@ -0,0 +1,493 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Starbucks — reference components</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="Reference fixture for design-systems/starbucks. Warm cream canvas,
|
||||
four-tier green system, full-pill buttons, whisper-soft layered shadows,
|
||||
and SoDoSans with universal -0.01em tracking."
|
||||
/>
|
||||
|
||||
<style>
|
||||
:root {
|
||||
--bg: #f2f0eb;
|
||||
--surface: #ffffff;
|
||||
--surface-warm: #edebe9;
|
||||
|
||||
--fg: rgba(0, 0, 0, 0.87);
|
||||
--fg-2: #33433d;
|
||||
--muted: rgba(0, 0, 0, 0.58);
|
||||
--meta: var(--muted);
|
||||
|
||||
--border: #d6dbde;
|
||||
--border-soft: #e7e7e7;
|
||||
|
||||
--accent: #00754A;
|
||||
--accent-on: #ffffff;
|
||||
--accent-hover: color-mix(in oklab, var(--accent), black 8%);
|
||||
--accent-active: color-mix(in oklab, var(--accent), black 14%);
|
||||
|
||||
--success: #16a34a;
|
||||
--warn: #fbbc05;
|
||||
--danger: #c82014;
|
||||
|
||||
--font-display: SoDoSans, "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
--font-body: SoDoSans, "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
--font-mono: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Monaco, Consolas, monospace;
|
||||
|
||||
--text-xs: 13px;
|
||||
--text-sm: 14px;
|
||||
--text-base: 16px;
|
||||
--text-lg: 19px;
|
||||
--text-xl: 24px;
|
||||
--text-2xl: 32px;
|
||||
--text-3xl: 45px;
|
||||
--text-4xl: 58px;
|
||||
|
||||
--leading-body: 1.5;
|
||||
--leading-tight: 1.2;
|
||||
--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: 64px;
|
||||
--section-y-tablet: 48px;
|
||||
--section-y-phone: 32px;
|
||||
|
||||
--radius-sm: 4px;
|
||||
--radius-md: 12px;
|
||||
--radius-lg: 16px;
|
||||
--radius-pill: 9999px;
|
||||
|
||||
--elev-flat: none;
|
||||
--elev-ring: 0 0 0 1px var(--border);
|
||||
--elev-raised: 0 0 0.5px 0 rgba(0, 0, 0, 0.14), 0 1px 1px 0 rgba(0, 0, 0, 0.24);
|
||||
|
||||
--focus-ring: 0 0 0 3px color-mix(in oklab, var(--accent), transparent 70%);
|
||||
|
||||
--motion-fast: 150ms;
|
||||
--motion-base: 200ms;
|
||||
--ease-standard: cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
|
||||
--container-max: 1440px;
|
||||
--container-gutter-desktop: 40px;
|
||||
--container-gutter-tablet: 24px;
|
||||
--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);
|
||||
letter-spacing: var(--tracking-display);
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
/* ─── Layout ────────────────────────────────────────────── */
|
||||
.container {
|
||||
max-width: var(--container-max);
|
||||
margin-inline: auto;
|
||||
padding-inline: var(--container-gutter-desktop);
|
||||
}
|
||||
section { padding-block: var(--section-y-desktop); }
|
||||
@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 ─────────────────────────────────────────
|
||||
* Universal -0.01em tracking; hierarchy carried by weight + color,
|
||||
* not by size jumps. H1 in Starbucks Green; H2 stays in Text Black. */
|
||||
h1, h2, h3 {
|
||||
font-family: var(--font-display);
|
||||
line-height: var(--leading-tight);
|
||||
letter-spacing: var(--tracking-display);
|
||||
margin: 0;
|
||||
}
|
||||
h1 {
|
||||
font-size: var(--text-3xl);
|
||||
font-weight: 600;
|
||||
color: #006241;
|
||||
}
|
||||
h2 {
|
||||
font-size: var(--text-xl);
|
||||
font-weight: 400;
|
||||
color: var(--fg);
|
||||
}
|
||||
h3 {
|
||||
font-size: var(--text-lg);
|
||||
font-weight: 600;
|
||||
color: var(--fg);
|
||||
}
|
||||
p { margin: 0; }
|
||||
.lead {
|
||||
font-size: var(--text-lg);
|
||||
color: var(--fg);
|
||||
line-height: var(--leading-body);
|
||||
}
|
||||
.body-muted { color: var(--muted); }
|
||||
.body-sm { font-size: var(--text-sm); }
|
||||
.eyebrow {
|
||||
font-size: var(--text-xs);
|
||||
color: var(--muted);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.15em;
|
||||
font-weight: 600;
|
||||
}
|
||||
.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 — universal 9999px pill, scale(0.95) press ── */
|
||||
.btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--space-2);
|
||||
padding: 14px 28px;
|
||||
border-radius: var(--radius-pill);
|
||||
font-family: var(--font-body);
|
||||
font-size: var(--text-base);
|
||||
font-weight: 600;
|
||||
line-height: 1.2;
|
||||
letter-spacing: var(--tracking-display);
|
||||
cursor: pointer;
|
||||
border: 1px solid transparent;
|
||||
transition: background-color var(--motion-base) var(--ease-standard),
|
||||
color var(--motion-base) var(--ease-standard),
|
||||
border-color var(--motion-base) var(--ease-standard),
|
||||
transform var(--motion-fast) var(--ease-standard);
|
||||
text-decoration: none;
|
||||
}
|
||||
.btn:active { transform: scale(0.95); }
|
||||
.btn:focus-visible { outline: none; box-shadow: var(--focus-ring); }
|
||||
|
||||
/* Primary Filled — Green Accent fill */
|
||||
.btn-primary {
|
||||
background: var(--accent);
|
||||
color: var(--accent-on);
|
||||
border-color: var(--accent);
|
||||
}
|
||||
.btn-primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); }
|
||||
|
||||
/* Primary Outlined — transparent / Green Accent stroke */
|
||||
.btn-secondary {
|
||||
background: transparent;
|
||||
color: var(--accent);
|
||||
border-color: var(--accent);
|
||||
}
|
||||
.btn-secondary:hover {
|
||||
background: color-mix(in oklab, var(--accent), transparent 92%);
|
||||
}
|
||||
|
||||
/* Dark Outlined — Text Black stroke (top-nav "Sign in") */
|
||||
.btn-ghost {
|
||||
background: transparent;
|
||||
color: var(--fg);
|
||||
border-color: var(--fg);
|
||||
padding: 7px 16px;
|
||||
font-size: var(--text-sm);
|
||||
}
|
||||
.btn-ghost:hover { background: rgba(0, 0, 0, 0.04); }
|
||||
|
||||
/* ─── Inputs — outlined-rectangle with --radius-sm ──────── */
|
||||
.field { display: flex; flex-direction: column; gap: var(--space-2); }
|
||||
.field label {
|
||||
font-size: var(--text-sm);
|
||||
font-weight: 600;
|
||||
color: var(--fg);
|
||||
}
|
||||
.field input {
|
||||
padding: 12px var(--space-4);
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid var(--border);
|
||||
background: var(--surface);
|
||||
color: var(--fg);
|
||||
font-family: var(--font-body);
|
||||
font-size: var(--text-base);
|
||||
letter-spacing: var(--tracking-display);
|
||||
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(--muted); }
|
||||
|
||||
/* ─── Cards — whisper-soft dual shadow, --radius-md ─────── */
|
||||
.card {
|
||||
background: var(--surface);
|
||||
border-radius: var(--radius-md);
|
||||
padding: var(--space-6);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-3);
|
||||
box-shadow: var(--elev-raised);
|
||||
}
|
||||
.card-eyebrow {
|
||||
font-size: var(--text-xs);
|
||||
color: var(--accent);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.15em;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
/* ─── Badges ────────────────────────────────────────────── */
|
||||
.badge {
|
||||
display: inline-flex; align-items: center; gap: var(--space-2);
|
||||
padding: 4px var(--space-3);
|
||||
border-radius: var(--radius-pill);
|
||||
font-size: var(--text-xs); font-weight: 600; line-height: 1.4;
|
||||
}
|
||||
.badge-success { color: var(--success); background: color-mix(in oklab, var(--success), transparent 88%); }
|
||||
.badge-muted { color: var(--muted); background: var(--surface-warm); }
|
||||
.badge-dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
|
||||
|
||||
/* Rewards Cost Pill — gold outline on transparent */
|
||||
.badge-rewards {
|
||||
color: #cba258;
|
||||
background: transparent;
|
||||
border: 1px solid #cba258;
|
||||
letter-spacing: 0.05em;
|
||||
}
|
||||
|
||||
/* ─── 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 / Layout helpers ─────────────────────────────── */
|
||||
.hero {
|
||||
background: var(--bg);
|
||||
padding-block: var(--section-y-desktop);
|
||||
}
|
||||
.hero-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1.4fr 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-6);
|
||||
background: var(--surface);
|
||||
border-radius: var(--radius-md);
|
||||
box-shadow: var(--elev-raised);
|
||||
}
|
||||
|
||||
.features-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: var(--space-6);
|
||||
}
|
||||
@media (max-width: 1023px) { .features-grid { grid-template-columns: 1fr 1fr; } }
|
||||
@media (max-width: 639px) { .features-grid { grid-template-columns: 1fr; } }
|
||||
|
||||
.form-row {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 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: 440px; }
|
||||
.form-actions { display: flex; gap: var(--space-3); margin-block-start: var(--space-2); flex-wrap: wrap; }
|
||||
.icon { width: 16px; height: 16px; flex-shrink: 0; }
|
||||
.row-between { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); }
|
||||
|
||||
/* Feature band — House Green band with white text on dark surface */
|
||||
.band-dark {
|
||||
background: #1E3932;
|
||||
border-radius: var(--radius-md);
|
||||
padding: var(--space-12) var(--space-8);
|
||||
color: #ffffff;
|
||||
}
|
||||
.band-dark h2 { color: #ffffff; }
|
||||
.band-dark .lead { color: rgba(255, 255, 255, 0.70); }
|
||||
.band-dark .btn-primary {
|
||||
background: #ffffff;
|
||||
color: var(--accent);
|
||||
border-color: #ffffff;
|
||||
}
|
||||
.band-dark .btn-primary:hover { background: rgba(255, 255, 255, 0.92); border-color: rgba(255, 255, 255, 0.92); }
|
||||
.band-dark .btn-secondary {
|
||||
background: transparent;
|
||||
color: #ffffff;
|
||||
border-color: #ffffff;
|
||||
}
|
||||
.band-dark .btn-secondary:hover { background: rgba(255, 255, 255, 0.12); }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main class="container">
|
||||
<section class="hero" data-od-id="hero">
|
||||
<div class="hero-grid">
|
||||
<div class="stack-4">
|
||||
<p class="eyebrow">Reference fixture · starbucks</p>
|
||||
<h1>More than coffee — your third place, on the house.</h1>
|
||||
<p class="lead" style="max-width: 56ch">
|
||||
Warm cream canvas, four greens with assigned roles, and the friendly
|
||||
confidence of SoDoSans. Open the app, find a store, or sit awhile —
|
||||
every surface is built to feel like the café itself.
|
||||
</p>
|
||||
<div class="hero-actions">
|
||||
<a href="./tokens.css" class="btn btn-primary">
|
||||
Order now
|
||||
<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">Join Rewards</a>
|
||||
<a href="#" class="btn btn-ghost">Find a store</a>
|
||||
</div>
|
||||
</div>
|
||||
<aside class="hero-meta" aria-label="Store status">
|
||||
<div class="row-between">
|
||||
<span class="body-sm">Nearest store</span>
|
||||
<span class="badge badge-success">
|
||||
<span class="badge-dot" aria-hidden="true"></span>
|
||||
Open · 1.2 mi
|
||||
</span>
|
||||
</div>
|
||||
<p class="body-sm" style="color: var(--muted)">
|
||||
Pike Place Roast brewing now · pickup in 6 min
|
||||
</p>
|
||||
<span class="badge badge-rewards" style="align-self:flex-start">200★ free drink</span>
|
||||
<p class="body-sm" style="color: var(--muted)">
|
||||
Press <kbd>⌘</kbd> <kbd>K</kbd> to reorder a favorite.
|
||||
</p>
|
||||
</aside>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section data-od-id="features">
|
||||
<div class="stack-3">
|
||||
<p class="eyebrow">A system for the café</p>
|
||||
<h2 style="max-width: 28ch">Warmth, mapped to a role — never a single brand green.</h2>
|
||||
</div>
|
||||
<div class="features-grid" style="margin-block-start: var(--space-8)">
|
||||
<article class="card">
|
||||
<p class="card-eyebrow">Four-tier green</p>
|
||||
<h3>Starbucks · Accent · House · Uplift</h3>
|
||||
<p class="body-muted body-sm">
|
||||
Each green is mapped to a surface role: Starbucks Green for headings,
|
||||
Green Accent (#00754A) for CTAs and the Frap, House Green for deep
|
||||
feature bands and the footer, Uplift for decorative accents.
|
||||
</p>
|
||||
<a href="./tokens.css" class="body-sm">Inspect tokens →</a>
|
||||
</article>
|
||||
<article class="card">
|
||||
<p class="card-eyebrow">Warm cream canvas</p>
|
||||
<h3>Never pure white.</h3>
|
||||
<p class="body-muted body-sm">
|
||||
Neutral Warm (#f2f0eb) and Ceramic (#edebe9) reference café paper,
|
||||
wood, and ceramic mugs. White is reserved for the card surface so
|
||||
content reads like a mug placed on the table.
|
||||
</p>
|
||||
<a href="./DESIGN.md" class="body-sm">Read the rule →</a>
|
||||
</article>
|
||||
<article class="card">
|
||||
<p class="card-eyebrow">Pill + press</p>
|
||||
<h3>Full-pill buttons, scale(0.95) press.</h3>
|
||||
<p class="body-muted body-sm">
|
||||
Every button is a 9999px pill. The active-press tactile rebound
|
||||
uses transform: scale(0.95) — a signature micro-interaction across
|
||||
every CTA, including the floating Frap order button.
|
||||
</p>
|
||||
<a href="./tokens.css" class="body-sm">Inspect motion →</a>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section data-od-id="band">
|
||||
<div class="band-dark">
|
||||
<div class="hero-grid">
|
||||
<div class="stack-4">
|
||||
<p class="eyebrow" style="color: rgba(255,255,255,0.70)">Rewards</p>
|
||||
<h2 style="font-size: var(--text-2xl); font-weight: 600">
|
||||
Free coffee is just the beginning.
|
||||
</h2>
|
||||
<p class="lead">
|
||||
Earn a Star for every dollar — redeem for handcrafted drinks,
|
||||
food, and at-home favorites. Members also unlock birthday treats,
|
||||
free refills in-café, and exclusive offers.
|
||||
</p>
|
||||
<div class="hero-actions">
|
||||
<a href="#" class="btn btn-primary">Sign up for free</a>
|
||||
<a href="#" class="btn btn-secondary">Learn more</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stack-3" aria-label="Rewards summary">
|
||||
<p class="body-sm" style="color: rgba(255,255,255,0.70)">Your Stars</p>
|
||||
<p style="font-family: var(--font-display); font-size: var(--text-4xl); font-weight: 600; line-height: var(--leading-tight); letter-spacing: var(--tracking-display)">
|
||||
312<span style="color: #cba258">★</span>
|
||||
</p>
|
||||
<p class="body-sm" style="color: rgba(255,255,255,0.70)">
|
||||
88 more Stars to your next free handcrafted drink.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section data-od-id="form">
|
||||
<div class="form-row">
|
||||
<div class="stack-4">
|
||||
<p class="eyebrow">Join Rewards</p>
|
||||
<h2>Sit awhile — and earn a Star on the way out.</h2>
|
||||
<p class="body-muted" style="max-width: 48ch">
|
||||
No fees, no commitment. Save your favorite drink, skip the line
|
||||
with mobile order, and let the third place come to you.
|
||||
</p>
|
||||
</div>
|
||||
<form class="form" onsubmit="event.preventDefault();">
|
||||
<div class="field">
|
||||
<label for="email">Work email</label>
|
||||
<input id="email" type="email" placeholder="you@starbucks.com" autocomplete="email" required />
|
||||
<p class="field-help">We'll send your welcome Star and nothing else.</p>
|
||||
</div>
|
||||
<div class="form-actions">
|
||||
<button type="submit" class="btn btn-primary">Create account</button>
|
||||
<button type="button" class="btn btn-secondary">Browse menu</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
182
design-systems/starbucks/tokens.css
Normal file
182
design-systems/starbucks/tokens.css
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
/* ─────────────────────────────────────────────────────────────────────────
|
||||
* design-systems/starbucks/tokens.css
|
||||
*
|
||||
* Structured token bindings for "Design System Inspired by Starbucks".
|
||||
* Warm cream canvas, four-tier green system, full-pill buttons, whisper-
|
||||
* soft layered shadows, and SoDoSans with universal -0.01em tracking.
|
||||
*
|
||||
* Key brand decisions encoded here:
|
||||
* - Neutral Warm cream (#f2f0eb) as the page canvas — never pure white
|
||||
* - Green Accent (#00754A) reserved for the primary CTA fill / Frap
|
||||
* - Text Black at rgba(0, 0, 0, 0.87) to match the warm canvas temperature
|
||||
* - Whisper-soft dual-shadow card elevation — never one heavy drop
|
||||
* - Universal -0.01em tracking on SoDoSans across every surface
|
||||
* - 9999px pill radius — every button is a full pill, no exceptions
|
||||
* - Measured ease-out (0.25, 0.46, 0.45, 0.94) — the Starbucks expander
|
||||
*
|
||||
* No C-extensions: every token in this :root maps to a shared schema slot.
|
||||
* ─────────────────────────────────────────────────────────────────── */
|
||||
|
||||
:root {
|
||||
/* ─── Surface ─────────────────────────────────────────────────────
|
||||
* Warm cream canvas: the brand never sits on cold white. Neutral
|
||||
* Warm references café paper and store wood; Ceramic is the
|
||||
* slightly warmer/darker step used for zone separators and
|
||||
* partnership bands. White is reserved for the card surface so
|
||||
* content cards feel like ceramic mugs on a wood table. */
|
||||
--bg: #f2f0eb; /* Neutral Warm — primary page canvas */
|
||||
--surface: #ffffff; /* White — content card / modal surface */
|
||||
--surface-warm: #edebe9; /* Ceramic — zone separator, warmer tier */
|
||||
|
||||
/* ─── Foreground ──────────────────────────────────────────────────
|
||||
* Text Black is 87%-opacity black, never pure #000 — it reads
|
||||
* warmer on the cream canvas. Rewards Green (--fg-2) is the
|
||||
* dustier slate-green used only on Rewards-page text blocks: a
|
||||
* "reward surface" signal without reaching for Starbucks Green. */
|
||||
--fg: rgba(0, 0, 0, 0.87); /* Text Black — primary heading + body */
|
||||
--fg-2: #33433d; /* Rewards Green — dustier reading tier */
|
||||
--muted: rgba(0, 0, 0, 0.58); /* Text Black Soft — secondary / metadata */
|
||||
--meta: var(--muted); /* alias — single secondary text tier */
|
||||
|
||||
/* ─── Border ──────────────────────────────────────────────────────
|
||||
* Input Border (#d6dbde) is the explicit DESIGN.md value for the
|
||||
* outlined-rectangle Add-in / Milk / Customize fields. The soft
|
||||
* tier is the nutrition-table hairline — quiet enough to row-
|
||||
* separate without competing with the card edge. */
|
||||
--border: #d6dbde; /* Input Border — outlined rectangle edge */
|
||||
--border-soft: #e7e7e7; /* Nutrition-table row hairline */
|
||||
|
||||
/* ─── Accent ──────────────────────────────────────────────────────
|
||||
* Green Accent (#00754A) is the CTA green — the brighter, more
|
||||
* luminous tier in the four-tier green system. Reserved for the
|
||||
* primary filled pill CTAs and the Frap floating circular order
|
||||
* button. Starbucks Green (#006241) is the heading tier and stays
|
||||
* inline on h1; House Green (#1E3932) is the feature-band /
|
||||
* footer tier and stays inline at those surfaces. */
|
||||
--accent: #00754A; /* Green Accent — primary CTA / Frap fill */
|
||||
--accent-on: #ffffff;
|
||||
--accent-hover: color-mix(in oklab, var(--accent), black 8%);
|
||||
--accent-active: color-mix(in oklab, var(--accent), black 14%);
|
||||
|
||||
/* ─── Semantic ────────────────────────────────────────────────────
|
||||
* Red (#c82014) is the explicit DESIGN.md error / destructive
|
||||
* value. Yellow (#fbbc05) is the documented warning / legacy brand
|
||||
* touch. Success has no brand-specific value in DESIGN.md — keep
|
||||
* the schema default so it does not collide with the four-tier
|
||||
* green system. */
|
||||
--success: #16a34a;
|
||||
--warn: #fbbc05; /* Yellow — DESIGN.md warning state */
|
||||
--danger: #c82014; /* Red — DESIGN.md error / destructive */
|
||||
|
||||
/* ─── Typography — fonts ──────────────────────────────────────────
|
||||
* SoDoSans is Starbucks' proprietary corporate typeface (licensed
|
||||
* from House Industries; not publicly available). Display and body
|
||||
* share the same stack — the visual identity comes from the
|
||||
* universal -0.01em / -0.16px tracking, not from typeface mixing.
|
||||
* Lander Tall (Rewards serif) and Kalam (Careers script) are
|
||||
* context-specific and intentionally NOT promoted to schema
|
||||
* slots — they stay inline at their respective surfaces. */
|
||||
--font-display: SoDoSans, "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
--font-body: SoDoSans, "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
--font-mono: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Monaco, Consolas, monospace;
|
||||
|
||||
/* ─── Typography — type scale ─────────────────────────────────────
|
||||
* DESIGN.md anchors at 1rem = 10px and uses semantic textSize-1
|
||||
* through textSize-10. We bind the px values directly so the
|
||||
* pasted :root works without the 62.5% root font-size trick.
|
||||
* Hierarchy is carried by weight + color, not by size jumps —
|
||||
* H1 and H2 share 24px (--text-xl), only weight (600 vs 400) and
|
||||
* color (Starbucks-Green vs Text Black) separate them. */
|
||||
--text-xs: 13px; /* Micro / text-1 — caption */
|
||||
--text-sm: 14px; /* Small / text-2 — button, label */
|
||||
--text-base: 16px; /* Body / text-3 — default body */
|
||||
--text-lg: 19px; /* Body Large — hero intro copy */
|
||||
--text-xl: 24px; /* H1 / H2 — shared by weight, not size */
|
||||
--text-2xl: 32px; /* sub-heading bridge to hero */
|
||||
--text-3xl: 45px; /* Hero Large / text-8 */
|
||||
--text-4xl: 58px; /* Jumbo / text-9 — display ceiling */
|
||||
|
||||
/* ─── Typography — leading & tracking ─────────────────────────────
|
||||
* Body leading 1.5, display leading 1.2 — the schema-standard
|
||||
* two-mode rhythm. Tracking sits at -0.01em across the entire
|
||||
* system: SoDoSans reads slightly compressed, which gives it its
|
||||
* confident-but-friendly presence without feeling squeezed. */
|
||||
--leading-body: 1.5;
|
||||
--leading-tight: 1.2;
|
||||
--tracking-display: -0.01em; /* universal — DESIGN.md letterSpacingNormal */
|
||||
|
||||
/* ─── Spacing ─────────────────────────────────────────────────────
|
||||
* 4px grid. The brand's universal rhythm constant is 16px
|
||||
* (--space-4) — the default outer gutter, card padding baseline,
|
||||
* and body text size all share it. The off-grid Starbucks rem
|
||||
* scale (4 / 8 / 16 / 24 / 32 / 40 / 48 / 56 / 64) maps cleanly
|
||||
* to this schema with 12px and 20px filling the gaps. */
|
||||
--space-1: 4px;
|
||||
--space-2: 8px;
|
||||
--space-3: 12px;
|
||||
--space-4: 16px; /* universal rhythm — gutter, card pad, body */
|
||||
--space-5: 20px;
|
||||
--space-6: 24px;
|
||||
--space-8: 32px;
|
||||
--space-12: 48px;
|
||||
|
||||
/* ─── Section rhythm ──────────────────────────────────────────────
|
||||
* 64px desktop / 48px tablet / 32px phone — generous breathing so
|
||||
* the cream canvas can carry "plenty of space in the café". The
|
||||
* brand never uses divider lines between sections; whitespace
|
||||
* does the work. */
|
||||
--section-y-desktop: 64px;
|
||||
--section-y-tablet: 48px;
|
||||
--section-y-phone: 32px;
|
||||
|
||||
/* ─── Radius ──────────────────────────────────────────────────────
|
||||
* Two anchor values define the geometry: 12px on cards / modals
|
||||
* (--cardBorderRadius), 9999px on every button (--buttonBorderRadius
|
||||
* is 50px on the brand, but 9999px pills are visually identical at
|
||||
* any reasonable height). 4px is reserved for outlined-rectangle
|
||||
* inputs (Add-in / Milk / store availability). 16px stays as the
|
||||
* larger-container tier for hero-photo frames. */
|
||||
--radius-sm: 4px; /* outlined-rectangle inputs */
|
||||
--radius-md: 12px; /* cards, modals — --cardBorderRadius */
|
||||
--radius-lg: 16px; /* larger containers */
|
||||
--radius-pill: 9999px; /* full pill — universal button radius */
|
||||
|
||||
/* ─── Elevation ───────────────────────────────────────────────────
|
||||
* Whisper-soft layered shadows. The brand forbids a single heavy
|
||||
* drop shadow; instead it stacks 2–3 low-alpha shadows with
|
||||
* different offsets to simulate ambient + direct lighting. The
|
||||
* --elev-raised value here is the canonical Starbucks card
|
||||
* --cardBoxShadow (DESIGN.md §6). The Frap floating button gets
|
||||
* its own stacked shadow inline at the component level. */
|
||||
--elev-flat: none;
|
||||
--elev-ring: 0 0 0 1px var(--border);
|
||||
--elev-raised: 0 0 0.5px 0 rgba(0, 0, 0, 0.14), 0 1px 1px 0 rgba(0, 0, 0, 0.24);
|
||||
|
||||
/* ─── Focus ring ──────────────────────────────────────────────────
|
||||
* Green-tinted ring built from --accent. DESIGN.md does not
|
||||
* publish a brand-specific focus treatment, so we keep the
|
||||
* schema's accent-tinted formula — Green Accent reads cleanly
|
||||
* against the warm cream canvas and the white card surface. */
|
||||
--focus-ring: 0 0 0 3px color-mix(in oklab, var(--accent), transparent 70%);
|
||||
|
||||
/* ─── Motion ──────────────────────────────────────────────────────
|
||||
* 150ms for micro-states (the universal `transform: scale(0.95)`
|
||||
* pressed-button rebound), 200ms for general state changes
|
||||
* (DESIGN.md: `all 0.2s ease` on buttons). The standard easing
|
||||
* curve is the Starbucks Expander cubic-bezier from DESIGN.md
|
||||
* §4 — a measured ease-out rather than the schema's snappier
|
||||
* default, giving accordions and disclosures a calm café cadence. */
|
||||
--motion-fast: 150ms;
|
||||
--motion-base: 200ms;
|
||||
--ease-standard: cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
|
||||
/* ─── Layout ──────────────────────────────────────────────────────
|
||||
* Content caps at 1440px (--columnWidthXLarge per DESIGN.md §5).
|
||||
* Gutters scale progressively: 40px desktop (--outerGutterLarge),
|
||||
* 24px tablet (--outerGutterMedium), 16px phone (--outerGutter,
|
||||
* the universal rhythm constant). */
|
||||
--container-max: 1440px;
|
||||
--container-gutter-desktop: 40px;
|
||||
--container-gutter-tablet: 24px;
|
||||
--container-gutter-phone: 16px;
|
||||
}
|
||||
724
design-systems/tesla/components.html
Normal file
724
design-systems/tesla/components.html
Normal file
|
|
@ -0,0 +1,724 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Tesla — reference components</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="Reference fixture for design-systems/tesla. Radical
|
||||
subtraction: pure-white canvas, single Electric Blue accent,
|
||||
Universal Sans typography capped at 40px, 0.33s universal
|
||||
transitions, and viewport-scale whitespace as the dominant
|
||||
composition device."
|
||||
/>
|
||||
|
||||
<style>
|
||||
:root {
|
||||
--bg: #ffffff;
|
||||
--surface: #f4f4f4;
|
||||
--surface-warm: var(--surface);
|
||||
|
||||
--fg: #171a20;
|
||||
--fg-2: #393c41;
|
||||
--muted: #5c5e62;
|
||||
--meta: #8e8e8e;
|
||||
|
||||
--border: #d0d1d2;
|
||||
--border-soft: #eeeeee;
|
||||
|
||||
--accent: #3e6ae1;
|
||||
--accent-on: #ffffff;
|
||||
--accent-hover: color-mix(in oklab, var(--accent), black 8%);
|
||||
--accent-active: color-mix(in oklab, var(--accent), black 14%);
|
||||
|
||||
--success: #16a34a;
|
||||
--warn: #eab308;
|
||||
--danger: #dc2626;
|
||||
|
||||
--font-display: "Universal Sans Display", -apple-system, Arial, sans-serif;
|
||||
--font-body: "Universal Sans Text", -apple-system, Arial, sans-serif;
|
||||
--font-mono: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Monaco, Consolas, monospace;
|
||||
|
||||
--text-xs: 11px;
|
||||
--text-sm: 12px;
|
||||
--text-base: 14px;
|
||||
--text-lg: 16px;
|
||||
--text-xl: 17px;
|
||||
--text-2xl: 22px;
|
||||
--text-3xl: 28px;
|
||||
--text-4xl: 40px;
|
||||
|
||||
--leading-body: 1.43;
|
||||
--leading-tight: 1.20;
|
||||
|
||||
--tracking-display: normal;
|
||||
|
||||
--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: 120px;
|
||||
--section-y-tablet: 72px;
|
||||
--section-y-phone: 48px;
|
||||
|
||||
--radius-sm: 4px;
|
||||
--radius-md: 12px;
|
||||
--radius-lg: 12px;
|
||||
--radius-pill: 9999px;
|
||||
|
||||
--elev-flat: none;
|
||||
--elev-ring: 0 0 0 1px var(--border);
|
||||
--elev-raised: 0 2px 8px rgba(0, 0, 0, 0.05);
|
||||
|
||||
--focus-ring: 0 0 0 3px color-mix(in oklab, var(--accent), transparent 70%);
|
||||
|
||||
--motion-fast: 250ms;
|
||||
--motion-base: 330ms;
|
||||
--ease-standard: cubic-bezier(0.5, 0, 0, 0.75);
|
||||
|
||||
--container-max: 1383px;
|
||||
--container-gutter-desktop: 24px;
|
||||
--container-gutter-tablet: 16px;
|
||||
--container-gutter-phone: 12px;
|
||||
}
|
||||
|
||||
/* ─── Reset (minimal) ───────────────────────────────────────── */
|
||||
*, *::before, *::after { box-sizing: border-box; }
|
||||
html, body { margin: 0; padding: 0; }
|
||||
body {
|
||||
background: var(--bg);
|
||||
color: var(--fg-2);
|
||||
font-family: var(--font-body);
|
||||
font-size: var(--text-base);
|
||||
line-height: var(--leading-body);
|
||||
font-weight: 400;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
/* ─── Layout primitives ─────────────────────────────────────── */
|
||||
.container {
|
||||
max-width: var(--container-max);
|
||||
margin-inline: auto;
|
||||
padding-inline: var(--container-gutter-desktop);
|
||||
}
|
||||
section { padding-block: var(--section-y-desktop); }
|
||||
/* Tesla separates chapters by surface change, not borders
|
||||
(DESIGN.md §7 — "Don't add borders to cards or containers"). */
|
||||
.band-light { background: var(--bg); color: var(--fg-2); }
|
||||
.band-soft { background: var(--surface); color: var(--fg-2); }
|
||||
@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 — weight 400/500 only; no bold ever ──────── */
|
||||
h1, h2, h3 {
|
||||
font-family: var(--font-display);
|
||||
margin: 0;
|
||||
color: var(--fg);
|
||||
line-height: var(--leading-tight);
|
||||
letter-spacing: var(--tracking-display);
|
||||
}
|
||||
h1 { font-size: var(--text-4xl); font-weight: 500; }
|
||||
h2 { font-size: var(--text-3xl); font-weight: 500; }
|
||||
h3 { font-size: var(--text-xl); font-weight: 500; }
|
||||
p { margin: 0; }
|
||||
|
||||
.lead {
|
||||
font-size: var(--text-2xl);
|
||||
line-height: 1.35;
|
||||
color: var(--fg);
|
||||
font-weight: 400;
|
||||
max-width: 52ch;
|
||||
}
|
||||
.body-muted { color: var(--muted); }
|
||||
.body-meta { color: var(--meta); font-size: var(--text-sm); }
|
||||
.body-sm { font-size: var(--text-sm); }
|
||||
|
||||
/* `.eyebrow` — tiny uppercase label. Tesla's marketing site
|
||||
avoids text-transforms in the main nav and CTAs (DESIGN.md §3
|
||||
Principles + §7 "Don't use uppercase text transforms"), so
|
||||
the eyebrow is reserved exclusively for the small "Reference
|
||||
fixture · tesla" stage-label above hero / section titles —
|
||||
not a recurring component. */
|
||||
.eyebrow {
|
||||
font-family: var(--font-body);
|
||||
font-size: var(--text-xs);
|
||||
font-weight: 500;
|
||||
line-height: 1;
|
||||
color: var(--muted);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.08em;
|
||||
}
|
||||
|
||||
.stack-3 > * + * { margin-block-start: var(--space-3); }
|
||||
.stack-4 > * + * { margin-block-start: var(--space-4); }
|
||||
.stack-6 > * + * { margin-block-start: var(--space-6); }
|
||||
.stack-8 > * + * { margin-block-start: var(--space-8); }
|
||||
|
||||
/* ─── Buttons ─────────────────────────────────────────────────
|
||||
* Tesla's CTA family is uniform: 200×40 minimum, 4px radius,
|
||||
* weight 500 14px label, content centered via flexbox. The
|
||||
* primary fills with Electric Blue; the secondary is a white
|
||||
* fill on white surfaces (high-contrast against hero
|
||||
* photography). DESIGN.md §4 documents the precise transition
|
||||
* string `border-color 0.33s, background-color 0.33s, color
|
||||
* 0.33s, box-shadow 0.25s` — we map that to var(--motion-base)
|
||||
* / var(--motion-fast) so the timing is bound to tokens. No
|
||||
* scale or translate on interaction (DESIGN.md §7). */
|
||||
.btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: var(--space-2);
|
||||
min-height: 40px;
|
||||
min-width: 200px;
|
||||
padding: 4px 16px;
|
||||
border: 3px solid transparent;
|
||||
border-radius: var(--radius-sm);
|
||||
font-family: var(--font-body);
|
||||
font-size: var(--text-base);
|
||||
font-weight: 500;
|
||||
line-height: 1.2;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
transition:
|
||||
background-color var(--motion-base) var(--ease-standard),
|
||||
border-color var(--motion-base) var(--ease-standard),
|
||||
color var(--motion-base) var(--ease-standard),
|
||||
box-shadow var(--motion-fast) var(--ease-standard);
|
||||
}
|
||||
.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(--bg);
|
||||
color: var(--fg-2);
|
||||
border-color: var(--border);
|
||||
}
|
||||
.btn-secondary:hover {
|
||||
background: var(--surface);
|
||||
border-color: var(--meta);
|
||||
}
|
||||
|
||||
/* ─── Nav link / text link ───────────────────────────────────
|
||||
* Tertiary "Learn / Order / Experience" tier from DESIGN.md §4
|
||||
* — 14px Pewter, no decoration at rest, underline on hover via
|
||||
* the documented box-shadow transition. */
|
||||
.text-link {
|
||||
color: var(--muted);
|
||||
font-size: var(--text-base);
|
||||
font-weight: 400;
|
||||
text-decoration: none;
|
||||
transition:
|
||||
box-shadow var(--motion-base) var(--ease-standard),
|
||||
color var(--motion-base) var(--ease-standard);
|
||||
}
|
||||
.text-link:hover {
|
||||
color: var(--fg);
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 4px;
|
||||
}
|
||||
|
||||
/* ─── Inputs ────────────────────────────────────────────────
|
||||
* DESIGN.md §4: transparent background, Carbon Dark text,
|
||||
* Silver Fog placeholder. The fixture binds a faint --border
|
||||
* box so the input is visible against the pale section
|
||||
* background; focus tightens to --accent and adds the soft blue
|
||||
* halo. Density is quiet — photography is supposed to dominate
|
||||
* the page, not form chrome. */
|
||||
.field {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-2);
|
||||
}
|
||||
.field label {
|
||||
font-size: var(--text-base);
|
||||
font-weight: 500;
|
||||
color: var(--fg);
|
||||
}
|
||||
.field input {
|
||||
padding: 10px 12px;
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid var(--border);
|
||||
background: var(--bg);
|
||||
color: var(--fg);
|
||||
font-family: var(--font-body);
|
||||
font-size: var(--text-base);
|
||||
line-height: 1.4;
|
||||
outline: none;
|
||||
transition:
|
||||
border-color var(--motion-fast) var(--ease-standard),
|
||||
box-shadow var(--motion-fast) var(--ease-standard);
|
||||
}
|
||||
.field input::placeholder { color: var(--meta); }
|
||||
.field input:hover { border-color: var(--muted); }
|
||||
.field input:focus-visible {
|
||||
border-color: var(--accent);
|
||||
box-shadow: var(--focus-ring);
|
||||
}
|
||||
.field-help {
|
||||
font-size: var(--text-sm);
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
/* ─── Cards ─────────────────────────────────────────────────
|
||||
* Vehicle card mirrors DESIGN.md §4: transparent background, no
|
||||
* border, no shadow, content top-aligned. Categorically the
|
||||
* "no chrome" card — interaction lives in the text links
|
||||
* beneath the model name. */
|
||||
.vehicle-card {
|
||||
background: transparent;
|
||||
border: none;
|
||||
box-shadow: var(--elev-flat);
|
||||
padding: var(--space-4);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: var(--space-3);
|
||||
text-align: center;
|
||||
}
|
||||
.vehicle-card .vehicle-image {
|
||||
width: 100%;
|
||||
aspect-ratio: 16 / 10;
|
||||
border-radius: var(--radius-md);
|
||||
background:
|
||||
radial-gradient(80% 50% at 50% 75%, color-mix(in oklab, var(--accent), transparent 70%), transparent 70%),
|
||||
var(--surface);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--muted);
|
||||
}
|
||||
.vehicle-card .vehicle-image svg { width: 56%; height: auto; }
|
||||
.vehicle-card .vehicle-name {
|
||||
font-family: var(--font-display);
|
||||
font-size: var(--text-xl);
|
||||
font-weight: 500;
|
||||
color: var(--fg);
|
||||
margin: 0;
|
||||
}
|
||||
.vehicle-card .vehicle-links {
|
||||
display: flex;
|
||||
gap: var(--space-6);
|
||||
}
|
||||
|
||||
/* ─── Badges ────────────────────────────────────────────────
|
||||
* Used sparingly. Tesla's marketing site has no semantic chips,
|
||||
* so we keep .badge present in the fixture for completeness
|
||||
* (system-status indicator on the hero side panel) but bind it
|
||||
* to the muted neutral tier by default. */
|
||||
.badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--space-2);
|
||||
padding: 4px var(--space-3);
|
||||
border-radius: var(--radius-pill);
|
||||
font-family: var(--font-body);
|
||||
font-size: var(--text-xs);
|
||||
font-weight: 500;
|
||||
line-height: 1.4;
|
||||
}
|
||||
.badge-success {
|
||||
color: var(--success);
|
||||
background: color-mix(in oklab, var(--success), transparent 90%);
|
||||
}
|
||||
.badge-dot {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
background: currentColor;
|
||||
}
|
||||
|
||||
/* ─── Links (inline) ────────────────────────────────────────
|
||||
* Tesla's body links lean on the muted Pewter tier with the
|
||||
* same hover treatment as .text-link — there's no separate
|
||||
* "inline" tone. We keep the global `a` rule pointed at
|
||||
* --muted so inline reading copy matches DESIGN.md §4. */
|
||||
a {
|
||||
color: var(--muted);
|
||||
text-decoration: none;
|
||||
transition: color var(--motion-base) var(--ease-standard);
|
||||
}
|
||||
a:hover {
|
||||
color: var(--fg);
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 4px;
|
||||
}
|
||||
|
||||
/* ─── Kbd ─────────────────────────────────────────────────── */
|
||||
kbd {
|
||||
font-family: var(--font-mono);
|
||||
font-size: var(--text-xs);
|
||||
font-weight: 500;
|
||||
padding: 2px 6px;
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid var(--border);
|
||||
background: var(--bg);
|
||||
color: var(--fg);
|
||||
}
|
||||
|
||||
/* ─── Distinctive: floating nav strip ───────────────────────
|
||||
* DESIGN.md §4 — Tesla nav is "TESLA wordmark (spaced uppercase)
|
||||
* on the left, five category buttons center-aligned" floating
|
||||
* above the hero with no shadow, no border. The frosted-glass
|
||||
* scroll state uses rgba(255,255,255,0.75) backdrop (DESIGN.md
|
||||
* §6 Level 1 "Frost"); we implement that as the resting state
|
||||
* so the fixture documents the canonical chrome. */
|
||||
.nav {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
background: rgba(255, 255, 255, 0.75);
|
||||
-webkit-backdrop-filter: saturate(180%) blur(20px);
|
||||
backdrop-filter: saturate(180%) blur(20px);
|
||||
}
|
||||
.nav-inner {
|
||||
max-width: var(--container-max);
|
||||
margin-inline: auto;
|
||||
padding-inline: var(--container-gutter-desktop);
|
||||
height: 56px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: var(--space-6);
|
||||
}
|
||||
.nav-brand {
|
||||
font-family: var(--font-display);
|
||||
font-size: var(--text-base);
|
||||
font-weight: 500;
|
||||
color: var(--fg);
|
||||
letter-spacing: 0.4em;
|
||||
}
|
||||
.nav-links {
|
||||
display: flex;
|
||||
gap: var(--space-2);
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
.nav-links a {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
height: 32px;
|
||||
padding: 4px 16px;
|
||||
border-radius: var(--radius-sm);
|
||||
font-size: var(--text-base);
|
||||
font-weight: 500;
|
||||
color: var(--fg);
|
||||
transition:
|
||||
color var(--motion-base) var(--ease-standard),
|
||||
background-color var(--motion-base) var(--ease-standard);
|
||||
}
|
||||
.nav-links a:hover {
|
||||
text-decoration: none;
|
||||
background: var(--surface);
|
||||
}
|
||||
@media (max-width: 639px) {
|
||||
.nav-inner { gap: var(--space-3); padding-inline: var(--container-gutter-phone); height: 48px; }
|
||||
.nav-links { gap: var(--space-1); }
|
||||
.nav-links a { padding: 4px 8px; }
|
||||
}
|
||||
|
||||
/* ─── Section-specific layout ───────────────────────────────
|
||||
* Tesla heroes are full-viewport gallery moments — we lean into
|
||||
* a 1:1 stacked composition with model name → subtitle → CTA
|
||||
* pair (DESIGN.md §7 Do: "Center CTAs horizontally below model
|
||||
* names — the vertical rhythm is model → subtitle → buttons").
|
||||
* The right column is a quiet status card so the fixture
|
||||
* exercises the secondary surface tier and badge. */
|
||||
.hero-stage {
|
||||
display: grid;
|
||||
grid-template-columns: 1.4fr 1fr;
|
||||
gap: var(--space-12);
|
||||
align-items: center;
|
||||
min-height: 60vh;
|
||||
}
|
||||
@media (max-width: 1023px) {
|
||||
.hero-stage { grid-template-columns: 1fr; gap: var(--space-8); min-height: 0; }
|
||||
}
|
||||
.hero-canvas {
|
||||
position: relative;
|
||||
border-radius: var(--radius-md);
|
||||
aspect-ratio: 16 / 10;
|
||||
overflow: hidden;
|
||||
background:
|
||||
radial-gradient(70% 50% at 70% 30%, color-mix(in oklab, var(--fg), transparent 70%), transparent 60%),
|
||||
linear-gradient(180deg, var(--surface) 0%, color-mix(in oklab, var(--surface), var(--accent) 6%) 100%);
|
||||
}
|
||||
.hero-canvas-label {
|
||||
position: absolute;
|
||||
top: var(--space-4);
|
||||
left: var(--space-4);
|
||||
font-family: var(--font-body);
|
||||
font-size: var(--text-sm);
|
||||
color: var(--muted);
|
||||
}
|
||||
.hero-actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: var(--space-3);
|
||||
margin-block-start: var(--space-6);
|
||||
}
|
||||
.hero-meta {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-3);
|
||||
padding: var(--space-5);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-md);
|
||||
background: var(--bg);
|
||||
}
|
||||
|
||||
/* Features: three centered "vehicle" cards. Tesla's nav-panel
|
||||
pattern from DESIGN.md §4 — 3 columns, transparent cards,
|
||||
model name + text-link pair. */
|
||||
.features-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: var(--space-8);
|
||||
}
|
||||
@media (max-width: 1023px) {
|
||||
.features-grid { grid-template-columns: 1fr 1fr; }
|
||||
}
|
||||
@media (max-width: 639px) {
|
||||
.features-grid { grid-template-columns: 1fr; }
|
||||
}
|
||||
|
||||
.form-row {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: var(--space-12);
|
||||
align-items: start;
|
||||
}
|
||||
@media (max-width: 1023px) {
|
||||
.form-row { grid-template-columns: 1fr; gap: var(--space-8); }
|
||||
}
|
||||
.form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-4);
|
||||
max-width: 420px;
|
||||
}
|
||||
.form-actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
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>
|
||||
<!-- ════════════════════════════════════════════════════════════
|
||||
NAV — TESLA wordmark left (spaced letters), five centered
|
||||
category buttons, three icon-like links right. Floats over
|
||||
the hero with a frosted-glass backdrop (DESIGN.md §4 §6).
|
||||
═══════════════════════════════════════════════════════════════ -->
|
||||
<header class="nav" data-od-id="nav">
|
||||
<div class="nav-inner">
|
||||
<span class="nav-brand">TESLA</span>
|
||||
<nav aria-label="Primary">
|
||||
<ul class="nav-links">
|
||||
<li><a href="#hero">Vehicles</a></li>
|
||||
<li><a href="#features">Energy</a></li>
|
||||
<li><a href="#form">Charging</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<!-- ════════════════════════════════════════════════════════════
|
||||
HERO — exercises: h1 (40px display, normal tracking),
|
||||
.lead (22px promo tier), .btn-primary (Electric Blue) paired
|
||||
with .btn-secondary (white), kbd, .badge-success. Stacked
|
||||
composition with the rhythm DESIGN.md §7 prescribes:
|
||||
model name → subtitle → CTA pair. Right column carries the
|
||||
system-status card so the fixture exercises --surface,
|
||||
--border, and the badge tier without polluting the hero
|
||||
with chrome.
|
||||
═══════════════════════════════════════════════════════════════ -->
|
||||
<section class="band-light" data-od-id="hero" id="hero">
|
||||
<div class="container">
|
||||
<div class="hero-stage">
|
||||
<div class="stack-6">
|
||||
<p class="eyebrow">Reference fixture · tesla</p>
|
||||
<h1>Model Reference</h1>
|
||||
<p class="lead">
|
||||
Accelerating the world's transition to sustainable
|
||||
energy — distilled into fifty-six tokens, one accent
|
||||
color, and the white space between every section.
|
||||
</p>
|
||||
<div class="hero-actions">
|
||||
<a href="./tokens.css" class="btn btn-primary">Order Now</a>
|
||||
<a href="./DESIGN.md" class="btn btn-secondary">View Inventory</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<aside class="hero-canvas" aria-label="Hero photography placeholder">
|
||||
<span class="hero-canvas-label">Photography canvas · 100vh</span>
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ════════════════════════════════════════════════════════════
|
||||
FEATURES — band switches to Light Ash (--surface), three
|
||||
transparent "vehicle" cards that mirror the DESIGN.md §4
|
||||
nav-panel pattern: image placeholder, centered model name
|
||||
in Universal Sans Display, two text links beneath. No
|
||||
shadow, no border, no card background — interaction lives
|
||||
in the .text-link tier only.
|
||||
═══════════════════════════════════════════════════════════════ -->
|
||||
<section class="band-soft" data-od-id="features" id="features">
|
||||
<div class="container">
|
||||
<div class="stack-3" style="max-width: 640px">
|
||||
<p class="eyebrow">The lineup</p>
|
||||
<h2>Three vehicles. One typography stack. Zero shadows.</h2>
|
||||
</div>
|
||||
|
||||
<div class="features-grid" style="margin-block-start: var(--space-12)">
|
||||
<article class="vehicle-card">
|
||||
<div class="vehicle-image" aria-hidden="true">
|
||||
<svg viewBox="0 0 200 80" fill="none"
|
||||
stroke="currentColor" stroke-width="1.5"
|
||||
stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M14 56h172l-14-22-26-12H46l-22 14L14 56z" />
|
||||
<circle cx="58" cy="60" r="10" fill="var(--bg)" />
|
||||
<circle cx="148" cy="60" r="10" fill="var(--bg)" />
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="vehicle-name">Model S</h3>
|
||||
<div class="vehicle-links">
|
||||
<a href="./tokens.css" class="text-link">Learn</a>
|
||||
<a href="./DESIGN.md" class="text-link">Order</a>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="vehicle-card">
|
||||
<div class="vehicle-image" aria-hidden="true">
|
||||
<svg viewBox="0 0 200 80" fill="none"
|
||||
stroke="currentColor" stroke-width="1.5"
|
||||
stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M14 56h172l-12-28-30-14H50l-24 18L14 56z" />
|
||||
<circle cx="58" cy="60" r="10" fill="var(--bg)" />
|
||||
<circle cx="148" cy="60" r="10" fill="var(--bg)" />
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="vehicle-name">Model 3</h3>
|
||||
<div class="vehicle-links">
|
||||
<a href="./tokens.css" class="text-link">Learn</a>
|
||||
<a href="./DESIGN.md" class="text-link">Order</a>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="vehicle-card">
|
||||
<div class="vehicle-image" aria-hidden="true">
|
||||
<svg viewBox="0 0 200 80" fill="none"
|
||||
stroke="currentColor" stroke-width="1.5"
|
||||
stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M14 56h172l-10-26-28-16H48l-22 18L14 56z" />
|
||||
<circle cx="58" cy="60" r="10" fill="var(--bg)" />
|
||||
<circle cx="148" cy="60" r="10" fill="var(--bg)" />
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="vehicle-name">Model Y</h3>
|
||||
<div class="vehicle-links">
|
||||
<a href="./tokens.css" class="text-link">Learn</a>
|
||||
<a href="./DESIGN.md" class="text-link">Order</a>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ════════════════════════════════════════════════════════════
|
||||
FORM — exercises: .field, input :focus-visible (border
|
||||
tightens to --accent + the soft Electric Blue halo),
|
||||
.btn-primary (the same 200×40 CTA from the hero — no new
|
||||
token introduced), .btn-secondary, .field-help. Band
|
||||
returns to white so the form reads as a calm transactional
|
||||
moment rather than a hero.
|
||||
═══════════════════════════════════════════════════════════════ -->
|
||||
<section class="band-light" data-od-id="form" id="form">
|
||||
<div class="container">
|
||||
<div class="form-row">
|
||||
<div class="stack-4">
|
||||
<p class="eyebrow">Schedule a drive</p>
|
||||
<h2>Quiet form. Loud product.</h2>
|
||||
<p class="body-muted" style="max-width: 52ch">
|
||||
Inputs inherit the same token block — a hairline Pale
|
||||
Silver edge, Carbon Dark text, the focus halo at three
|
||||
pixels of soft Electric Blue. Press
|
||||
<kbd>↵</kbd> to confirm; the rest of the page stays
|
||||
out of the way.
|
||||
</p>
|
||||
<div class="row-between" style="max-width: 360px">
|
||||
<span class="body-sm">Test-drive availability</span>
|
||||
<span class="badge badge-success">
|
||||
<span class="badge-dot" aria-hidden="true"></span>
|
||||
Open
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form class="form" onsubmit="event.preventDefault();">
|
||||
<div class="field">
|
||||
<label for="email">Work email</label>
|
||||
<input
|
||||
id="email"
|
||||
type="email"
|
||||
placeholder="you@tesla.com"
|
||||
autocomplete="email"
|
||||
required
|
||||
/>
|
||||
<p class="field-help">
|
||||
We'll only contact you to confirm the appointment.
|
||||
</p>
|
||||
</div>
|
||||
<div class="form-actions">
|
||||
<button type="submit" class="btn btn-primary">
|
||||
Schedule a Drive
|
||||
<svg class="icon" viewBox="0 0 24 24" fill="none"
|
||||
stroke="currentColor" stroke-width="1.75"
|
||||
stroke-linecap="round" stroke-linejoin="round"
|
||||
aria-hidden="true"><path d="M5 12h14M13 6l6 6-6 6"/></svg>
|
||||
</button>
|
||||
<button type="button" class="btn btn-secondary">
|
||||
View Inventory
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
285
design-systems/tesla/tokens.css
Normal file
285
design-systems/tesla/tokens.css
Normal file
|
|
@ -0,0 +1,285 @@
|
|||
/* ─────────────────────────────────────────────────────────────────────────
|
||||
* design-systems/tesla/tokens.css
|
||||
*
|
||||
* Structured token bindings for "Design System Inspired by Tesla" —
|
||||
* radical subtraction, full-viewport photography, near-zero UI. This
|
||||
* file is the *machine-readable* form of the values described in
|
||||
* `DESIGN.md`. Agents are expected to paste the `:root { … }` block
|
||||
* verbatim into the first `<style>` of every artifact they generate
|
||||
* against this design system, then reference everything via
|
||||
* var(--name) from then on.
|
||||
*
|
||||
* Why this file exists:
|
||||
* DESIGN.md tells humans that Tesla uses "Pure White (#FFFFFF) as the
|
||||
* canvas that lets photography breathe" and "Electric Blue (#3E6AE1)
|
||||
* as the only chromatic color in the entire interface", but agents
|
||||
* have to translate those prose names into the standard token names
|
||||
* the lint enforces (`--bg`, `--accent`). That translation step is
|
||||
* where token misuse happens. This file pre-translates the brand
|
||||
* once, so agents copy structure instead of inventing it.
|
||||
*
|
||||
* Brand-specific schema decisions (non-obvious bindings worth flagging
|
||||
* for reviewers and downstream brand authors):
|
||||
* 1. --bg is pure #ffffff and --surface is Light Ash #f4f4f4 — the
|
||||
* "barely perceptible shift from pure white" Tesla uses to
|
||||
* differentiate alternate feature bands. --surface-warm aliases
|
||||
* to var(--surface): Tesla deliberately has only two light tiers
|
||||
* because photography (not surface) carries the visual richness.
|
||||
* 2. The four-stop foreground ramp is real and worth keeping
|
||||
* independent — Carbon Dark → Graphite → Pewter → Silver Fog map
|
||||
* cleanly onto --fg / --fg-2 / --muted / --meta. DESIGN.md §2
|
||||
* names every stop; collapsing any of them would flatten the
|
||||
* hierarchy of headings → body → tertiary links → placeholders.
|
||||
* 3. --border is bound to Pale Silver (#d0d1d2) for the rare strong
|
||||
* border, --border-soft to Cloud Gray (#eeeeee) for the more
|
||||
* common hairline divider. Tesla uses borders sparingly but the
|
||||
* two-stop split exists in DESIGN.md §2.
|
||||
* 4. --accent-hover / --accent-active use the schema black-mix
|
||||
* formulas. DESIGN.md §4 says "subtle darkening of blue
|
||||
* background" without pinning a hex; the schema defaults satisfy
|
||||
* "subtle" without inventing a brand-specific value.
|
||||
* 5. Semantic --success / --warn / --danger inherit schema defaults
|
||||
* verbatim. DESIGN.md §2: "Tesla's marketing site avoids semantic
|
||||
* color coding... Error, success, and warning states follow
|
||||
* standard browser defaults in form contexts."
|
||||
* 6. --font-display and --font-body are split across the Universal
|
||||
* Sans Display / Universal Sans Text families with -apple-system
|
||||
* and Arial as the published Tesla fallback chain (DESIGN.md §3).
|
||||
* 7. --text-base is 14px (NOT the 16px web norm). Tesla's UI
|
||||
* explicitly clusters at 14 for nav, body, button, and link —
|
||||
* DESIGN.md §3 lists six rows at 14px and only two breakaway
|
||||
* tiers (22px promo, 40px hero). Rebinding --text-base to 16
|
||||
* would lose the brand's engineered uniformity.
|
||||
* 8. --text-4xl is 40px — Tesla's hard display ceiling. DESIGN.md §7
|
||||
* ("Don't use text larger than 40px on the web") makes this not
|
||||
* a soft cap but an identity rule.
|
||||
* 9. --tracking-display is `normal`. DESIGN.md §3 Principles:
|
||||
* "'Normal' letter-spacing everywhere... Unlike most modern tech
|
||||
* brands that use negative tracking for headlines, Tesla uses
|
||||
* default letter-spacing at every level." Most brands in the
|
||||
* schema bind a negative em value here; Tesla deliberately does
|
||||
* not.
|
||||
* 10. --section-y-desktop is 120px (vs schema default 80, Apple 100).
|
||||
* DESIGN.md §1 + §5 — "viewport-height sections" and "whitespace
|
||||
* as a luxury signal" mean Tesla's chapter breathing room is the
|
||||
* most generous in the schema after the dedicated print brands.
|
||||
* 11. --radius-sm / --radius-md / --radius-lg are 4 / 12 / 12 (vs
|
||||
* schema 8 / 12 / 16). DESIGN.md §5 defines a deliberate Tesla
|
||||
* radius scale: 0 default, 4 buttons, 12 category cards, and
|
||||
* "no larger tier" — we bind --radius-lg to the same 12 because
|
||||
* Tesla has no softer card silhouette to address.
|
||||
* 12. --elev-raised is the lightest in the schema (rgba 0.05).
|
||||
* DESIGN.md §6: "Tesla's approach to elevation is essentially
|
||||
* 'none.'... Subtle shadow hints on rare hover states use
|
||||
* rgba(0,0,0,0.05)." The schema default (oklab fg-mix) reads as
|
||||
* Material weight by comparison; we hand-pick a flatter value.
|
||||
* 13. --motion-fast / --motion-base / --ease-standard override the
|
||||
* schema. DESIGN.md §1 + §4 — "0.33s cubic-bezier transitions as
|
||||
* the universal timing for all interactive state changes" and a
|
||||
* documented `box-shadow 0.33s cubic-bezier(0.5, 0, 0, 0.75)`
|
||||
* curve. We bind base to 330ms (Tesla's universal duration),
|
||||
* fast to 250ms (the box-shadow micro-state), and easing to the
|
||||
* published Tesla text-link curve.
|
||||
* 14. --container-max is 1383px. DESIGN.md §5: "Max width:
|
||||
* approximately 1383px". The number itself is a brand-specific
|
||||
* detail (most marketing sites round to 1280 / 1440); we keep
|
||||
* the literal value rather than collapse to the nearest round
|
||||
* number.
|
||||
*
|
||||
* Contract sources:
|
||||
* - Standard token names: design-systems/_schema/tokens.schema.ts
|
||||
* (TOKEN_SCHEMA — every name below appears there or as an
|
||||
* explicit B-slot alias.)
|
||||
* - A2 fallback values: design-systems/_schema/defaults.css
|
||||
* (We override --motion-fast, --motion-base, --ease-standard,
|
||||
* --elev-raised, --radius-sm, --radius-lg; the rest match
|
||||
* defaults.)
|
||||
*
|
||||
* Keep this file additive: never invent token names not also documented
|
||||
* in DESIGN.md or the shared schema.
|
||||
* ─────────────────────────────────────────────────────────────────── */
|
||||
|
||||
:root {
|
||||
/* ─── Surface (2 levels) ──────────────────────────────────────────
|
||||
* Pure white retail canvas with a single "barely perceptible" Light
|
||||
* Ash tier for alternate bands. --surface-warm aliases to surface
|
||||
* because Tesla has no third light tier — photography, not surface,
|
||||
* carries the visual richness. */
|
||||
--bg: #ffffff; /* Pure White — the canvas that lets photography breathe */
|
||||
--surface: #f4f4f4; /* Light Ash — alternate feature band, barely a shift */
|
||||
--surface-warm: var(--surface); /* alias — Tesla has only two light tiers */
|
||||
|
||||
/* ─── Foreground ramp (4 levels) ─────────────────────────────────
|
||||
* Tesla's neutrals are explicitly named in DESIGN.md §2: Carbon Dark
|
||||
* → Graphite → Pewter → Silver Fog. We bind all four — the ramp is
|
||||
* genuinely walked across model names (Carbon Dark), body copy
|
||||
* (Graphite), tertiary "Learn / Order" links (Pewter), and form
|
||||
* placeholders / disabled (Silver Fog). Collapsing any stop would
|
||||
* flatten the hierarchy. */
|
||||
--fg: #171a20; /* Carbon Dark — headings, nav, model names */
|
||||
--fg-2: #393c41; /* Graphite — default body / paragraph color */
|
||||
--muted: #5c5e62; /* Pewter — tertiary links (Learn, Order, Experience) */
|
||||
--meta: #8e8e8e; /* Silver Fog — placeholders, disabled states */
|
||||
|
||||
/* ─── Border (2 levels) ──────────────────────────────────────────
|
||||
* Tesla uses borders sparingly — containers are usually defined by
|
||||
* spacing, not lines (DESIGN.md §7: "Don't add borders to cards or
|
||||
* containers"). When borders appear, --border is the stronger
|
||||
* delineation (Pale Silver, used on rare hairlines and field
|
||||
* outlines) and --border-soft is the lighter divider (Cloud Gray,
|
||||
* used between dense rows). */
|
||||
--border: #d0d1d2; /* Pale Silver — strong border, field outline */
|
||||
--border-soft: #eeeeee; /* Cloud Gray — light divider between rows */
|
||||
|
||||
/* ─── Accent ──────────────────────────────────────────────────────
|
||||
* Electric Blue — Tesla's only chromatic move. DESIGN.md §2: "stands
|
||||
* alone as the only chromatic color in the entire interface" and §7
|
||||
* "Don't use more than one chromatic color besides the blue CTA"
|
||||
* make this a strict one-color brand. The schema's ≤2-visible-uses
|
||||
* cap aligns naturally with Tesla's "Use Electric Blue exclusively
|
||||
* for primary CTAs". */
|
||||
--accent: #3e6ae1; /* Electric Blue — sole CTA color, sole accent */
|
||||
--accent-on: #ffffff; /* white label on the blue fill */
|
||||
|
||||
/* ─── Accent states ───────────────────────────────────────────────
|
||||
* DESIGN.md §4 documents "subtle darkening of blue background" on
|
||||
* hover without pinning specific hex values. We inherit the schema
|
||||
* black-mix formulas — subtle darkening is exactly what these
|
||||
* formulas produce. */
|
||||
--accent-hover: color-mix(in oklab, var(--accent), black 8%);
|
||||
--accent-active: color-mix(in oklab, var(--accent), black 14%);
|
||||
|
||||
/* ─── Semantic ────────────────────────────────────────────────────
|
||||
* DESIGN.md §2: "Tesla's marketing site avoids semantic color coding
|
||||
* (no green/red/yellow status indicators). Error, success, and
|
||||
* warning states follow standard browser defaults in form contexts."
|
||||
* Inherit the schema defaults; reserve under 5% of any surface area
|
||||
* regardless. */
|
||||
--success: #16a34a;
|
||||
--warn: #eab308;
|
||||
--danger: #dc2626;
|
||||
|
||||
/* ─── Typography ──────────────────────────────────────────────────
|
||||
* Universal Sans Display / Universal Sans Text — the custom family
|
||||
* that replaced Gotham to unify Tesla's website, mobile app, and
|
||||
* vehicle interface. We mirror the published Tesla fallback chain
|
||||
* (-apple-system, Arial) so artifacts rendered on non-Tesla clients
|
||||
* land on the closest practical substitute. */
|
||||
--font-display: "Universal Sans Display", -apple-system, Arial, sans-serif;
|
||||
--font-body: "Universal Sans Text", -apple-system, Arial, sans-serif;
|
||||
--font-mono: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Monaco, Consolas, monospace;
|
||||
|
||||
/* Type scale (px) — derived from DESIGN.md §3 hierarchy table.
|
||||
* Tesla's reading baseline is 14px (NOT the 16px web norm); nav,
|
||||
* body, button, sub-link all sit at 14. Only two tiers break away
|
||||
* upward — 22px promo and 40px hero — and DESIGN.md §7 hard-caps
|
||||
* web text at 40px ("Don't use text larger than 40px on the web").
|
||||
* The 28px mid-display tier honors the §8 mobile hero scale
|
||||
* ("hero text scales to ~28px"). */
|
||||
--text-xs: 11px; /* micro — fine print below the 14px baseline */
|
||||
--text-sm: 12px; /* small UI labels */
|
||||
--text-base: 14px; /* Tesla's reading baseline (nav, body, button, link) */
|
||||
--text-lg: 16px; /* Category Label — white text on category cards */
|
||||
--text-xl: 17px; /* Product Name — model names in nav panel and cards */
|
||||
--text-2xl: 22px; /* Promo Text — "0% APR Available" on hero */
|
||||
--text-3xl: 28px; /* mid display — mobile hero scale */
|
||||
--text-4xl: 40px; /* Hero Title — Tesla's hard display ceiling */
|
||||
|
||||
/* DESIGN.md §3 hierarchy table: hero 40/48 (1.20), body 14/20
|
||||
* (1.43). Two values for the whole system — Tesla's leading
|
||||
* envelope is as restrained as its weight set. */
|
||||
--leading-body: 1.43; /* SF Pro Text body, 17px */
|
||||
--leading-tight: 1.20; /* Hero Title 40/48 */
|
||||
|
||||
/* DESIGN.md §3 Principles: "'Normal' letter-spacing everywhere.
|
||||
* Unlike most modern tech brands that use negative tracking for
|
||||
* headlines, Tesla uses default letter-spacing at every level."
|
||||
* Most schema brands bind a negative em value here; Tesla
|
||||
* deliberately does not. */
|
||||
--tracking-display: normal;
|
||||
|
||||
/* ─── Spacing ─────────────────────────────────────────────────────
|
||||
* 8px base grid (DESIGN.md §5). Schema defaults match Tesla's scale
|
||||
* exactly — no overrides needed. */
|
||||
--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 rhythm ──────────────────────────────────────────────
|
||||
* Tesla uses viewport-height sections (DESIGN.md §1 §5) and
|
||||
* whitespace as a luxury signal. Token-level padding is the most
|
||||
* generous in the schema — 120 desktop is 50% wider than the schema
|
||||
* default (80) and 20% wider than Apple's 100. The full-viewport
|
||||
* 100vh rhythm itself lives in components. */
|
||||
--section-y-desktop: 120px;
|
||||
--section-y-tablet: 72px;
|
||||
--section-y-phone: 48px;
|
||||
|
||||
/* ─── Radius ──────────────────────────────────────────────────────
|
||||
* DESIGN.md §5 documents Tesla's deliberate three-stop scale:
|
||||
* 0px most elements (sharp default)
|
||||
* 4px buttons (primary, secondary, nav items)
|
||||
* ~12px category cards (the largest sanctioned rounding)
|
||||
* 50% carousel dots
|
||||
*
|
||||
* We bind --radius-sm to 4 (buttons), --radius-md to 12 (category
|
||||
* cards), and --radius-lg to 12 as well — Tesla has no softer card
|
||||
* tier beyond category. The schema's 16px lg default would invent a
|
||||
* silhouette Tesla never uses. Pill stays at 9999 for dots / chips. */
|
||||
--radius-sm: 4px; /* buttons — "barely perceptible rounding" */
|
||||
--radius-md: 12px; /* category cards — the largest sanctioned */
|
||||
--radius-lg: 12px; /* same — Tesla has no third radius tier */
|
||||
--radius-pill: 9999px; /* carousel dots, badges */
|
||||
|
||||
/* ─── Elevation ───────────────────────────────────────────────────
|
||||
* DESIGN.md §6: "Tesla's approach to elevation is essentially
|
||||
* 'none.'" Three sanctioned levels but flatter than any other
|
||||
* schema brand:
|
||||
* - flat → none (the default; photography carries depth)
|
||||
* - ring → 0 0 0 1px var(--border) (rare border-led containment)
|
||||
* - raised → 0 2px 8px rgba(0,0,0,0.05) (DESIGN.md §6 Level 3
|
||||
* "Subtle" — minimal shadow hints on rare hover states)
|
||||
*
|
||||
* The schema default oklab-mix raised reads as Material weight by
|
||||
* comparison; Tesla flattens it to the lightest credible value. */
|
||||
--elev-flat: none;
|
||||
--elev-ring: 0 0 0 1px var(--border);
|
||||
--elev-raised: 0 2px 8px rgba(0, 0, 0, 0.05);
|
||||
|
||||
/* ─── Focus ring ──────────────────────────────────────────────────
|
||||
* Electric Blue ring at the accent — Tesla's single chromatic
|
||||
* signal doubles as the focus halo. Schema default formula is
|
||||
* already the correct color through var(--accent). */
|
||||
--focus-ring: 0 0 0 3px color-mix(in oklab, var(--accent), transparent 70%);
|
||||
|
||||
/* ─── Motion ──────────────────────────────────────────────────────
|
||||
* DESIGN.md §1 §4 — "0.33s cubic-bezier transitions as the
|
||||
* universal timing for all interactive state changes." The
|
||||
* documented Tesla Text Link curve is cubic-bezier(0.5, 0, 0,
|
||||
* 0.75); we bind that as --ease-standard.
|
||||
* - fast → 250ms (the box-shadow micro-state from §4)
|
||||
* - base → 330ms (Tesla's universal 0.33s)
|
||||
*
|
||||
* No brand in the schema runs base this slow; Tesla's "consistency
|
||||
* in motion is as important as consistency in color" (DESIGN.md §7)
|
||||
* earns the override. */
|
||||
--motion-fast: 250ms;
|
||||
--motion-base: 330ms;
|
||||
--ease-standard: cubic-bezier(0.5, 0, 0, 0.75);
|
||||
|
||||
/* ─── Layout ──────────────────────────────────────────────────────
|
||||
* DESIGN.md §5: "Max width: approximately 1383px (full viewport
|
||||
* width used for most content)." We keep the literal value rather
|
||||
* than collapse to a rounder number — the figure itself is a Tesla
|
||||
* artifact. Gutters step 24 → 16 → 12 on a clean 8px scale; mobile
|
||||
* keeps horizontal padding tight so photography stays full-bleed. */
|
||||
--container-max: 1383px;
|
||||
--container-gutter-desktop: 24px;
|
||||
--container-gutter-tablet: 16px;
|
||||
--container-gutter-phone: 12px;
|
||||
}
|
||||
507
design-systems/wechat/components.html
Normal file
507
design-systems/wechat/components.html
Normal file
|
|
@ -0,0 +1,507 @@
|
|||
<!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>
|
||||
176
design-systems/wechat/tokens.css
Normal file
176
design-systems/wechat/tokens.css
Normal file
|
|
@ -0,0 +1,176 @@
|
|||
/* ─────────────────────────────────────────────────────────────────────────
|
||||
* design-systems/wechat/tokens.css
|
||||
*
|
||||
* Structured token bindings for "WeChat" (微信) — Tencent's all-in-one
|
||||
* social, payment, and mini-program super-app. The visual language is
|
||||
* a quiet, light-friendly consumer canvas built around one decisive
|
||||
* signal: WeChat Green (#07C160). Everything else recedes so green
|
||||
* carries the brand — CTAs, success states, active tab, focus ring.
|
||||
* Bilingual CJK-aware typography (PingFang SC, Hiragino Sans GB,
|
||||
* Microsoft YaHei, Noto Sans SC) keeps English and 中文 on the same
|
||||
* vertical rhythm so chat rows, page titles, and mini-program cards
|
||||
* align in either script without per-language overrides.
|
||||
*
|
||||
* Brand identity in three sentences:
|
||||
* 1. The canvas is the muted #EDEDED chat-list gray, not pure white —
|
||||
* this is the colour every WeChat user has stared at for a decade.
|
||||
* Surfaces (#F7F7F7) lift one tone above it. Pure white belongs to
|
||||
* incoming chat bubbles, not pages.
|
||||
* 2. Brand green #07C160 is the only accent. Hover (#10B160) and
|
||||
* pressed (#059050) come straight from the platform spec; the same
|
||||
* green doubles as the success state because in WeChat green *is*
|
||||
* "good / done / sent / paid".
|
||||
* 3. Type is mobile-first and dense: 15px body at 1.6 line-height,
|
||||
* 18px page titles, 11px timestamps. The display ladder (24 → 32
|
||||
* → 48px) is deliberately modest — WeChat's marketing voice
|
||||
* ("微信,是一个生活方式") is understated, not billboard-loud.
|
||||
*
|
||||
* Schema decisions:
|
||||
* - --bg: #ededed (chat-list gray, DESIGN.md §UI Neutrals).
|
||||
* - --surface: #f7f7f7 (card / sheet lift on bg).
|
||||
* - --surface-warm: var(--surface) — single surface tier.
|
||||
* - --fg: #1a1a1a (ink); --fg-2 alias — DESIGN.md has one text tier.
|
||||
* - --muted: #888888 (secondary + timestamps);
|
||||
* --meta: var(--muted) — DESIGN.md does not separate the tier.
|
||||
* - --border: #e0e0e0; --border-soft alias — single border weight.
|
||||
* - --accent: #07c160 + hover #10b160 + active #059050 (DESIGN.md).
|
||||
* - --success: #07c160 (brand-as-success — green is "done" in WeChat).
|
||||
* - --warn: #fab702; --danger: #fa5151 (DESIGN.md functional palette).
|
||||
* - --font-display / --font-body: identical apple-system + CJK stack.
|
||||
* DESIGN.md uses a single family across the app; bilingual users get
|
||||
* PingFang SC → Hiragino Sans GB → Microsoft YaHei → Noto Sans SC
|
||||
* fallbacks so 中文 renders crisply on macOS, iOS, Windows and Linux.
|
||||
* - Type scale: 11 / 13 / 15 / 16 / 18 / 24 / 32 / 48 — mobile-first
|
||||
* chat ladder extended with a modest 48px hero for marketing pages.
|
||||
* - --leading-body: 1.6 (DESIGN.md body, generous for CJK density).
|
||||
* - Radius: 4 / 8 / 16 / pill (DESIGN.md §Border Radius).
|
||||
* - Motion: 100ms hover, 200ms message-entry, cubic-bezier(0.25, 0.1,
|
||||
* 0.25, 1) — DESIGN.md §Motion (instant + fast + ease-default).
|
||||
* ─────────────────────────────────────────────────────────────────── */
|
||||
|
||||
:root {
|
||||
/* ─── Surface ──────────────────────────────────────────────────────
|
||||
* Light-friendly canvas: the muted #EDEDED chat-list gray is the
|
||||
* WeChat user's home colour. Surfaces lift one tone above it. */
|
||||
--bg: #ededed; /* chat-list gray — primary canvas */
|
||||
--surface: #f7f7f7; /* card / sheet lift on bg */
|
||||
--surface-warm: var(--surface); /* alias — single surface tier */
|
||||
|
||||
/* ─── Foreground ───────────────────────────────────────────────────
|
||||
* One ink, one secondary. DESIGN.md does not separate dark / warm
|
||||
* tiers, so --fg-2 and --meta alias their siblings. */
|
||||
--fg: #1a1a1a; /* ink — primary text */
|
||||
--fg-2: var(--fg); /* alias — single text weight */
|
||||
--muted: #888888; /* secondary + timestamps */
|
||||
--meta: var(--muted); /* alias — same gray for metadata */
|
||||
|
||||
/* ─── Border ───────────────────────────────────────────────────────
|
||||
* Dividers are quiet — most cards are defined by background contrast,
|
||||
* not borders. When borders appear, they are the same hairline gray. */
|
||||
--border: #e0e0e0; /* card / row edge */
|
||||
--border-soft: var(--border); /* alias — no inner separator tier */
|
||||
|
||||
/* ─── Accent ───────────────────────────────────────────────────────
|
||||
* WeChat Green is the single load-bearing colour. Hover and active
|
||||
* are taken verbatim from DESIGN.md so primary buttons feel native. */
|
||||
--accent: #07c160; /* WeChat green — primary brand */
|
||||
--accent-on: #ffffff;
|
||||
--accent-hover: #10b160; /* DESIGN.md primary hover */
|
||||
--accent-active: #059050; /* DESIGN.md primary pressed */
|
||||
|
||||
/* ─── Semantic ─────────────────────────────────────────────────────
|
||||
* Green doubles as success because in WeChat green *is* "done / paid /
|
||||
* sent". Warning + danger come straight from DESIGN.md §Functional. */
|
||||
--success: #07c160; /* brand-as-success — paid / done */
|
||||
--warn: #fab702; /* DESIGN.md warning orange */
|
||||
--danger: #fa5151; /* DESIGN.md error red */
|
||||
|
||||
/* ─── Typography ───────────────────────────────────────────────────
|
||||
* Single bilingual stack across display + body. -apple-system first
|
||||
* for SF / SF Pro on Apple platforms; PingFang SC + Hiragino Sans GB
|
||||
* + Microsoft YaHei + Noto Sans SC keep 中文 crisp on macOS, iOS,
|
||||
* Windows and Linux respectively. Helvetica Neue / Helvetica / Arial
|
||||
* close the Latin tail. */
|
||||
--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;
|
||||
|
||||
/* Type scale — DESIGN.md §Typography is mobile-first (11–18px). The
|
||||
* top three tiers (24 / 32 / 48px) extend the ladder for marketing
|
||||
* pages while staying understated — WeChat does not shout. */
|
||||
--text-xs: 11px; /* caption / timestamp */
|
||||
--text-sm: 13px; /* secondary text */
|
||||
--text-base: 15px; /* body — WeChat default */
|
||||
--text-lg: 16px; /* section header / button label */
|
||||
--text-xl: 18px; /* page title */
|
||||
--text-2xl: 24px; /* feature heading */
|
||||
--text-3xl: 32px; /* H2 — section title */
|
||||
--text-4xl: 48px; /* display hero — modest by design */
|
||||
|
||||
/* Leading + tracking.
|
||||
* 1.6 body line-height (DESIGN.md) gives Chinese characters generous
|
||||
* air; 1.3 tight line-height matches the page-title spec. Display
|
||||
* tracking compresses one notch — works for both Latin and CJK. */
|
||||
--leading-body: 1.6;
|
||||
--leading-tight: 1.3;
|
||||
--tracking-display: -0.01em;
|
||||
|
||||
/* ─── Spacing ──────────────────────────────────────────────────────
|
||||
* 4px base unit, DESIGN.md §Spacing System. The schema's --space-5
|
||||
* (20px) and --space-12 (48px) fill in tiers DESIGN.md does not name
|
||||
* but the layout grid needs. */
|
||||
--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 rhythm ───────────────────────────────────────────────
|
||||
* Phone-first product — desktop rhythm stays modest (72px) and
|
||||
* collapses to 32px on phones to keep chat-row density on small
|
||||
* screens. */
|
||||
--section-y-desktop: 72px;
|
||||
--section-y-tablet: 48px;
|
||||
--section-y-phone: 32px;
|
||||
|
||||
/* ─── Radius ───────────────────────────────────────────────────────
|
||||
* DESIGN.md §Border Radius: 4 / 8 / 16 / pill. (The brand-specific
|
||||
* --radius-bubble lives in DESIGN.md but is component-local — chat
|
||||
* bubbles re-derive it inline so it does not enter the shared schema.) */
|
||||
--radius-sm: 4px;
|
||||
--radius-md: 8px;
|
||||
--radius-lg: 16px;
|
||||
--radius-pill: 9999px;
|
||||
|
||||
/* ─── Elevation ────────────────────────────────────────────────────
|
||||
* Whisper-quiet shadow lifted from DESIGN.md's incoming-bubble spec
|
||||
* (0 1px 2px rgba(0,0,0,0.06)). WeChat surfaces almost never float;
|
||||
* when they do, the shadow is barely perceptible. */
|
||||
--elev-flat: none;
|
||||
--elev-ring: 0 0 0 1px var(--border);
|
||||
--elev-raised: 0 1px 2px rgba(0, 0, 0, 0.06);
|
||||
|
||||
/* ─── Focus ────────────────────────────────────────────────────────
|
||||
* Green-tinted ring keeps the brand voice consistent on keyboard
|
||||
* focus across inputs, buttons, and links. */
|
||||
--focus-ring: 0 0 0 3px color-mix(in oklab, var(--accent), transparent 70%);
|
||||
|
||||
/* ─── Motion ───────────────────────────────────────────────────────
|
||||
* DESIGN.md §Motion: 100ms hover, 200ms chat-message entry. Easing
|
||||
* is the platform default (cubic-bezier(0.25, 0.1, 0.25, 1)) — the
|
||||
* classic browser "ease", chosen for muscle-memory familiarity. */
|
||||
--motion-fast: 100ms;
|
||||
--motion-base: 200ms;
|
||||
--ease-standard: cubic-bezier(0.25, 0.1, 0.25, 1);
|
||||
|
||||
/* ─── Layout ───────────────────────────────────────────────────────
|
||||
* 1200px container fits WeChat's marketing-site pattern; phone
|
||||
* gutters stay at 16px to match the chat row inset. */
|
||||
--container-max: 1200px;
|
||||
--container-gutter-desktop: 24px;
|
||||
--container-gutter-tablet: 16px;
|
||||
--container-gutter-phone: 16px;
|
||||
}
|
||||
865
design-systems/xiaohongshu/components.html
Normal file
865
design-systems/xiaohongshu/components.html
Normal file
|
|
@ -0,0 +1,865 @@
|
|||
<!doctype html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>小红书 / Xiaohongshu — reference components</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="Reference fixture for design-systems/xiaohongshu. Every visible
|
||||
value comes from tokens.css. 小红书 / RED signature moves: grey
|
||||
canvas + white card masonry, PingFang SC throughout, brand red
|
||||
used exactly once per screen as accent, full-pill CTAs, soft
|
||||
black text (rgba 0,0,0,.8), no shadow until hover."
|
||||
/>
|
||||
|
||||
<style>
|
||||
:root {
|
||||
--bg: #f5f5f5;
|
||||
--surface: #ffffff;
|
||||
--surface-warm: #fafafa;
|
||||
|
||||
--fg: rgba(0, 0, 0, 0.8);
|
||||
--fg-2: rgba(0, 0, 0, 0.62);
|
||||
--muted: rgba(0, 0, 0, 0.45);
|
||||
--meta: rgba(0, 0, 0, 0.27);
|
||||
|
||||
--border: rgba(0, 0, 0, 0.08);
|
||||
--border-soft: rgba(0, 0, 0, 0.05);
|
||||
|
||||
--accent: #ff2442;
|
||||
--accent-on: #ffffff;
|
||||
--accent-hover: #ff2e4d;
|
||||
--accent-active: #e6203a;
|
||||
|
||||
--success: #02b940;
|
||||
--warn: #ff7d03;
|
||||
--danger: #ff2442;
|
||||
|
||||
--font-display: "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans SC", -apple-system, "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
--font-body: "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans SC", -apple-system, "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
--font-mono: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Monaco, Consolas, monospace;
|
||||
|
||||
--text-xs: 12px;
|
||||
--text-sm: 14px;
|
||||
--text-base: 16px;
|
||||
--text-lg: 18px;
|
||||
--text-xl: 20px;
|
||||
--text-2xl: 24px;
|
||||
--text-3xl: 28px;
|
||||
--text-4xl: 32px;
|
||||
|
||||
--leading-body: 1.5;
|
||||
--leading-tight: 1.25;
|
||||
--tracking-display: 0;
|
||||
|
||||
--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: 64px;
|
||||
--section-y-tablet: 48px;
|
||||
--section-y-phone: 32px;
|
||||
|
||||
--radius-sm: 8px;
|
||||
--radius-md: 12px;
|
||||
--radius-lg: 16px;
|
||||
--radius-pill: 9999px;
|
||||
|
||||
--elev-flat: none;
|
||||
--elev-ring: 0 0 0 1px var(--border);
|
||||
--elev-raised: 0 4px 12px rgba(0, 0, 0, 0.08);
|
||||
|
||||
--focus-ring: 0 0 0 3px color-mix(in oklab, var(--accent), transparent 70%);
|
||||
|
||||
--motion-fast: 150ms;
|
||||
--motion-base: 200ms;
|
||||
--ease-standard: cubic-bezier(0.2, 0, 0, 1);
|
||||
|
||||
--container-max: 1200px;
|
||||
--container-gutter-desktop: 24px;
|
||||
--container-gutter-tablet: 16px;
|
||||
--container-gutter-phone: 12px;
|
||||
}
|
||||
|
||||
/* ─── 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 primitives ─────────────────────────────────── */
|
||||
.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-soft); }
|
||||
@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 — PingFang SC throughout, tracking 0 ───── */
|
||||
h1, h2, h3 {
|
||||
margin: 0;
|
||||
color: var(--fg);
|
||||
font-family: var(--font-display);
|
||||
line-height: var(--leading-tight);
|
||||
letter-spacing: var(--tracking-display);
|
||||
}
|
||||
h1 { font-size: var(--text-4xl); font-weight: 600; }
|
||||
h2 { font-size: var(--text-2xl); font-weight: 600; }
|
||||
h3 { font-size: var(--text-xl); font-weight: 600; }
|
||||
p { margin: 0; }
|
||||
.lead {
|
||||
font-size: var(--text-lg);
|
||||
color: var(--fg-2);
|
||||
line-height: var(--leading-body);
|
||||
font-weight: 400;
|
||||
}
|
||||
.body-muted { color: var(--fg-2); }
|
||||
.body-sm { font-size: var(--text-sm); }
|
||||
/* Eyebrow stays sentence-case (not uppercase) — Chinese voice */
|
||||
.eyebrow {
|
||||
font-size: var(--text-xs);
|
||||
color: var(--accent);
|
||||
font-weight: 500;
|
||||
font-family: var(--font-body);
|
||||
}
|
||||
.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); }
|
||||
|
||||
/* ─── Top nav — soft pill segmented ─────────────────────── */
|
||||
.topnav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding-block: var(--space-4);
|
||||
gap: var(--space-4);
|
||||
}
|
||||
.topnav-brand {
|
||||
font-family: var(--font-display);
|
||||
font-weight: 600;
|
||||
font-size: var(--text-lg);
|
||||
color: var(--fg);
|
||||
text-decoration: none;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--space-2);
|
||||
}
|
||||
.topnav-brand-mark {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: var(--radius-pill);
|
||||
background: var(--accent);
|
||||
color: var(--accent-on);
|
||||
font-size: var(--text-xs);
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.04em;
|
||||
}
|
||||
.topnav-tabs {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-1);
|
||||
}
|
||||
.topnav-tabs a {
|
||||
position: relative;
|
||||
padding: var(--space-2) var(--space-3);
|
||||
font-size: var(--text-sm);
|
||||
font-weight: 400;
|
||||
color: var(--muted);
|
||||
text-decoration: none;
|
||||
transition: color var(--motion-fast) var(--ease-standard);
|
||||
}
|
||||
.topnav-tabs a:hover { color: var(--fg-2); }
|
||||
.topnav-tabs a[aria-current="page"] {
|
||||
color: var(--fg);
|
||||
font-weight: 600;
|
||||
}
|
||||
/* DESIGN.md §4: tab indicator is a 2px underline at component-layer red, matched to text width */
|
||||
.topnav-tabs a[aria-current="page"]::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: var(--space-3);
|
||||
right: var(--space-3);
|
||||
bottom: 0;
|
||||
height: 2px;
|
||||
background: var(--accent-hover);
|
||||
border-radius: var(--radius-pill);
|
||||
}
|
||||
.topnav-right { display: flex; align-items: center; gap: var(--space-3); }
|
||||
|
||||
/* ─── Buttons — full pill, no shadow ────────────────────── */
|
||||
.btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--space-2);
|
||||
padding: 10px 20px;
|
||||
border-radius: var(--radius-pill);
|
||||
font-family: var(--font-body);
|
||||
font-size: var(--text-sm);
|
||||
font-weight: 500;
|
||||
line-height: 1;
|
||||
cursor: pointer;
|
||||
border: 1px solid transparent;
|
||||
text-decoration: none;
|
||||
transition: background-color var(--motion-fast) var(--ease-standard),
|
||||
color var(--motion-fast) var(--ease-standard),
|
||||
border-color var(--motion-fast) var(--ease-standard);
|
||||
}
|
||||
.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(--border);
|
||||
color: var(--fg);
|
||||
}
|
||||
.btn-secondary:hover { background: rgba(0, 0, 0, 0.12); }
|
||||
.btn-ghost {
|
||||
background: transparent;
|
||||
color: var(--fg);
|
||||
border-color: var(--border);
|
||||
}
|
||||
.btn-ghost:hover { background: var(--border-soft); }
|
||||
/* Follow CTA: feed-card variant per DESIGN.md §4 "Follow Button — three-state" */
|
||||
.btn-follow {
|
||||
padding: 6px 14px;
|
||||
font-size: var(--text-xs);
|
||||
font-weight: 500;
|
||||
border-radius: var(--radius-pill);
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
background: var(--accent);
|
||||
color: var(--accent-on);
|
||||
}
|
||||
.btn-follow[aria-pressed="true"] {
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
/* ─── Chips / tags — pill rectangle ─────────────────────── */
|
||||
.chip {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--space-2);
|
||||
padding: 4px 12px;
|
||||
border-radius: var(--radius-pill);
|
||||
font-size: var(--text-xs);
|
||||
font-weight: 400;
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
color: var(--fg-2);
|
||||
}
|
||||
.chip-trending {
|
||||
background: var(--accent);
|
||||
color: var(--accent-on);
|
||||
font-weight: 500;
|
||||
}
|
||||
.chip-success {
|
||||
background: color-mix(in oklab, var(--success), transparent 88%);
|
||||
color: var(--success);
|
||||
}
|
||||
.dot {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
background: currentColor;
|
||||
}
|
||||
|
||||
/* ─── Cards — 12px radius, flat by default ──────────────── */
|
||||
.card {
|
||||
background: var(--surface);
|
||||
border-radius: var(--radius-md);
|
||||
padding: var(--space-6);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-3);
|
||||
transition: box-shadow var(--motion-base) var(--ease-standard),
|
||||
transform var(--motion-base) var(--ease-standard);
|
||||
}
|
||||
/* DESIGN.md §6 "Subtle (1)" — PC card hover only */
|
||||
.card:hover {
|
||||
box-shadow: var(--elev-raised);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
/* ─── Feed card — image flush, footer with heart + count ─ */
|
||||
.feed-card {
|
||||
background: var(--surface);
|
||||
border-radius: var(--radius-md);
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
transition: box-shadow var(--motion-base) var(--ease-standard),
|
||||
transform var(--motion-base) var(--ease-standard);
|
||||
}
|
||||
.feed-card:hover {
|
||||
box-shadow: var(--elev-raised);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
.feed-card-cover {
|
||||
width: 100%;
|
||||
aspect-ratio: 3 / 4;
|
||||
display: block;
|
||||
background:
|
||||
linear-gradient(135deg,
|
||||
color-mix(in oklab, var(--accent), white 80%),
|
||||
color-mix(in oklab, var(--accent), white 50%));
|
||||
position: relative;
|
||||
}
|
||||
.feed-card-cover-tag {
|
||||
position: absolute;
|
||||
top: var(--space-3);
|
||||
left: var(--space-3);
|
||||
padding: 4px 10px;
|
||||
border-radius: var(--radius-pill);
|
||||
background: rgba(0, 0, 0, 0.45);
|
||||
color: #ffffff;
|
||||
font-size: var(--text-xs);
|
||||
font-weight: 500;
|
||||
backdrop-filter: blur(4px);
|
||||
}
|
||||
.feed-card-body {
|
||||
padding: var(--space-3);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-3);
|
||||
}
|
||||
.feed-card-title {
|
||||
font-size: var(--text-sm);
|
||||
font-weight: 500;
|
||||
color: var(--fg);
|
||||
line-height: 1.43;
|
||||
margin: 0;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
.feed-card-foot {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-2);
|
||||
font-size: var(--text-xs);
|
||||
color: var(--muted);
|
||||
}
|
||||
.feed-card-avatar {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: var(--radius-pill);
|
||||
background: color-mix(in oklab, var(--accent), white 70%);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.feed-card-author {
|
||||
flex: 1;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
color: var(--fg-2);
|
||||
}
|
||||
.feed-card-likes {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
color: var(--muted);
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.feed-card-likes .icon { color: var(--muted); }
|
||||
.feed-card-likes[data-liked="true"] .icon { color: var(--accent); fill: var(--accent); }
|
||||
|
||||
/* ─── Inputs — F5F5F5 fill, pill, no border ─────────────── */
|
||||
.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, .field textarea {
|
||||
background: var(--bg);
|
||||
color: var(--fg);
|
||||
border: 1px solid transparent;
|
||||
border-radius: var(--radius-pill);
|
||||
padding: 10px 16px;
|
||||
font-family: var(--font-body);
|
||||
font-size: var(--text-sm);
|
||||
outline: none;
|
||||
transition: border-color var(--motion-fast) var(--ease-standard),
|
||||
box-shadow var(--motion-fast) var(--ease-standard),
|
||||
background-color var(--motion-fast) var(--ease-standard);
|
||||
}
|
||||
.field textarea {
|
||||
border-radius: var(--radius-md);
|
||||
padding: var(--space-3) var(--space-4);
|
||||
resize: vertical;
|
||||
min-height: 88px;
|
||||
font-family: var(--font-body);
|
||||
}
|
||||
.field input::placeholder, .field textarea::placeholder { color: var(--meta); }
|
||||
.field input:focus, .field textarea:focus {
|
||||
background: var(--surface);
|
||||
border-color: var(--border);
|
||||
box-shadow: var(--focus-ring);
|
||||
}
|
||||
.field-help { font-size: var(--text-xs); color: var(--muted); }
|
||||
|
||||
/* ─── Hero meta sidecar (soft pink-tinted card) ─────────── */
|
||||
.hero-meta {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-4);
|
||||
padding: var(--space-6);
|
||||
border-radius: var(--radius-lg);
|
||||
/* Soft pink-tinted surface — derived from accent at low alpha,
|
||||
a brand-friendly tint without introducing a new token. */
|
||||
background: color-mix(in oklab, var(--accent), white 92%);
|
||||
}
|
||||
.hero-meta-title {
|
||||
font-size: var(--text-sm);
|
||||
font-weight: 600;
|
||||
color: var(--fg);
|
||||
margin: 0;
|
||||
}
|
||||
.hero-meta-stats {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: var(--space-3);
|
||||
}
|
||||
.hero-meta-stat {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
}
|
||||
.hero-meta-stat-num {
|
||||
font-size: var(--text-xl);
|
||||
font-weight: 600;
|
||||
color: var(--fg);
|
||||
font-variant-numeric: tabular-nums;
|
||||
line-height: 1.2;
|
||||
}
|
||||
.hero-meta-stat-label {
|
||||
font-size: var(--text-xs);
|
||||
color: var(--muted);
|
||||
}
|
||||
.hero-meta-foot {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: var(--space-3);
|
||||
padding-block-start: var(--space-3);
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
/* ─── Section layouts ───────────────────────────────────── */
|
||||
.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;
|
||||
}
|
||||
.features-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: var(--space-5);
|
||||
margin-block-start: var(--space-8);
|
||||
}
|
||||
@media (max-width: 1023px) { .features-grid { grid-template-columns: 1fr 1fr; } }
|
||||
@media (max-width: 639px) { .features-grid { grid-template-columns: 1fr; } }
|
||||
.feed-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: var(--space-3);
|
||||
margin-block-start: var(--space-6);
|
||||
}
|
||||
@media (max-width: 1023px) { .feed-grid { grid-template-columns: repeat(3, 1fr); } }
|
||||
@media (max-width: 639px) { .feed-grid { grid-template-columns: repeat(2, 1fr); } }
|
||||
.form-row {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 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);
|
||||
padding: var(--space-6);
|
||||
background: var(--surface);
|
||||
border-radius: var(--radius-lg);
|
||||
max-width: 480px;
|
||||
}
|
||||
.form-actions {
|
||||
display: flex;
|
||||
gap: var(--space-3);
|
||||
margin-block-start: var(--space-2);
|
||||
}
|
||||
.icon { width: 16px; height: 16px; flex-shrink: 0; }
|
||||
.icon-lg { width: 18px; height: 18px; flex-shrink: 0; }
|
||||
.row-between {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: var(--space-3);
|
||||
}
|
||||
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);
|
||||
}
|
||||
code {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 0.875em;
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
border-radius: var(--radius-sm);
|
||||
padding: 1px 6px;
|
||||
color: var(--fg);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header class="container">
|
||||
<nav class="topnav" aria-label="Primary">
|
||||
<a class="topnav-brand" href="./DESIGN.md">
|
||||
<span class="topnav-brand-mark" aria-hidden="true">RED</span>
|
||||
<span>灵感参考</span>
|
||||
</a>
|
||||
<div class="topnav-tabs" role="tablist">
|
||||
<a href="#" role="tab" aria-current="page">发现</a>
|
||||
<a href="#" role="tab">关注</a>
|
||||
<a href="#" role="tab">附近</a>
|
||||
<a href="#" role="tab">购物</a>
|
||||
</div>
|
||||
<div class="topnav-right">
|
||||
<a href="#" class="body-sm" style="color: var(--fg-2); text-decoration: none; font-weight: 500;">登录</a>
|
||||
<a href="#" class="btn btn-primary">发布笔记</a>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main class="container">
|
||||
<!-- HERO — 种草 voice, pill CTA, soft pink-tinted sidecar. -->
|
||||
<section data-od-id="hero">
|
||||
<div class="hero-grid">
|
||||
<div class="stack-4">
|
||||
<p class="eyebrow">灵感参考 · xiaohongshu</p>
|
||||
<h1>标记你的生活,<br />让世界被你种草。</h1>
|
||||
<p class="lead" style="max-width: 30ch;">
|
||||
你今天的早餐、出差住的酒店、买了三支才挑对的口红 —
|
||||
都是别人正在寻找的灵感。Show up. 被看见。被收藏。
|
||||
</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="1.75" 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-ghost">阅读设计语言</a>
|
||||
</div>
|
||||
<p class="body-sm body-muted" style="margin-block-start: var(--space-3);">
|
||||
按 <kbd>⌘</kbd> <kbd>K</kbd> 搜索笔记 · Press <kbd>⌘</kbd> <kbd>K</kbd> to search.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<aside class="hero-meta" aria-label="今日数据 · Today on RED">
|
||||
<div class="row-between">
|
||||
<p class="hero-meta-title">今日小红书 · Today on RED</p>
|
||||
<span class="chip chip-trending">
|
||||
<span class="dot" aria-hidden="true"></span>
|
||||
热门
|
||||
</span>
|
||||
</div>
|
||||
<div class="hero-meta-stats">
|
||||
<div class="hero-meta-stat">
|
||||
<span class="hero-meta-stat-num">3.2亿</span>
|
||||
<span class="hero-meta-stat-label">月活用户</span>
|
||||
</div>
|
||||
<div class="hero-meta-stat">
|
||||
<span class="hero-meta-stat-num">8,400万</span>
|
||||
<span class="hero-meta-stat-label">日均笔记</span>
|
||||
</div>
|
||||
<div class="hero-meta-stat">
|
||||
<span class="hero-meta-stat-num">72%</span>
|
||||
<span class="hero-meta-stat-label">女性用户</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="hero-meta-foot">
|
||||
<span class="body-sm" style="color: var(--muted);">
|
||||
Source · xiaohongshu.com · <time datetime="2026-05-18">2026.05</time>
|
||||
</span>
|
||||
<a href="#" class="body-sm" style="color: var(--accent); text-decoration: none; font-weight: 500;">查看 →</a>
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- DISCOVER — masonry-style feed mock with bilingual titles. -->
|
||||
<section data-od-id="discover">
|
||||
<div class="row-between" style="flex-wrap: wrap; gap: var(--space-3);">
|
||||
<div class="stack-2">
|
||||
<p class="eyebrow">今日发现 · Discover</p>
|
||||
<h2>看看大家正在种草什么</h2>
|
||||
</div>
|
||||
<div style="display: flex; gap: var(--space-2); flex-wrap: wrap;">
|
||||
<span class="chip chip-trending">穿搭</span>
|
||||
<span class="chip">咖啡</span>
|
||||
<span class="chip">旅行</span>
|
||||
<span class="chip">护肤</span>
|
||||
<span class="chip">家居</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="feed-grid">
|
||||
<article class="feed-card">
|
||||
<div class="feed-card-cover" aria-hidden="true">
|
||||
<span class="feed-card-cover-tag">视频</span>
|
||||
</div>
|
||||
<div class="feed-card-body">
|
||||
<h3 class="feed-card-title" style="font-family: var(--font-body);">
|
||||
周末和朋友的 brunch 小记 · A slow Saturday in Shanghai
|
||||
</h3>
|
||||
<div class="feed-card-foot">
|
||||
<span class="feed-card-avatar" aria-hidden="true"></span>
|
||||
<span class="feed-card-author">小满 · xiaoman</span>
|
||||
<span class="feed-card-likes" data-liked="true" aria-label="2.3 万 likes">
|
||||
<svg class="icon" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
|
||||
<path d="M12 21s-7-4.5-9.5-9C.9 8.8 2.7 5 6 5c2 0 3.3 1 4 2.3C10.7 6 12 5 14 5c3.3 0 5.1 3.8 3.5 7-2.5 4.5-9.5 9-9.5 9z"/>
|
||||
</svg>
|
||||
2.3w
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
<article class="feed-card">
|
||||
<div class="feed-card-cover" aria-hidden="true" style="aspect-ratio: 1 / 1;"></div>
|
||||
<div class="feed-card-body">
|
||||
<h3 class="feed-card-title" style="font-family: var(--font-body);">
|
||||
租房 50㎡ 的暖色客厅改造 — before / after 全记录
|
||||
</h3>
|
||||
<div class="feed-card-foot">
|
||||
<span class="feed-card-avatar" aria-hidden="true" style="background: color-mix(in oklab, var(--success), white 70%);"></span>
|
||||
<span class="feed-card-author">桃子家的小屋</span>
|
||||
<span class="feed-card-likes" aria-label="8,712 likes">
|
||||
<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
||||
<path d="M12 21s-7-4.5-9.5-9C.9 8.8 2.7 5 6 5c2 0 3.3 1 4 2.3C10.7 6 12 5 14 5c3.3 0 5.1 3.8 3.5 7-2.5 4.5-9.5 9-9.5 9z"/>
|
||||
</svg>
|
||||
8,712
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
<article class="feed-card">
|
||||
<div class="feed-card-cover" aria-hidden="true" style="aspect-ratio: 4 / 5; background: linear-gradient(160deg, color-mix(in oklab, var(--accent), white 60%), color-mix(in oklab, var(--warn), white 70%));"></div>
|
||||
<div class="feed-card-body">
|
||||
<h3 class="feed-card-title" style="font-family: var(--font-body);">
|
||||
第三支才挑对 · 显白程度十级的口红种草清单
|
||||
</h3>
|
||||
<div class="feed-card-foot">
|
||||
<span class="feed-card-avatar" aria-hidden="true" style="background: color-mix(in oklab, var(--warn), white 60%);"></span>
|
||||
<span class="feed-card-author">林深 · lin.shen</span>
|
||||
<span class="feed-card-likes" data-liked="true" aria-label="1.1 万 likes">
|
||||
<svg class="icon" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
|
||||
<path d="M12 21s-7-4.5-9.5-9C.9 8.8 2.7 5 6 5c2 0 3.3 1 4 2.3C10.7 6 12 5 14 5c3.3 0 5.1 3.8 3.5 7-2.5 4.5-9.5 9-9.5 9z"/>
|
||||
</svg>
|
||||
1.1w
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
<article class="feed-card">
|
||||
<div class="feed-card-cover" aria-hidden="true" style="aspect-ratio: 3 / 4; background: linear-gradient(200deg, color-mix(in oklab, var(--success), white 60%), color-mix(in oklab, var(--success), white 88%));">
|
||||
<span class="feed-card-cover-tag">长图</span>
|
||||
</div>
|
||||
<div class="feed-card-body">
|
||||
<h3 class="feed-card-title" style="font-family: var(--font-body);">
|
||||
京都早春三日 · 在小巷里慢慢迷路也很好
|
||||
</h3>
|
||||
<div class="feed-card-foot">
|
||||
<span class="feed-card-avatar" aria-hidden="true" style="background: color-mix(in oklab, var(--success), white 50%);"></span>
|
||||
<span class="feed-card-author">远野日记 · tono</span>
|
||||
<span class="feed-card-likes" aria-label="6,304 likes">
|
||||
<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
||||
<path d="M12 21s-7-4.5-9.5-9C.9 8.8 2.7 5 6 5c2 0 3.3 1 4 2.3C10.7 6 12 5 14 5c3.3 0 5.1 3.8 3.5 7-2.5 4.5-9.5 9-9.5 9z"/>
|
||||
</svg>
|
||||
6,304
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- FEATURES — three guiding principles, flat cards, hover lift. -->
|
||||
<section data-od-id="features">
|
||||
<div class="stack-3">
|
||||
<p class="eyebrow">为什么这样设计 · Why it looks like this</p>
|
||||
<h2 style="max-width: 22ch;">
|
||||
克制做底色,内容才是主角。
|
||||
</h2>
|
||||
<p class="lead" style="max-width: 56ch;">
|
||||
一个 <code>--accent</code>,一种 <code>PingFang SC</code>,
|
||||
一套 <code>rgba(0,0,0,*)</code> 的浮层灰阶 —
|
||||
其余空间,全部交给图片本身。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="features-grid">
|
||||
<article class="card">
|
||||
<span class="chip chip-trending" style="align-self: flex-start;">
|
||||
<span class="dot" aria-hidden="true"></span>
|
||||
品牌唯一红
|
||||
</span>
|
||||
<h3>一屏只用一次的红</h3>
|
||||
<p class="body-muted body-sm">
|
||||
<code>#ff2442</code> 只出现在主要 CTA、心形高亮、
|
||||
热门标签上。同一个页面里出现两次饱和红,画面就立刻
|
||||
"降级"成了打折海报,而不是一张笔记。
|
||||
</p>
|
||||
<a href="./tokens.css" class="body-sm" style="color: var(--accent); text-decoration: none; font-weight: 500;">查看 token →</a>
|
||||
</article>
|
||||
|
||||
<article class="card">
|
||||
<span class="chip" style="align-self: flex-start;">柔黑文字</span>
|
||||
<h3>从不使用纯黑</h3>
|
||||
<p class="body-muted body-sm">
|
||||
标题 <code>rgba(0,0,0,.8)</code>、正文 <code>.62</code>、
|
||||
描述 <code>.45</code>、占位 <code>.27</code> —
|
||||
四级透明黑替代离散灰阶,同一个浮层落在白卡、照片、
|
||||
抽屉上都成立。
|
||||
</p>
|
||||
<a href="./DESIGN.md" class="body-sm" style="color: var(--accent); text-decoration: none; font-weight: 500;">阅读规则 →</a>
|
||||
</article>
|
||||
|
||||
<article class="card">
|
||||
<span class="chip" style="align-self: flex-start;">圆得彻底</span>
|
||||
<h3>能按的,一定是 pill</h3>
|
||||
<p class="body-muted body-sm">
|
||||
卡片 <code>12px</code>、按钮 <code>9999px</code>。
|
||||
一个直角按钮在这套系统里立刻显得不对 —
|
||||
层次靠间距和圆角,不靠投影。Shadow stays
|
||||
off until hover earns it.
|
||||
</p>
|
||||
<a href="./tokens.css" class="body-sm" style="color: var(--accent); text-decoration: none; font-weight: 500;">检查圆角 →</a>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- FORM — pill input on F5F5F5, primary red CTA, follow CTA demo. -->
|
||||
<section data-od-id="form">
|
||||
<div class="form-row">
|
||||
<div class="stack-4">
|
||||
<p class="eyebrow">订阅每日灵感 · Daily inspo digest</p>
|
||||
<h2>每天一封 · 100 张被收藏最多的笔记。</h2>
|
||||
<p class="lead" style="max-width: 44ch;">
|
||||
No spam,no third-party,no enterprise tone.
|
||||
一个邮箱,一个发布频率,仅此而已 —
|
||||
我们用 <code>--focus-ring</code> 的 RED 红圈
|
||||
提示你正在专注的输入框。
|
||||
</p>
|
||||
<ul style="margin: 0; padding-inline-start: var(--space-5); color: var(--fg-2); font-size: var(--text-sm);" class="stack-3">
|
||||
<li>输入框背景使用 <code>--bg</code>(#F5F5F5),focus 时升到 <code>--surface</code>。</li>
|
||||
<li>提交按钮即 <code>.btn-primary</code> — 系统里只有一种红 CTA。</li>
|
||||
<li>占位符使用 <code>--meta</code>,0.27 透明度的最弱一档。</li>
|
||||
</ul>
|
||||
<div class="row-between" style="margin-block-start: var(--space-4); max-width: 360px;">
|
||||
<div class="stack-2" style="display: flex; flex-direction: column;">
|
||||
<span class="body-sm" style="color: var(--fg); font-weight: 500;">关注按钮三态</span>
|
||||
<span class="body-sm" style="color: var(--muted);">DESIGN.md §4 · Follow Button</span>
|
||||
</div>
|
||||
<div style="display: flex; gap: var(--space-2);">
|
||||
<button type="button" class="btn-follow">+ 关注</button>
|
||||
<button type="button" class="btn-follow" aria-pressed="true">已关注</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form class="form" onsubmit="event.preventDefault();" aria-label="订阅每日灵感">
|
||||
<div class="row-between">
|
||||
<p style="font-size: var(--text-sm); font-weight: 600; color: var(--fg); margin: 0;">订阅每日灵感</p>
|
||||
<span class="chip chip-success">
|
||||
<span class="dot" aria-hidden="true"></span>
|
||||
Free · 免费
|
||||
</span>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="nickname">你的昵称 · Nickname</label>
|
||||
<input
|
||||
id="nickname"
|
||||
type="text"
|
||||
placeholder="给未来的自己留个名字"
|
||||
autocomplete="nickname"
|
||||
/>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="email">邮箱 · Email</label>
|
||||
<input
|
||||
id="email"
|
||||
type="email"
|
||||
placeholder="you@example.com"
|
||||
autocomplete="email"
|
||||
required
|
||||
/>
|
||||
<p class="field-help">仅用于发送每日灵感清单,可随时退订。</p>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="topic">最想被种草的主题 · What to be inspired by</label>
|
||||
<textarea
|
||||
id="topic"
|
||||
placeholder="例如:周末 brunch、出租屋改造、本子文具、晨跑路线…"
|
||||
></textarea>
|
||||
</div>
|
||||
<div class="form-actions">
|
||||
<button type="submit" class="btn btn-primary">
|
||||
立即订阅
|
||||
<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
||||
stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
||||
<path d="M5 12h14M13 6l6 6-6 6"/>
|
||||
</svg>
|
||||
</button>
|
||||
<button type="button" class="btn btn-ghost">看示例</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
193
design-systems/xiaohongshu/tokens.css
Normal file
193
design-systems/xiaohongshu/tokens.css
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
/* ─────────────────────────────────────────────────────────────────────────
|
||||
* design-systems/xiaohongshu/tokens.css
|
||||
*
|
||||
* Structured token bindings for "Design System Inspired by Xiaohongshu"
|
||||
* (小红书 / RED) — the Chinese lifestyle UGC discovery platform.
|
||||
*
|
||||
* The visual baseline is *daily-ness*: a near-white masonry canvas that
|
||||
* functions as a transparent picture frame for user-uploaded breakfasts,
|
||||
* lipstick swatches, and hotel sofas. Brand red (`#FF2442`) is the
|
||||
* single saturated voice the system permits — one accent per screen,
|
||||
* never two. Neutrals are translucent overlays (`rgba(0,0,0, .05/.08/
|
||||
* .27/.45/.62/.80)`) rather than discrete grey steps so the same fill
|
||||
* lands believably on white card, photo, or sheet.
|
||||
*
|
||||
* Brand identity in three sentences:
|
||||
* 1. Canvas is `#F5F5F5`; the white card (`#FFFFFF`) lifts above it
|
||||
* by color contrast, not by elevation. Shadows are essentially
|
||||
* absent — depth comes from rounding and spacing.
|
||||
* 2. Type is PingFang SC throughout (no separate display face) with
|
||||
* the documented Hiragino Sans GB / Microsoft YaHei / Noto Sans SC
|
||||
* fallback chain so 标记我的生活 reads the same on macOS, Windows,
|
||||
* and Linux. Tracking is zero everywhere; the heading scale caps
|
||||
* at 32px per DESIGN.md §3 ("no 48/64 hero").
|
||||
* 3. Brand red is the singular accent — used on CTAs, hearts, active
|
||||
* tabs, and tag-on-trending. Danger has no independent token in
|
||||
* RED's production system, so `--danger` aliases to the same red
|
||||
* (DESIGN.md §2: "danger reuses --primary"). Skill authors should
|
||||
* differentiate destructive intent via outline + leading icon when
|
||||
* the difference matters.
|
||||
*
|
||||
* Schema decisions:
|
||||
* - --bg: #f5f5f5 (canvas behind cards); --surface: #ffffff (card).
|
||||
* - --surface-warm: #fafafa (info background, DESIGN.md --bg0-lighter).
|
||||
* - --fg: rgba(0, 0, 0, 0.8) (title — soft black, never #000);
|
||||
* --fg-2: rgba(0, 0, 0, 0.62) (paragraph); --muted: 0.45 (caption);
|
||||
* --meta: rgba(0, 0, 0, 0.27) (disabled / placeholder).
|
||||
* - --border: rgba(0, 0, 0, 0.08) (hairline separator);
|
||||
* --border-soft: rgba(0, 0, 0, 0.05) (fill1 — group line).
|
||||
* - --accent: #ff2442 (token red, default); --accent-hover: #ff2e4d
|
||||
* (component-layer red — slightly pinker / lighter, doubles as the
|
||||
* natural hover state for solid-fill primary CTAs).
|
||||
* - --danger: #ff2442 (reuses brand red — DESIGN.md §2 explicit).
|
||||
* - Font stacks lead with PingFang SC then the CJK fallback chain —
|
||||
* RED is CJK-primary, so Latin fallbacks (`-apple-system`, etc.)
|
||||
* come after.
|
||||
* - Type scale ceiling at 32px (--text-4xl). DESIGN.md §3 forbids
|
||||
* 48/64 hero type; --text-3xl bumps to 28px so the marketing
|
||||
* hero has a usable intermediate above section headings.
|
||||
* - --radius-md: 12px (feed card), --radius-lg: 16px (feature card),
|
||||
* --radius-pill: 9999px (buttons are pill, always).
|
||||
* - --elev-raised: 0 4px 12px rgba(0, 0, 0, 0.08) — DESIGN.md §6
|
||||
* "Subtle (1)", used only on PC card hover in the live system.
|
||||
* ─────────────────────────────────────────────────────────────────── */
|
||||
|
||||
:root {
|
||||
/* ─── Surface ──────────────────────────────────────────────────────
|
||||
* Canvas → Card → Subtle. The grey canvas IS the separation device
|
||||
* for white cards; cards do not need shadows to feel lifted. */
|
||||
--bg: #f5f5f5; /* Canvas — page background behind cards (DESIGN.md --bg0) */
|
||||
--surface: #ffffff; /* Surface — the white card / modal (DESIGN.md --bg) */
|
||||
--surface-warm: #fafafa; /* Subtle — information background (DESIGN.md --bg0-lighter) */
|
||||
|
||||
/* ─── Foreground ───────────────────────────────────────────────────
|
||||
* Four translucent black tiers. Pure #000 is never used for text;
|
||||
* even the title sits at 0.8 alpha so it reads as "soft black"
|
||||
* against the white card. */
|
||||
--fg: rgba(0, 0, 0, 0.8); /* Title — DESIGN.md --title */
|
||||
--fg-2: rgba(0, 0, 0, 0.62); /* Paragraph — DESIGN.md --paragraph */
|
||||
--muted: rgba(0, 0, 0, 0.45); /* Description — DESIGN.md --description */
|
||||
--meta: rgba(0, 0, 0, 0.27); /* Disabled / placeholder — DESIGN.md --disabled */
|
||||
|
||||
/* ─── Border ───────────────────────────────────────────────────────
|
||||
* Hairlines via alpha — the same line drops onto white card, onto
|
||||
* photo, or onto a sheet without recoloring. No opaque grey borders
|
||||
* by default. */
|
||||
--border: rgba(0, 0, 0, 0.08); /* Separator hairline — DESIGN.md --separator */
|
||||
--border-soft: rgba(0, 0, 0, 0.05); /* Fill1 — group / row line that should not visually compete */
|
||||
|
||||
/* ─── Accent ───────────────────────────────────────────────────────
|
||||
* One saturated voice for the whole system. The hover slot maps to
|
||||
* the documented component-layer red (#FF2E4D, slightly pinker /
|
||||
* lighter) — the same divergence that appears on .reds-button-new
|
||||
* .primary in production, repurposed as the natural hover state for
|
||||
* a token-driven primary CTA. */
|
||||
--accent: #ff2442; /* RED brand red (DESIGN.md --primary / --color-red) */
|
||||
--accent-on: #ffffff; /* White text on red CTA */
|
||||
--accent-hover: #ff2e4d; /* Component-layer red — slightly pinker; reads as a lift on hover */
|
||||
--accent-active: #e6203a; /* Pressed — darker, more saturated red */
|
||||
|
||||
/* ─── Semantic ─────────────────────────────────────────────────────
|
||||
* Success / warn lifted from DESIGN.md §2 (the platform's
|
||||
* --success / --warning tokens; nearly invisible in the consumer
|
||||
* flow but tokenized for B2B console contexts). Danger has no
|
||||
* independent token in RED — it reuses --primary; aliasing here
|
||||
* preserves that explicit DESIGN.md decision. */
|
||||
--success: #02b940; /* DESIGN.md --success (#EAF8EF bg variant lives in components) */
|
||||
--warn: #ff7d03; /* DESIGN.md --warning */
|
||||
--danger: #ff2442; /* Reuses brand red — DESIGN.md §2 "danger reuses --primary" */
|
||||
|
||||
/* ─── Typography ───────────────────────────────────────────────────
|
||||
* CJK-primary brand: PingFang SC leads, then the documented
|
||||
* Hiragino Sans GB / Microsoft YaHei / Noto Sans SC chain, then
|
||||
* the Apple/Helvetica Latin fallback. No separate display face —
|
||||
* every Typography-FontFamily-* in production resolves to PingFang SC.
|
||||
* --font-mono inherits the schema default (RED Number is intentionally
|
||||
* NOT referenced here per DESIGN.md §3 — end users don't have it). */
|
||||
--font-display: "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans SC", -apple-system, "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
--font-body: "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans SC", -apple-system, "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
--font-mono: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Monaco, Consolas, monospace;
|
||||
|
||||
/* Type scale — DESIGN.md §3 PC web tokens (h1=32 → c2=12).
|
||||
* The ceiling is deliberately compact: DESIGN.md forbids 48/64 hero
|
||||
* type, so --text-4xl tops out at 32px. --text-3xl interpolates at
|
||||
* 28px so a marketing hero has visual lift above section headings. */
|
||||
--text-xs: 12px; /* c2 — small caption */
|
||||
--text-sm: 14px; /* b2 / t3 — body standard, secondary label */
|
||||
--text-base: 16px; /* b1 / t2 — body large, the marketing reading size */
|
||||
--text-lg: 18px; /* t1 — strong label */
|
||||
--text-xl: 20px; /* h3 — card heading */
|
||||
--text-2xl: 24px; /* h2 — section heading */
|
||||
--text-3xl: 28px; /* intermediate display — between h1 and h2 */
|
||||
--text-4xl: 32px; /* h1 — display ceiling (DESIGN.md "no 48/64 hero") */
|
||||
|
||||
/* Leading + tracking — DESIGN.md §3.
|
||||
* Body rides on 1.5 (24px on 16px b1). Headings tighten to 1.25
|
||||
* (40px on 32px h1). Tracking is 0 everywhere; only component-level
|
||||
* Chinese overrides hand-tune -0.3 to -0.64px and that stays out
|
||||
* of the token system. */
|
||||
--leading-body: 1.5;
|
||||
--leading-tight: 1.25;
|
||||
--tracking-display: 0; /* DESIGN.md: "Every --Typography-Spacing-* token is 0" */
|
||||
|
||||
/* ─── Spacing ──────────────────────────────────────────────────────
|
||||
* 8pt grid, stops 4 / 8 / 12 / 16 / 20 / 24 / 32 / 48 per DESIGN.md
|
||||
* §5. Section-level gaps (48 / 64) live in the rhythm tier below. */
|
||||
--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 rhythm ───────────────────────────────────────────────
|
||||
* The masonry feed itself is dense (cards 7–10px apart) but section
|
||||
* gaps on marketing surfaces breathe at 48–64px desktop, stepping
|
||||
* down to 32px on phone where vertical real estate is scarce. */
|
||||
--section-y-desktop: 64px;
|
||||
--section-y-tablet: 48px;
|
||||
--section-y-phone: 32px;
|
||||
|
||||
/* ─── Radius ───────────────────────────────────────────────────────
|
||||
* Generous everywhere: 8px on inputs and tags, 12px on feed cards,
|
||||
* 16px on feature / hero cards, full pill on every button. The
|
||||
* pill radius is functionally mandatory — a square-cornered CTA
|
||||
* reads as wrong in this brand. */
|
||||
--radius-sm: 8px; /* Inputs, tags, small chips */
|
||||
--radius-md: 12px; /* Feed card (DESIGN.md §4 "Cards") */
|
||||
--radius-lg: 16px; /* Larger feature card, bottom-sheet top corners */
|
||||
--radius-pill: 9999px; /* Buttons — always */
|
||||
|
||||
/* ─── Elevation ────────────────────────────────────────────────────
|
||||
* Three sanctioned levels, used sparingly. Default is flat — depth
|
||||
* comes from canvas color contrast and rounding. The raised tier
|
||||
* lands on PC card hover only; modal shadow is one-off on .modal
|
||||
* and stays in components. */
|
||||
--elev-flat: none;
|
||||
--elev-ring: 0 0 0 1px var(--border);
|
||||
--elev-raised: 0 4px 12px rgba(0, 0, 0, 0.08); /* DESIGN.md §6 "Subtle (1)" — PC card hover */
|
||||
|
||||
/* ─── Focus ────────────────────────────────────────────────────────
|
||||
* Brand red at 30% opacity. The schema default formula works for
|
||||
* the RED accent without override. */
|
||||
--focus-ring: 0 0 0 3px color-mix(in oklab, var(--accent), transparent 70%);
|
||||
|
||||
/* ─── Motion ───────────────────────────────────────────────────────
|
||||
* Standard durations. RED's interaction language is soft and quiet;
|
||||
* no aggressive bouncing curves. Defaults are appropriate. */
|
||||
--motion-fast: 150ms;
|
||||
--motion-base: 200ms;
|
||||
--ease-standard: cubic-bezier(0.2, 0, 0, 1);
|
||||
|
||||
/* ─── Layout ───────────────────────────────────────────────────────
|
||||
* 1200px container width — comfortably wider than the note-detail
|
||||
* two-pane (~1100px) for marketing surfaces. Phone gutter is tighter
|
||||
* (12px) so the masonry feed runs nearly edge-to-edge per DESIGN.md
|
||||
* §5 "Mobile Two-Column". */
|
||||
--container-max: 1200px;
|
||||
--container-gutter-desktop: 24px;
|
||||
--container-gutter-tablet: 16px;
|
||||
--container-gutter-phone: 12px;
|
||||
}
|
||||
Loading…
Reference in a new issue