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:
chaoxiaoche 2026-05-18 15:40:52 +08:00 committed by GitHub
parent 0026dfa344
commit 336620e06f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 8062 additions and 0 deletions

View 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. 110, 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>

View 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.151.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.080.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
* 4251664px 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.151.25 band.
* `--tracking-display` is `0` because DESIGN.md §3 explicitly
* lists display headings as `letter-spacing: normal`; positive
* body tracking (0.080.28px) is applied per-component. */
--leading-body: 1.35;
--leading-tight: 1.2;
--tracking-display: 0;
/* Spacing
* 8px base unit per DESIGN.md §5 (148px range). The 4/8/12/16/
* 20/24/32/48 tier covers structural rhythm; finer sub-tier
* increments (13px 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 4251664px 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;
}

View 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>

View 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 500600
* - 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 300900 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 12801440" 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;
}

View 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", 812px 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>

View 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: 2432px 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 412px 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;
}

View 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 1228px 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, " 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, " 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 &amp; 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 &amp; 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&rsquo;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>

View 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 1228px 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
* (ヒラギノ角ゴ / メイリオ / 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, " 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, " 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;
}

View 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>

View 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 2254px 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 2254px;
* 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 2254px */
--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;
}

View 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&#8209;planetary.</h1>
<p class="lead">
From low&#8209;Earth orbit to the surface of Mars — we build the
rockets, the spacecraft, and the operating cadence that turn
humanity into a multi&#8209;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&#8209;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&#8209;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&#8209;entry telemetry.</h2>
<p class="body-muted" style="text-transform: uppercase; letter-spacing: 0.02em; max-width: 48ch;">
We&#8217;ll send pre&#8209;flight readiness reviews and
post&#8209;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>

View 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.51.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;
}

View 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&nbsp;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>

View 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 23 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;
}

View 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>

View 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;
}

View 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>

View 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 (1118px). 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;
}

View 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 spamno third-partyno 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>#F5F5F5focus 时升到 <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>

View 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 710px apart) but section
* gaps on marketing surfaces breathe at 4864px 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;
}