Files
workbuddy_skills/open-design/design-systems/stripe/components.html
T
admin 5afeb6b78a ④段整套同步(六节 + P/S/F/J + 模板)+ open-design 移到技能根
一、④段整套同步(用户选候选 A)
- 新增 `references/stage-proto-doc/assets/产品文档模板.html`(16 KB/247 行,从备份拷、转 LF)。
- ④段 SKILL.md 换成备份 15:28 那版,并按模板口径改齐:
  · 「产品文档框架」从「四块+一附录」改成**六节** —— 页面结构/功能清单/使用场景/操作流程/
    跨页与界面状态/待确认与偏离;与模板同构,⛔ 不从空白页重写结构。
  · 编号从 R/S/F 改成 **P 页面/S 场景/F 功能/J 二级功能**,各自唯一、按展示顺序通排。
  · 保留旧版有价值的要求:每项功能补齐 做什么/入口在哪/输入边界/异常与恢复;重点功能三处标红;
    横向流程图每步写清页面/功能/操作/完成判据。
  · 完成标准「七节」→「六节」,自查清单同步换掉「需求」那批条目,补模板与配图两条。
  · 路径 `designs/<项目>/` → `docs/pm/<项目>/ui/`(2 处)。
  · 补回本机那条「每份产出文档开头都要有一张图」的引用(指向主 skill,段内不复述)。
- 主 skill 同步:四段结构表④行、④段沿革、画图小节(④段画页面结构图 + 用户路径)。

二、open-design 移到技能根(用户选候选 A)
- `git mv product-planning/references/stage-delivery/assets/open-design → open-design/`(4178 文件)。
- `.gitignore` 白名单加 `!/open-design/` —— 否则移出去就不再纳入版本控制。
- 引用改 11 处:路径形态 `assets/open-design/...` → `<技能根>/open-design/...`;
  「随段携带的自带件」改称「同根技能」;「路径怎么找」那条改成自带件(段内相对路径)与
  同根技能(`<技能根>/<技能名>/`)两类分开写。
- open-design 自己的 ATTRIBUTION.md 两处同步(它现在与 oil-ui-pro 同层)。
- 副作用(有益):命名闸门读数由 2063 降到 225 —— 此前它把 open-design 里 919 个第三方 md
  当成规则一起扫了;现在只扫本包 23 个 md。已确认④段的 H1 与 4a/4b/4c 子步仍在检查范围内。

三、验收
- check_naming.py --ws E:/ProgramData/AIProject/contentm_agent = 通过 225 / 失败 0 / 提示 27。
- 活文件里 `assets/open-design` 残留 0;工作区 CRLF 文件 0。
2026-10-15 21:17:28 +08:00

1019 lines
39 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Inspired by Stripe — reference components</title>
<meta
name="description"
content="Reference fixture for design-systems/stripe. Every visible
value comes from tokens.css; the page itself follows Stripe's
rules — sohne-var with ss01 globally, weight 300 at display
sizes, deep navy (#061b31) headings instead of black, blue-
tinted multi-layer shadows on elevated surfaces, conservative
4-8px radius, and Stripe Purple capped at ≤2 visible uses
per screen."
/>
<style>
/* :root paste — sourced verbatim from
design-systems/stripe/tokens.css. Keep this block in sync
when tokens.css changes. The agent prompt instructs the
Designer panelist to paste this block as the FIRST thing in
their <style>, then reference everything below via var(...). */
:root {
--bg: #ffffff;
--surface: #ffffff;
--surface-warm: #f6f9fc;
--fg: #061b31;
--fg-2: #273951;
--muted: #64748d;
--meta: var(--muted);
--border: #e5edf5;
--border-soft: var(--border);
--accent: #533afd;
--accent-on: #ffffff;
--accent-hover: #4434d4;
--accent-active: #2e2b8c;
--success: #15be53;
--warn: #9b6829;
--danger: #ea2261;
--font-display:
"sohne-var", "Söhne", "Sohne",
"SF Pro Display", -apple-system, BlinkMacSystemFont,
system-ui, "Helvetica Neue", Arial, sans-serif;
--font-body:
"sohne-var", "Söhne", "Sohne",
"SF Pro Display", -apple-system, BlinkMacSystemFont,
system-ui, "Helvetica Neue", Arial, sans-serif;
--font-mono:
"SourceCodePro", "Source Code Pro",
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: 32px;
--text-3xl: 48px;
--text-4xl: 56px;
--leading-body: 1.40;
--leading-tight: 1.10;
--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: 96px;
--section-y-tablet: 64px;
--section-y-phone: 40px;
--radius-sm: 4px;
--radius-md: 6px;
--radius-lg: 8px;
--radius-pill: 9999px;
--elev-flat: none;
--elev-ring: 0 0 0 1px var(--border);
--elev-raised:
rgba(50, 50, 93, 0.25) 0px 30px 45px -30px,
rgba(0, 0, 0, 0.10) 0px 18px 36px -18px;
--focus-ring:
0 0 0 2px var(--accent),
0 0 0 5px color-mix(in oklab, var(--accent), transparent 75%);
--motion-fast: 150ms;
--motion-base: 200ms;
--ease-standard: cubic-bezier(0.2, 0, 0, 1);
--container-max: 1080px;
--container-gutter-desktop: 32px;
--container-gutter-tablet: 24px;
--container-gutter-phone: 16px;
}
/* ─── Reset (minimal) ───────────────────────────────────────── */
*, *::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: 300; /* Stripe's signature: 300 is body weight */
line-height: var(--leading-body);
/* `"ss01"` is non-negotiable on every sohne-var element
(DESIGN.md §3 Principles). Inheriting from <body> applies
it to every descendant by default; specific components
that need `tnum` for financial tables override locally. */
font-feature-settings: "ss01";
-webkit-font-smoothing: antialiased;
}
strong { font-weight: 400; } /* never 600/700 in sohne-var per DESIGN.md §7 */
/* ─── 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);
}
@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 ────────────────────────────────────────────── */
h1, h2, h3 {
font-family: var(--font-display);
font-weight: 300; /* Stripe's anti-convention signature */
line-height: var(--leading-tight);
letter-spacing: var(--tracking-display);
color: var(--fg);
margin: 0;
}
h1 {
font-size: var(--text-4xl);
line-height: 1.03; /* DESIGN.md §3: display hero ratio */
letter-spacing: -0.025em; /* exact -1.4px at 56px */
}
h2 {
font-size: var(--text-2xl);
}
h3 {
font-size: var(--text-xl);
letter-spacing: -0.01em; /* relaxed at 22px per DESIGN.md §3 */
}
p { margin: 0; }
.lede {
font-size: var(--text-lg);
line-height: var(--leading-body);
color: var(--muted);
max-width: 56ch;
}
.body-muted { color: var(--muted); }
.body-meta { color: var(--meta); font-size: var(--text-sm); }
.body-sm { font-size: var(--text-sm); }
/* `.eyebrow` is the only place uppercase + tracking is used —
small label, deep-navy on lavender wash. tracking is well
above the craft 0.06em floor. */
.eyebrow {
display: inline-block;
font-size: var(--text-xs);
font-weight: 400;
line-height: 1;
color: var(--accent);
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 ───────────────────────────────────────────────
* Stripe buttons are conservative — 4px radius, 8/16 padding,
* weight-400 sohne-var with ss01. Hover shifts background
* colour; never transforms. Primary uses --accent / --accent-on;
* ghost uses the brand purple as text colour against a
* transparent shell with a purple-tinted border. */
.btn {
display: inline-flex;
align-items: center;
gap: var(--space-2);
padding: 8px 16px;
border-radius: var(--radius-sm);
font-family: var(--font-display);
font-feature-settings: "ss01";
font-weight: 400;
font-size: var(--text-base);
line-height: 1;
cursor: pointer;
border: 1px solid transparent;
background: transparent;
color: var(--fg);
text-decoration: none;
transition:
background-color var(--motion-fast) var(--ease-standard),
border-color var(--motion-fast) var(--ease-standard),
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-primary {
background: var(--accent);
color: var(--accent-on);
}
.btn-primary:hover { background: var(--accent-hover); }
.btn-primary:active { background: var(--accent-active); }
/* Ghost / outlined — DESIGN.md §4 spec verbatim.
The hover wash is the only place we tint --accent with
transparency inline; it's a once-per-page rule rather than
a cross-component pattern, so it stays inline rather than
being promoted to a token. */
.btn-ghost {
background: transparent;
color: var(--accent);
border-color: color-mix(in oklab, var(--accent), white 65%);
}
.btn-ghost:hover {
background: color-mix(in oklab, var(--accent), transparent 95%);
border-color: color-mix(in oklab, var(--accent), white 55%);
}
/* Compact button for nav and toolbar — 14px / 4px radius */
.btn-sm {
padding: 6px 12px;
font-size: var(--text-sm);
}
/* ─── Inputs ────────────────────────────────────────────── */
.field {
display: flex;
flex-direction: column;
gap: var(--space-2);
}
.field label {
font-size: var(--text-sm);
font-weight: 400;
color: var(--fg-2); /* dark-slate label per DESIGN.md §4 */
}
.field input,
.field select,
.field textarea {
padding: 10px 12px;
border-radius: var(--radius-sm);
border: 1px solid var(--border);
background: var(--surface);
color: var(--fg);
font-family: inherit;
font-feature-settings: "ss01";
font-size: var(--text-base);
font-weight: 300;
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:focus-visible,
.field select:focus-visible,
.field textarea: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 ─────────────────────────────────────────────────
* Two card flavours:
* .card hairline border, no shadow — for grids and
* form panels where the border IS the edge.
* .card-elevated no border, blue-tinted multi-layer shadow —
* for featured / hero cards where Stripe's
* trademark elevation does the work.
* Both share radius and padding. Hover on .card-elevated
* intensifies the shadow; .card stays static (border doesn't
* pulse). */
.card {
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius-md);
padding: var(--space-6);
display: flex;
flex-direction: column;
gap: var(--space-3);
}
.card-elevated {
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);
transition: box-shadow var(--motion-base) var(--ease-standard);
}
.card-elevated:hover {
/* Intensify the near layer; far stays the same so the lift
reads as "drawing closer", not "growing larger". */
box-shadow:
rgba(50, 50, 93, 0.25) 0px 30px 45px -30px,
rgba(0, 0, 0, 0.14) 0px 24px 48px -16px;
}
.card-title {
font-family: var(--font-display);
font-feature-settings: "ss01";
font-weight: 300;
font-size: var(--text-xl);
line-height: 1.12;
letter-spacing: -0.01em;
color: var(--fg);
}
/* ─── Badges ────────────────────────────────────────────────
* Success badge per DESIGN.md §4. Pill radius is wrong here —
* Stripe uses 4px on badges, never 9999px. */
.badge {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 1px 6px;
border-radius: var(--radius-sm);
font-size: var(--text-xs);
font-weight: 300;
line-height: 1.6;
font-feature-settings: "ss01";
}
.badge-success {
color: var(--success);
background: color-mix(in oklab, var(--success), transparent 80%);
border: 1px solid
color-mix(in oklab, var(--success), transparent 60%);
}
.badge-muted {
color: var(--muted);
background: var(--surface-warm);
border: 1px solid var(--border);
}
.badge-accent {
color: var(--accent);
background: color-mix(in oklab, var(--accent), white 88%);
border: 1px solid
color-mix(in oklab, var(--accent), white 70%);
}
.badge-dot {
width: 6px;
height: 6px;
border-radius: 50%;
background: currentColor;
}
/* ─── Links ─────────────────────────────────────────────────
* Default link is Stripe Purple. Underline appears only on
* hover, at a generous offset so the rule reads as a
* deliberate gesture. */
a {
color: var(--accent);
text-decoration: none;
font-feature-settings: "ss01";
}
a:hover {
text-decoration: underline;
text-underline-offset: 3px;
text-decoration-thickness: 1px;
}
/* ─── Kbd ───────────────────────────────────────────────────
* SourceCodePro at 12px, hairline border, navy text. Stripe's
* kbd is small and dense — no padding inflation. */
kbd {
font-family: var(--font-mono);
font-size: 11px;
font-weight: 500;
padding: 2px 6px;
border-radius: var(--radius-sm);
border: 1px solid var(--border);
background: var(--surface-warm);
color: var(--fg-2);
}
/* ─── Distinctive brand element: gradient halo ──────────────
* Stripe's signature hero decoration is a soft pastel wash
* behind the headline — built here from the brand purple
* mixed at varying transparencies (no raw hex outside :root).
* The halo sits in a layered radial-gradient and lets the
* navy headline read as "floating in twilight". */
.halo {
position: absolute;
inset: -10% -20% -20% -10%;
pointer-events: none;
background:
radial-gradient(
60% 50% at 15% 30%,
color-mix(in oklab, var(--accent), transparent 70%) 0%,
color-mix(in oklab, var(--accent), transparent 100%) 65%
),
radial-gradient(
45% 40% at 85% 20%,
color-mix(in oklab, var(--danger), transparent 75%) 0%,
color-mix(in oklab, var(--danger), transparent 100%) 70%
),
radial-gradient(
55% 45% at 70% 80%,
color-mix(in oklab, var(--accent), transparent 80%) 0%,
color-mix(in oklab, var(--accent), transparent 100%) 70%
);
filter: blur(40px);
z-index: 0;
}
/* ─── Code block ────────────────────────────────────────────
* DESIGN.md §3: SourceCodePro at 12px / weight 500 / 2.00
* line-height. The 2.00 leading is intentional — Stripe's
* payment-integration snippets need to read like prose, not
* like a terminal dump. */
.code {
background: var(--surface-warm);
border: 1px solid var(--border);
border-radius: var(--radius-md);
padding: var(--space-5);
font-family: var(--font-mono);
font-size: var(--text-xs);
font-weight: 500;
line-height: 2.0;
color: var(--fg);
white-space: pre;
overflow-x: auto;
}
.code .k { color: var(--accent); } /* keyword */
.code .s { color: var(--success); } /* string */
.code .c { color: var(--muted); } /* comment */
.code .n { color: var(--fg-2); } /* identifier */
/* ─── Metric (tabular financial rendering) ──────────────────
* DESIGN.md §3 "Caption Tabular" — `tnum` for any number in
* a data display so columns of figures align. The values use
* deep navy; the labels use slate. */
.metric {
display: flex;
flex-direction: column;
gap: 4px;
}
.metric-value {
font-family: var(--font-display);
font-feature-settings: "tnum", "ss01";
font-weight: 300;
font-size: var(--text-2xl);
line-height: 1.1;
letter-spacing: -0.01em;
color: var(--fg);
}
.metric-label {
font-size: var(--text-sm);
color: var(--muted);
}
/* ─── Section-specific layout ───────────────────────────── */
.hero {
position: relative;
overflow: hidden;
}
.hero-inner {
position: relative;
z-index: 1;
display: grid;
grid-template-columns: 1.3fr 1fr;
gap: var(--space-12);
align-items: end;
}
@media (max-width: 1023px) {
.hero-inner { grid-template-columns: 1fr; gap: var(--space-8); }
}
.hero h1 {
max-width: 14ch;
margin: 0;
}
.hero-actions {
display: flex;
gap: var(--space-3);
margin-block-start: var(--space-8);
}
.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; }
}
/* Pricing-style featured card row — exercises .card-elevated
alongside .card so the elevation contrast is visible. */
.pricing-grid {
display: grid;
grid-template-columns: 1fr 1.1fr 1fr;
gap: var(--space-5);
align-items: stretch;
margin-block-start: var(--space-8);
}
@media (max-width: 1023px) {
.pricing-grid { grid-template-columns: 1fr; }
}
.pricing-price {
font-family: var(--font-display);
font-feature-settings: "tnum", "ss01";
font-weight: 300;
font-size: var(--text-3xl);
line-height: 1.05;
letter-spacing: -0.02em;
color: var(--fg);
}
.pricing-price small {
font-size: var(--text-sm);
color: var(--muted);
margin-inline-start: var(--space-2);
font-feature-settings: "ss01";
}
.pricing-list {
list-style: none;
padding: 0;
margin: 0;
display: flex;
flex-direction: column;
gap: var(--space-2);
}
.pricing-list li {
position: relative;
padding-inline-start: var(--space-5);
font-size: var(--text-sm);
color: var(--fg-2);
line-height: 1.5;
}
.pricing-list li::before {
content: "";
position: absolute;
left: 0;
top: 0.45em;
width: 12px;
height: 7px;
border-left: 1.5px solid var(--accent);
border-bottom: 1.5px solid var(--accent);
transform: rotate(-45deg);
transform-origin: 0 100%;
}
.metric-row {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: var(--space-8);
}
@media (max-width: 767px) {
.metric-row { grid-template-columns: repeat(2, 1fr); gap: var(--space-6); }
}
.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;
padding: var(--space-6);
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
box-shadow: var(--elev-raised);
}
.form-grid-2 {
display: grid;
grid-template-columns: 1fr 1fr;
gap: var(--space-3);
}
.form-actions {
display: flex;
gap: var(--space-3);
margin-block-start: var(--space-2);
align-items: center;
}
.icon { width: 14px; height: 14px; flex-shrink: 0; }
.row-between {
display: flex;
align-items: center;
justify-content: space-between;
gap: var(--space-3);
}
</style>
</head>
<body>
<main class="container">
<!-- ════════════════════════════════════════════════════════════
HERO — exercises: gradient .halo (Stripe's signature
pastel-wash behind the headline, built entirely from
color-mix() over --accent and --danger), h1 at 56px /
weight 300 / -0.025em (the brand's "whisper authority"),
.lede in slate, .btn-primary + .btn-ghost from DESIGN.md
§4 verbatim. Asymmetric grid on desktop; single column on
tablet.
═══════════════════════════════════════════════════════════════ -->
<section class="hero" data-od-id="hero">
<div class="halo" aria-hidden="true"></div>
<div class="hero-inner">
<div class="stack-6">
<span class="eyebrow">Payments · Infrastructure</span>
<h1>Financial infrastructure designed for the internet.</h1>
<p class="lede">
The reference fixture pastes Stripe's token block into a
single <code style="font-family:var(--font-mono);font-size:0.92em;color:var(--fg-2)">&lt;style&gt;</code>
and renders a recognisable Stripe page without authoring
any additional values — every colour, type size, and
shadow below resolves through <code style="font-family:var(--font-mono);font-size:0.92em;color:var(--fg-2)">var(--*)</code>.
</p>
<div class="hero-actions">
<a href="./tokens.css" class="btn btn-primary">
Start now
<svg
class="icon"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="1.5"
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">Contact sales</a>
</div>
</div>
<aside class="card-elevated" aria-label="Payment summary">
<div class="row-between">
<span class="eyebrow">Payment intent</span>
<span class="badge badge-success">
<span class="badge-dot" aria-hidden="true"></span>
Succeeded
</span>
</div>
<div class="metric">
<div
class="metric-value"
style="font-size: var(--text-3xl); letter-spacing: -0.02em"
>
$4,280.<span style="color: var(--muted)">00</span>
</div>
<div class="metric-label">
Charged via card_1NqK… · 2026-05-14
</div>
</div>
<div
style="
display: grid;
grid-template-columns: 1fr 1fr;
gap: var(--space-3);
padding-block-start: var(--space-3);
border-top: 1px solid var(--border);
"
>
<div class="metric">
<div class="metric-label">Net</div>
<div
class="metric-value"
style="font-size: var(--text-base); color: var(--fg-2)"
>
$4,154.88
</div>
</div>
<div class="metric">
<div class="metric-label">Fee</div>
<div
class="metric-value"
style="font-size: var(--text-base); color: var(--fg-2)"
>
$125.12
</div>
</div>
</div>
<p class="body-sm body-muted">
Press <kbd>⌘</kbd> <kbd>K</kbd> to inspect this token block
in the design-system viewer.
</p>
</aside>
</div>
</section>
<!-- ════════════════════════════════════════════════════════════
METRICS — exercises: .metric pattern with `tnum`, deep-
navy values, slate labels. Numbers describe the fixture
itself, not invented claims.
═══════════════════════════════════════════════════════════════ -->
<section data-od-id="metrics">
<div class="stack-3">
<span class="eyebrow">Schema in numbers</span>
<h2 style="max-width: 24ch">
Fifty-six tokens carry every visible value below.
</h2>
</div>
<div class="metric-row" style="margin-block-start: var(--space-8)">
<div class="metric">
<div class="metric-value">56</div>
<div class="metric-label">tokens in :root</div>
</div>
<div class="metric">
<div class="metric-value">0</div>
<div class="metric-label">brand-C extensions</div>
</div>
<div class="metric">
<div class="metric-value">3</div>
<div class="metric-label">elevation levels (flat · ring · raised)</div>
</div>
<div class="metric">
<div class="metric-value">300</div>
<div class="metric-label">default sohne-var weight</div>
</div>
</div>
</section>
<!-- ════════════════════════════════════════════════════════════
FEATURES — exercises: h2, .card (hairline border), h3 with
tighter tracking, body text, .features-grid responsive
ladder. Each card describes a real property of this
fixture (no "feature one/two/three" filler).
═══════════════════════════════════════════════════════════════ -->
<section data-od-id="features">
<div class="stack-3">
<span class="eyebrow">What this fixture exercises</span>
<h2 style="max-width: 30ch">
Every component renders through <code style="font-family:var(--font-mono);font-size:0.92em;color:var(--fg-2)">var(--*)</code> —
no raw hex, no off-token type.
</h2>
</div>
<div class="features-grid">
<article class="card">
<h3 class="card-title">Shadow as identity</h3>
<p class="body-muted body-sm">
The trademark blue-tinted multi-layer shadow lives at
<code style="font-family:var(--font-mono);font-size:0.92em;color:var(--fg-2)">--elev-raised</code>.
Rebinding it to a neutral drop shadow erases Stripe's
atmosphere even when every colour is right.
</p>
<a href="./tokens.css" class="body-sm">Inspect tokens →</a>
</article>
<article class="card">
<h3 class="card-title">Light weight as voice</h3>
<p class="body-muted body-sm">
Display sizes run at weight 300 with negative tracking.
Stripe's headlines feel like they don't need to shout;
weight 600 would erase the brand instantly.
</p>
<a href="./DESIGN.md" class="body-sm">Read the spec →</a>
</article>
<article class="card">
<h3 class="card-title">Accent discipline</h3>
<p class="body-muted body-sm">
<code style="font-family:var(--font-mono);font-size:0.92em;color:var(--fg-2)">--accent</code>
appears at most twice per screen — primary CTA and link
colour. Decorative ruby / magenta gradients live in the
hero halo, not on interactive elements.
</p>
<a href="./tokens.css" class="body-sm">Inspect accent →</a>
</article>
</div>
</section>
<!-- ════════════════════════════════════════════════════════════
PRICING — exercises: .card-elevated with the trademark
blue-tinted shadow, alongside two .card siblings so the
elevation hierarchy is immediately visible. Tabular numerals
on the price; check-mark feature list keyed to --accent.
═══════════════════════════════════════════════════════════════ -->
<section data-od-id="pricing">
<div class="stack-3">
<span class="eyebrow">Pricing pattern</span>
<h2 style="max-width: 26ch">
Featured tier lifts via shadow alone — no colour shift, no
border change.
</h2>
</div>
<div class="pricing-grid">
<article class="card">
<div class="stack-3">
<h3 class="card-title">Starter</h3>
<p class="body-muted body-sm">
For new accounts integrating their first payment flow.
</p>
</div>
<div class="pricing-price">
$0<small>/month</small>
</div>
<ul class="pricing-list">
<li>Standard processing rates</li>
<li>Dashboard access</li>
<li>Email support</li>
</ul>
<a href="./tokens.css" class="btn btn-ghost btn-sm">Start free</a>
</article>
<article class="card-elevated" aria-label="Featured tier">
<div class="row-between">
<span class="eyebrow">Most popular</span>
<span class="badge badge-accent">Featured</span>
</div>
<h3 class="card-title">Growth</h3>
<div class="pricing-price">
$79<small>/month</small>
</div>
<ul class="pricing-list">
<li>Volume processing discounts</li>
<li>Subscriptions &amp; invoicing</li>
<li>Fraud monitoring</li>
<li>Priority support</li>
</ul>
<a href="./tokens.css" class="btn btn-primary btn-sm">Choose Growth</a>
</article>
<article class="card">
<div class="stack-3">
<h3 class="card-title">Scale</h3>
<p class="body-muted body-sm">
Custom pricing for high-volume merchants and platforms.
</p>
</div>
<div class="pricing-price">
Custom
</div>
<ul class="pricing-list">
<li>Negotiated processing rates</li>
<li>Dedicated solutions engineer</li>
<li>Custom data residency</li>
</ul>
<a href="./DESIGN.md" class="btn btn-ghost btn-sm">Contact sales</a>
</article>
</div>
</section>
<!-- ════════════════════════════════════════════════════════════
CODE PREVIEW — exercises: SourceCodePro at 12px / weight
500 / line-height 2.00 (DESIGN.md §3). Keyword tokens use
--accent; strings use --success; comments use --muted.
═══════════════════════════════════════════════════════════════ -->
<section data-od-id="code">
<div class="form-row">
<div class="stack-4">
<span class="eyebrow">Integration preview</span>
<h2 style="max-width: 18ch">
Two lines of code, one charge.
</h2>
<p class="body-muted lede">
The monospace face is <code style="font-family:var(--font-mono);font-size:0.95em">SourceCodePro</code>
at 12px / weight 500 / line-height 2.00 — generous leading
so payment snippets read like prose, not like terminal
output.
</p>
<div>
<a href="./tokens.css" class="btn btn-ghost btn-sm">Inspect mono token</a>
</div>
</div>
<pre class="code"><span class="c">// Create a PaymentIntent on your server.</span>
<span class="k">const</span> <span class="n">intent</span> = <span class="k">await</span> <span class="n">stripe</span>.<span class="n">paymentIntents</span>.<span class="n">create</span>({
<span class="n">amount</span>: <span class="k">4280</span>,
<span class="n">currency</span>: <span class="s">"usd"</span>,
<span class="n">automatic_payment_methods</span>: { <span class="n">enabled</span>: <span class="k">true</span> },
});
<span class="c">// Return the client secret to the browser.</span>
<span class="k">res</span>.<span class="n">json</span>({ <span class="n">clientSecret</span>: <span class="n">intent</span>.<span class="n">client_secret</span> });</pre>
</div>
</section>
<!-- ════════════════════════════════════════════════════════════
FORM — exercises: .field, input :focus-visible (purple halo
via --focus-ring), .btn-primary (reused), .btn-ghost,
.field-help. The form panel itself uses .card-elevated so
the trademark shadow frames the inputs.
═══════════════════════════════════════════════════════════════ -->
<section data-od-id="form">
<div class="form-row">
<div class="stack-4">
<span class="eyebrow">Form components</span>
<h2 style="max-width: 22ch">
Inputs inherit the same tokens.
</h2>
<p class="body-muted lede">
Focus rings, borders, placeholder colour — all derive
from <code style="font-family:var(--font-mono);font-size:0.92em;color:var(--fg-2)">--accent</code>
and <code style="font-family:var(--font-mono);font-size:0.92em;color:var(--fg-2)">--border</code>.
The submit button reuses <code style="font-family:var(--font-mono);font-size:0.92em;color:var(--fg-2)">.btn-primary</code>
unchanged. The panel itself lifts via the trademark
blue-tinted shadow.
</p>
</div>
<form class="form" onsubmit="event.preventDefault();">
<div class="row-between">
<h3 class="card-title" style="font-size: var(--text-lg)">
Contact sales
</h3>
<span class="badge badge-muted">Secure</span>
</div>
<div class="form-grid-2">
<div class="field">
<label for="first">First name</label>
<input
id="first"
type="text"
placeholder="Jamie"
autocomplete="given-name"
/>
</div>
<div class="field">
<label for="last">Last name</label>
<input
id="last"
type="text"
placeholder="Liu"
autocomplete="family-name"
/>
</div>
</div>
<div class="field">
<label for="email">Work email</label>
<input
id="email"
type="email"
placeholder="[email protected]"
autocomplete="email"
required
/>
<p class="field-help">
We'll send the integration brief and nothing else.
</p>
</div>
<div class="field">
<label for="volume">Monthly processing volume</label>
<select id="volume">
<option>Under $80k</option>
<option>$80k – $500k</option>
<option>$500k – $5M</option>
<option>Over $5M</option>
</select>
</div>
<div class="form-actions">
<button type="submit" class="btn btn-primary">
Start now
</button>
<button type="button" class="btn btn-ghost">
Skip for now
</button>
</div>
</form>
</div>
</section>
</main>
</body>
</html>