assets/template.html
<!doctype html>
<!--
OD web-prototype seed.
Copy this file to <project>/index.html, then fill `<main id="content">` by
pasting blocks from `references/layouts.md`. Every class referenced in
layouts.md is defined here — DO NOT remove unused ones unless you also
remove their callers, and DO NOT invent new global classes (use inline
style="…" for one-off tweaks).
Theme tokens at the top of `<style>` are the *only* place to set palette
and type. They map cleanly onto a DESIGN.md — when an active design system
is injected, swap these six variables and everything reflows.
-->
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>[REPLACE] Page title · brand</title>
<style>
/* ─── tokens ─────────────────────────────────────────────────────────
Six variables. Bind them to the active DESIGN.md and stop.
Do not introduce raw hex anywhere else in this file. */
:root {
--bg: #fafaf7; /* page background — never #fff */
--surface: #ffffff; /* cards, modals, raised areas */
--fg: #1a1916; /* primary text — never #000 */
--muted: #6b6964; /* secondary text, captions */
--border: #e8e5df; /* hairlines, dividers */
--accent: #c96442; /* one accent — used at most 2× per screen */
/* derived — do not change */
--accent-soft: color-mix(in oklch, var(--accent) 14%, transparent);
--fg-soft: color-mix(in oklch, var(--fg) 6%, transparent);
/* type — display = serif (default), body = sans, mono for numerics */
--font-display: 'Iowan Old Style', 'Charter', Georgia, 'Times New Roman', serif;
--font-body: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
--font-mono: ui-monospace, 'JetBrains Mono', 'SF Mono', Menlo, monospace;
/* scale — clamp() so it works at 1280, 1440, 1920 without media queries */
--fs-h1: clamp(44px, 6vw, 76px);
--fs-h2: clamp(32px, 4vw, 48px);
--fs-h3: 22px;
--fs-lead: 19px;
--fs-body: 16px;
--fs-meta: 13px;
/* spacing — 8-point grid */
--gap-xs: 8px;
--gap-sm: 12px;
--gap-md: 20px;
--gap-lg: 32px;
--gap-xl: 56px;
--gap-2xl: 96px;
--container: 1120px;
--gutter: 32px;
--radius: 10px;
--radius-lg: 16px;
}
/* ─── reset & base ──────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
margin: 0;
background: var(--bg);
color: var(--fg);
font-family: var(--font-body);
font-size: var(--fs-body);
line-height: 1.55;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
}
img, svg { display: block; max-width: 100%; }
a { color: inherit; text-decoration: none; }
button { font: inherit; cursor: pointer; }
p { text-wrap: pretty; }
h1, h2, h3, h4 { text-wrap: balance; }
/* ─── layout primitives ─────────────────────────────────────────── */
.container {
max-width: var(--container);
margin-inline: auto;
padding-inline: var(--gutter);
}
.section {
padding-block: clamp(48px, 8vw, var(--gap-2xl));
}
.section + .section { border-top: 1px solid var(--border); }
.stack { display: flex; flex-direction: column; }
.stack > * + * { margin-top: var(--gap-md); }
.row { display: flex; align-items: center; gap: var(--gap-md); }
.row-between { display: flex; align-items: center; justify-content: space-between; gap: var(--gap-md); }
.grid-2 { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--gap-lg); }
.grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--gap-lg); }
.grid-4 { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--gap-md); }
.grid-2-1 { display: grid; grid-template-columns: 2fr 1fr; gap: var(--gap-xl); align-items: start; }
.grid-1-2 { display: grid; grid-template-columns: 1fr 2fr; gap: var(--gap-xl); align-items: start; }
@media (max-width: 920px) {
.grid-2, .grid-3, .grid-4, .grid-2-1, .grid-1-2 { grid-template-columns: 1fr; }
.topnav-inner { flex-wrap: wrap; }
.topnav nav { display: none; }
.log-row { grid-template-columns: 1fr; }
.log-row .pull { text-align: left; }
}
/* ─── type ──────────────────────────────────────────────────────── */
.h1, h1 { font-family: var(--font-display); font-size: var(--fs-h1); line-height: 1.04; letter-spacing: -0.02em; margin: 0; }
.h2, h2 { font-family: var(--font-display); font-size: var(--fs-h2); line-height: 1.1; letter-spacing: -0.015em; margin: 0; }
.h3, h3 { font-size: var(--fs-h3); font-weight: 600; line-height: 1.3; letter-spacing: -0.005em; margin: 0; }
.lead { font-size: var(--fs-lead); line-height: 1.55; color: var(--muted); max-width: 60ch; margin: 0; }
.eyebrow {
font-family: var(--font-mono);
font-size: 12px;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--accent);
margin: 0 0 var(--gap-md);
}
.meta { font-family: var(--font-mono); font-size: var(--fs-meta); color: var(--muted); }
.num { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
/* ─── chrome: nav + footer ──────────────────────────────────────── */
.topnav {
position: sticky; top: 0; z-index: 10;
background: color-mix(in oklch, var(--bg) 92%, transparent);
backdrop-filter: blur(12px);
border-bottom: 1px solid var(--border);
}
.topnav-inner { display: flex; align-items: center; justify-content: space-between; padding-block: 14px; }
.topnav .logo { font-family: var(--font-display); font-size: 19px; font-weight: 600; letter-spacing: -0.01em; }
.topnav nav { display: flex; gap: var(--gap-lg); }
.topnav nav a { font-size: 14px; color: var(--muted); }
.topnav nav a:hover { color: var(--fg); }
.pagefoot { padding-block: var(--gap-xl); color: var(--muted); font-size: 13px; border-top: 1px solid var(--border); }
.pagefoot .row-between { flex-wrap: wrap; gap: var(--gap-md); }
/* ─── buttons ───────────────────────────────────────────────────── */
.btn {
display: inline-flex; align-items: center; gap: 8px;
padding: 11px 20px;
border-radius: var(--radius);
border: 1px solid transparent;
font-size: 15px;
font-weight: 500;
letter-spacing: -0.005em;
transition: transform 0.05s ease, background 0.15s ease, border-color 0.15s ease;
}
.btn:active { transform: translateY(1px); }
.btn-primary { background: var(--accent); color: var(--surface); border-color: var(--accent); }
.btn-primary:hover { background: color-mix(in oklch, var(--accent) 88%, black); }
.btn-secondary { background: transparent; color: var(--fg); border-color: var(--border); }
.btn-secondary:hover { border-color: var(--fg); }
.btn-ghost { background: transparent; color: var(--fg); border-color: transparent; padding-inline: 8px; }
.btn-ghost:hover { color: var(--accent); }
.btn-arrow::after { content: '→'; transition: transform 0.15s ease; }
.btn-arrow:hover::after { transform: translateX(2px); }
/* ─── card / surface ────────────────────────────────────────────── */
.card {
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
padding: 28px;
}
.card-flat { background: transparent; border: 0; padding: 0; }
.card-rule { background: transparent; border: 0; border-top: 1px solid var(--fg); padding: 24px 0 0; border-radius: 0; }
/* ─── feature cell (icon + h3 + p) ──────────────────────────────── */
.feature .feature-mark {
width: 36px; height: 36px;
display: grid; place-items: center;
border: 1px solid var(--border);
border-radius: 10px;
color: var(--accent);
margin-bottom: var(--gap-md);
}
.feature .feature-mark svg { width: 18px; height: 18px; }
.feature h3 { margin-bottom: 6px; }
.feature p { margin: 0; color: var(--muted); font-size: 15px; }
/* ─── stat (big number + label) ─────────────────────────────────── */
.stat .stat-num {
font-family: var(--font-display);
font-size: clamp(56px, 8vw, 96px);
line-height: 0.95;
letter-spacing: -0.04em;
color: var(--accent);
font-weight: 600;
}
.stat .stat-label { color: var(--muted); font-size: 14px; margin-top: 8px; max-width: 24ch; }
.stat .stat-unit { font-size: 0.5em; opacity: 0.7; margin-left: 2px; }
/* ─── quote / testimonial ───────────────────────────────────────── */
.quote {
font-family: var(--font-display);
font-size: clamp(24px, 2.6vw, 32px);
line-height: 1.32;
letter-spacing: -0.01em;
max-width: 28ch;
}
.quote-author { color: var(--muted); font-size: 14px; margin-top: var(--gap-md); }
.quote-mark {
font-family: var(--font-display);
font-size: 140px; line-height: 0.7;
color: var(--accent); opacity: 0.18;
margin-bottom: -28px;
}
/* ─── pill / badge / tag ────────────────────────────────────────── */
.pill {
display: inline-flex; align-items: center; gap: 6px;
padding: 4px 10px;
background: var(--accent-soft);
color: var(--accent);
border-radius: 999px;
font-family: var(--font-mono);
font-size: 11px;
letter-spacing: 0.04em;
text-transform: uppercase;
}
.tag {
display: inline-flex; align-items: center;
padding: 4px 10px;
background: transparent;
color: var(--muted);
border: 1px solid var(--border);
border-radius: 999px;
font-size: 12px;
}
/* ─── form field ────────────────────────────────────────────────── */
.field { display: flex; flex-direction: column; gap: 6px; }
.field label { font-size: 13px; color: var(--muted); }
.input, .textarea {
width: 100%;
padding: 11px 14px;
border: 1px solid var(--border);
border-radius: var(--radius);
background: var(--surface);
color: var(--fg);
font: inherit;
font-size: 15px;
}
.input:focus, .textarea:focus {
outline: 2px solid var(--accent-soft);
border-color: var(--accent);
}
.textarea { min-height: 96px; resize: vertical; line-height: 1.55; }
/* ─── table (data-style, no chrome) ─────────────────────────────── */
.ds-table { width: 100%; border-collapse: collapse; font-size: 14px; }
.ds-table th, .ds-table td { padding: 12px 14px; text-align: left; border-bottom: 1px solid var(--border); }
.ds-table th { color: var(--muted); font-weight: 500; font-family: var(--font-mono); font-size: 12px; letter-spacing: 0.04em; text-transform: uppercase; }
.ds-table tbody tr:hover { background: var(--fg-soft); }
.ds-table .num-col { font-family: var(--font-mono); font-variant-numeric: tabular-nums; text-align: right; }
/* ─── image placeholder (DS-tinted block, never broken <img>) ──── */
.ph-img {
background:
linear-gradient(135deg, var(--accent-soft), var(--fg-soft)),
var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
aspect-ratio: 16 / 10;
display: grid; place-items: center;
color: var(--muted);
font-family: var(--font-mono);
font-size: 12px;
letter-spacing: 0.04em;
}
.ph-img.square { aspect-ratio: 1 / 1; }
.ph-img.portrait { aspect-ratio: 3 / 4; }
.ph-img.wide { aspect-ratio: 16 / 9; }
/* ─── divider ───────────────────────────────────────────────────── */
.rule { border: 0; border-top: 1px solid var(--border); margin: 0; }
.rule-strong { border: 0; border-top: 1px solid var(--fg); margin: 0; }
/* ─── hero variants used by layouts.md ──────────────────────────── */
.hero { padding-block: clamp(80px, 12vw, 160px); }
.hero-center { text-align: center; max-width: 32ch; margin-inline: auto; }
.hero h1 { margin-bottom: var(--gap-md); }
.hero .lead { margin-bottom: var(--gap-lg); }
.hero-cta { display: inline-flex; gap: var(--gap-sm); flex-wrap: wrap; }
.hero-center .hero-cta { justify-content: center; }
.hero-split { display: grid; grid-template-columns: 1fr 1fr; gap: var(--gap-2xl); align-items: center; }
@media (max-width: 920px) { .hero-split { grid-template-columns: 1fr; } }
/* ─── log row (newsletter, blog list, changelog) ────────────────── */
.log-row { display: grid; grid-template-columns: 120px 1fr 100px; gap: var(--gap-lg); padding: 22px 0; border-top: 1px solid var(--border); align-items: baseline; }
.log-row .meta { color: var(--muted); }
.log-row h3 { font-size: 19px; }
.log-row .pull { text-align: right; }
/* ─── list (unordered/ordered list with consistent spacing) ─────── */
.list { margin: 16px 0 0; padding: 0 0 0 18px; }
.list li { margin: 8px 0; color: var(--fg); font-size: var(--fs-body); }
</style>
</head>
<body>
<header class="topnav" data-od-id="topnav">
<div class="container topnav-inner">
<span class="logo">[REPLACE] Brand</span>
<nav>
<a href="#added">[REPLACE] Added</a>
<a href="#fixed">[REPLACE] Fixed</a>
<a href="#upgrade-note">[REPLACE] Upgrade note</a>
</nav>
<a class="btn btn-primary" href="REPLACE_WITH_REAL_URL">[REPLACE] CTA</a>
</div>
</header>
<main id="content">
<!--
┌─────────────────────────────────────────────────────────────────┐
│ PASTE LAYOUTS FROM references/layouts.md HERE. │
│ ► Each layout block is a self-contained `<section>` — │
│ drop in 3–6 of them per page. │
│ ► Always lead with one hero (Layout 1 or 2). │
│ ► End with a CTA strip + footer (Layout 6 / footer below). │
└─────────────────────────────────────────────────────────────────┘
-->
<section class="section hero" data-od-id="hero">
<div class="container hero-center">
<p class="eyebrow">[REPLACE] Eyebrow</p>
<h1>[REPLACE] One sharp sentence about what this is.</h1>
<p class="lead">[REPLACE] One subhead sentence — concrete value, not a tagline.</p>
</div>
</section>
</main>
<footer class="pagefoot" data-od-id="footer">
<div class="container row-between">
<span>© [REPLACE] Brand · [REPLACE] Year</span>
<span class="meta">[REPLACE] tagline · contact@example.com</span>
</div>
</footer>
</body>
</html>
example.html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Release Notes v2.3.1</title>
<style>
:root {
--bg: #fafaf7;
--surface: #ffffff;
--fg: #1a1916;
--muted: #6b6964;
--border: #e8e5df;
--accent: #c96442;
--font-display: 'Iowan Old Style', 'Charter', Georgia, 'Times New Roman', serif;
--font-body: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
--font-mono: ui-monospace, 'JetBrains Mono', 'SF Mono', Menlo, monospace;
}
* { box-sizing: border-box; }
body { margin: 0; font: 16px/1.55 var(--font-body); color: var(--fg); background: var(--bg); }
.container { max-width: 1120px; margin: 0 auto; padding: 0 32px; }
.section { padding: 72px 0; }
.section[data-od-id="hero"] { padding: 96px 0 84px; }
.section + .section { border-top: 1px solid var(--border); }
.topnav { position: sticky; top: 0; z-index: 10; background: color-mix(in oklch, var(--bg) 92%, transparent); border-bottom: 1px solid var(--border); backdrop-filter: blur(10px); }
.topnav-inner { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 14px 0; }
.logo { font: 600 19px/1.1 var(--font-display); letter-spacing: -0.01em; }
nav { display: flex; gap: 24px; }
nav a { text-decoration: none; color: var(--muted); font-size: 14px; }
nav a:hover { color: var(--fg); }
.btn { display: inline-flex; align-items: center; gap: 8px; padding: 10px 18px; border: 1px solid transparent; border-radius: 10px; font: 500 15px/1 var(--font-body); cursor: pointer; }
.btn-primary { background: var(--accent); color: var(--surface); border-color: var(--accent); }
.hero { text-align: center; max-width: 80ch; margin: 0 auto; }
.eyebrow { margin: 0 0 14px; color: var(--accent); text-transform: uppercase; letter-spacing: 0.08em; font: 12px/1.2 var(--font-mono); }
h1 { margin: 0 auto 20px; max-width: 16ch; font: 600 clamp(40px, 6vw, 66px)/1.05 var(--font-display); letter-spacing: -0.02em; }
.lead { margin: 0 auto; color: var(--muted); font-size: 19px; max-width: 40ch; }
.row-between { display: flex; align-items: center; justify-content: space-between; gap: 16px; }
h2 { margin: 0; font: 600 clamp(30px, 4vw, 44px)/1.1 var(--font-display); letter-spacing: -0.015em; }
.meta { color: var(--muted); font: 13px/1.4 var(--font-mono); }
.log-row { display: grid; grid-template-columns: 120px 1fr 140px; gap: 24px; align-items: baseline; padding: 18px 0; border-top: 1px solid var(--border); }
.log-row h3 { margin: 0; font-size: 19px; letter-spacing: -0.005em; }
.log-row p { margin: 5px 0 0; color: var(--muted); font-size: 14px; }
.pull { text-align: right; }
.list { margin: 16px 0 0; padding: 0 0 0 18px; }
.list li { margin: 8px 0; }
.cta { text-align: center; max-width: 640px; margin: 0 auto; }
.cta p { color: var(--muted); margin: 14px 0 28px; }
.pagefoot { border-top: 1px solid var(--border); padding: 28px 0; font-size: 13px; color: var(--muted); }
.pagefoot .row-between { flex-wrap: wrap; }
.num { font-variant-numeric: tabular-nums; font-family: var(--font-mono); }
@media (max-width: 920px) {
.log-row { grid-template-columns: 1fr; gap: 8px; }
.pull { text-align: left; }
nav { display: none; }
}
</style>
</head>
<body>
<header class="topnav" data-od-id="topnav">
<div class="container topnav-inner">
<span class="logo">Release notes</span>
<nav>
<a href="#added">Added</a>
<a href="#fixed">Fixed</a>
<a href="#upgrade-note">Upgrade note</a>
</nav>
</div>
</header>
<main id="content">
<section class="section" data-od-id="hero">
<div class="container hero">
<p class="eyebrow">Release notes · Version: v2.3.1 · Date: —</p>
<h1>v2.3.1</h1>
<p class="lead">No summary provided.</p>
</div>
</section>
<section class="section" id="added" data-od-id="added">
<div class="container">
<div class="row-between" style="margin-bottom: 24px;">
<h2>Added</h2>
<span class="meta">—</span>
</div>
<article class="log-row">
<span class="meta num">—</span>
<div>
<h3>No additions provided</h3>
</div>
<span class="pull meta">—</span>
</article>
</div>
</section>
<section class="section" id="fixed" data-od-id="fixed">
<div class="container">
<div class="row-between" style="margin-bottom: 24px;">
<h2>Fixed</h2>
<span class="meta">—</span>
</div>
<article class="log-row">
<span class="meta num">—</span>
<div>
<h3>No fixes provided</h3>
</div>
<span class="pull meta">—</span>
</article>
</div>
</section>
<section class="section" id="breaking-changes" data-od-id="breaking-changes">
<div class="container">
<div class="row-between" style="margin-bottom: 24px;">
<h2>Breaking changes</h2>
<span class="meta">—</span>
</div>
<article class="log-row">
<span class="meta num">—</span>
<div>
<h3>None</h3>
</div>
<span class="pull meta">—</span>
</article>
</div>
</section>
<section class="section" id="known-issues" data-od-id="known-issues">
<div class="container">
<div class="row-between" style="margin-bottom: 16px;">
<h2>Known issues</h2>
<span class="meta">—</span>
</div>
<p class="meta">None reported</p>
</div>
</section>
<section class="section" id="upgrade-note" data-od-id="upgrade-note">
<div class="container">
<div class="row-between" style="margin-bottom: 16px;">
<h2>Upgrade note</h2>
<span class="meta">—</span>
</div>
<p class="meta">No upgrade actions required based on provided information.</p>
</div>
</section>
</main>
<footer class="pagefoot" data-od-id="footer">
<div class="container row-between">
<span>© —</span>
<span class="meta">Release notes · v2.3.1</span>
</div>
</footer>
</body>
</html>
references/checklist.md
# Release Notes One-Pager Checklist
Run this before emitting `<artifact>`. P0 = must pass; P1 = should pass; P2 = nice to have.
## P0 — must pass
- [ ] Required sections exist and are visible: Added, Fixed, Breaking changes, Known issues, Upgrade note.
- [ ] Added section explicitly says `No additions provided` when no additions are supplied.
- [ ] Fixed section explicitly says `No fixes provided` when no fixes are supplied.
- [ ] Breaking changes section explicitly says `None` when there are no breaking changes.
- [ ] Known issues section explicitly says `None reported` when there are no known issues.
- [ ] Upgrade note section explicitly says either concrete steps or `No upgrade actions required based on provided information`.
- [ ] No invented dates, versions, metrics, or claims. If missing, use labelled placeholders such as `Version: —`.
- [ ] No filler copy and no lorem ipsum.
- [ ] No raw hex colors outside the `:root` token block.
- [ ] No default AI-accent indigo or violet accents (`#6366f1`, `#4f46e5`, `#4338ca`, `#3730a3`, `#8b5cf6`, `#7c3aed`, `#a855f7`).
- [ ] No purple/blue trust gradient hero backgrounds.
- [ ] No emoji icons in headings, feature rows, buttons, or list rows.
- [ ] Display headings use `var(--font-display)`; no hardcoded sans display type.
- [ ] No rounded card with a coloured left-border accent pattern.
- [ ] Every top-level `<section>` has a `data-od-id`.
- [ ] Every required release-note section root also includes the matching `id` anchor: `added`, `fixed`, `breaking-changes`, `known-issues`, `upgrade-note`.
- [ ] Every emitted CTA uses a real destination; `href="#"` is not acceptable. If no destination exists, omit the closing CTA strip.
- [ ] No placeholder tokens remain in href attributes: `[REPLACE]`, `REPLACE_WITH_REAL_URL`, or `https://example.com`. All hrefs must be valid URLs.
- [ ] CTA copy must accurately describe an available destination; do not promise content that is not present.
- [ ] Mobile reflow works without horizontal scroll at narrow widths.
## P1 — should pass
- [ ] Added and Fixed entries are concrete (what changed, not generic "improvements").
- [ ] Known issue rows include status or workaround when provided by the user.
- [ ] Upgrade note uses clear verbs (`Run`, `Update`, `Restart`, `Rebuild`) for action steps.
- [ ] Headlines stay concise (prefer under 14 words).
- [ ] CTA label is specific (`Read full changelog`, `Share release notes`) and not generic (`Learn more`).
- [ ] Accent usage stays restrained; avoid reusing accent treatment in every section.
## P2 — nice to have
- [ ] One meaningful highlight in hero (single decisive sentence, not marketing fluff).
- [ ] Log rows are balanced in length and easy to scan.
- [ ] Numeric values use `.num` style for legibility.
references/layouts.md
# Web prototype layouts
**8 paste-ready section skeletons.** Drop into `<main id="content">` of `assets/template.html`. Don't write sections from scratch — pick the closest layout, paste, swap copy.
## Pre-flight (do this once before pasting anything)
1. **Read `assets/template.html`** through the end of the `<style>` block. Every class used below must exist there. If one is missing, add it to `<style>` rather than inlining it on each section.
2. **Pick a section list before writing copy.** Default rhythms:
- **Landing**: 1 hero → 2 features → 3 stat-row OR quote → 4 split → 6 cta-strip → footer
- **Marketing / editorial**: 1 hero-center → 7 log-list → 4 split → 6 cta-strip
- **Pricing / docs**: 1 hero-center → table-driven → 6 cta-strip
3. **One accent per screen, used at most twice.** The hero eyebrow and the primary button already use it; budget any third usage carefully.
## Class inventory (must exist in `template.html`)
> `section` `container` `hero` `hero-center` `hero-split` `hero-cta` `eyebrow` `lead` `h1` `h2` `h3` `meta` `num` `btn` `btn-primary` `btn-secondary` `btn-ghost` `btn-arrow` `card` `card-flat` `card-rule` `feature` `feature-mark` `stat` `stat-num` `stat-label` `stat-unit` `quote` `quote-mark` `quote-author` `pill` `tag` `field` `input` `textarea` `ds-table` `num-col` `ph-img` `square` `portrait` `wide` `rule` `rule-strong` `list` `grid-2` `grid-3` `grid-4` `grid-2-1` `grid-1-2` `row` `row-between` `stack` `log-row` `pull` `topnav` `pagefoot`
If you reach for a class not on this list, define it in `<style>` first or use `style="…"` inline. Never invent a global class on a `<section>` that isn't backed by CSS.
---
## Layout 1 — Hero, centered
Use when the page leads with a thesis sentence (most landings, most marketing pages). One eyebrow, one h1 (≤14 words), one lead sentence, two CTAs.
```html
<section class="section hero" data-od-id="hero">
<div class="container hero-center">
<p class="eyebrow">EYEBROW · CONTEXT</p>
<h1>One sharp sentence about what this is.</h1>
<p class="lead">One concrete-value subhead — what changes for the reader.</p>
<div class="hero-cta">
<a class="btn btn-primary" href="REPLACE_WITH_REAL_URL">Primary action</a>
<a class="btn btn-secondary" href="REPLACE_WITH_REAL_URL">Secondary</a>
</div>
</div>
</section>
```
## Layout 2 — Hero, split (text + visual)
Use when there is a real product visual (product UI, screenshot, photograph). Left half copy, right half a `ph-img` placeholder the user replaces.
```html
<section class="section" data-od-id="hero-split">
<div class="container hero-split">
<div>
<p class="eyebrow">EYEBROW · ROLE</p>
<h1>Headline that names the change.</h1>
<p class="lead" style="margin-top: 20px;">A short subhead — concrete, not corporate. Two sentences max.</p>
<div class="hero-cta" style="margin-top: 28px;">
<a class="btn btn-primary" href="REPLACE_WITH_REAL_URL">Primary action</a>
<a class="btn btn-ghost btn-arrow" href="REPLACE_WITH_REAL_URL">Read the story</a>
</div>
</div>
<div class="ph-img wide" aria-label="Hero visual placeholder">[ Hero visual · 16:9 ]</div>
</div>
</section>
```
## Layout 3 — Feature triplet
Three feature cells. Lead with a small `<h2>` framing the row. Don't put an icon on every heading — one tasteful mark per cell, monoline.
```html
<section class="section" data-od-id="features">
<div class="container stack" style="gap: 56px;">
<div style="max-width: 36ch;">
<p class="eyebrow">WHAT'S DIFFERENT</p>
<h2>Three things you'll notice in the first ten minutes.</h2>
</div>
<div class="grid-3">
<div class="feature card-flat">
<div class="feature-mark">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6"><path d="M12 3v18M3 12h18"/></svg>
</div>
<h3>Specific feature one</h3>
<p>Two-sentence description that names the user value, not the technology.</p>
</div>
<div class="feature card-flat">
<div class="feature-mark">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6"><circle cx="12" cy="12" r="8"/><path d="M12 8v4l3 2"/></svg>
</div>
<h3>Specific feature two</h3>
<p>Two-sentence description that names the user value, not the technology.</p>
</div>
<div class="feature card-flat">
<div class="feature-mark">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6"><path d="M4 7h16M4 12h10M4 17h16"/></svg>
</div>
<h3>Specific feature three</h3>
<p>Two-sentence description that names the user value, not the technology.</p>
</div>
</div>
</div>
</section>
```
## Layout 4 — Stat row (data billboard)
Use when there are real numbers. Three stats max — four feels like a brochure. **Don't invent metrics.** If you don't have a number, use a different layout.
```html
<section class="section" data-od-id="stats">
<div class="container">
<p class="eyebrow" style="margin-bottom: 40px;">BY THE NUMBERS · 2026</p>
<div class="grid-3">
<div class="stat">
<div class="stat-num num">38<span class="stat-unit">×</span></div>
<p class="stat-label">less data moved over the wire vs. naive sync, on real customer workloads.</p>
</div>
<div class="stat">
<div class="stat-num num">3,184</div>
<p class="stat-label">paying teams, including 14 of the YC W26 batch.</p>
</div>
<div class="stat">
<div class="stat-num num">$0.04<span class="stat-unit">/GB</span></div>
<p class="stat-label">average egress saved — typical $1,800/mo bill drops to $200.</p>
</div>
</div>
</div>
</section>
```
## Layout 5 — Pull quote (testimonial)
A single decisive quote with attribution. Use sparingly — one per page, never two in a row.
```html
<section class="section" data-od-id="quote">
<div class="container" style="max-width: 800px;">
<div class="quote-mark">"</div>
<blockquote class="quote">Filebase pays for itself in the first month. We were going to hire a dedicated DevOps person to babysit our sync — instead we just switched.</blockquote>
<p class="quote-author">— Mira Hassan, CTO at Northwind Studios</p>
</div>
</section>
```
## Layout 6 — CTA strip (closing)
End the page on one decisive ask. Centered, generous whitespace, one primary button. No secondary unless the page has zero other buttons.
```html
<section class="section" data-od-id="cta-strip" style="text-align: center;">
<div class="container" style="max-width: 600px;">
<h2>Stop measuring meetings. Start measuring focus.</h2>
<p class="lead" style="margin: 16px auto 32px;">Free for solo. $4/mo per teammate after that.</p>
<a class="btn btn-primary" href="REPLACE_WITH_REAL_URL">Start free</a>
</div>
</section>
```
## Layout 7 — Log list (changelog / blog index / posts)
Editorial layout for a list of dated entries. Date in mono on the left, title + dek in the middle, optional pull stat on the right. Borders on top, never around — boxes feel like a brochure.
```html
<section class="section" data-od-id="log">
<div class="container">
<div class="row-between" style="margin-bottom: 32px;">
<h2>Recent changes</h2>
<a class="btn btn-ghost btn-arrow" href="REPLACE_WITH_REAL_URL">View all</a>
</div>
<div>
<article class="log-row">
<span class="meta">Apr 27, 2026</span>
<div>
<h3>Sync engine v3 — half the wire bytes</h3>
<p style="margin: 4px 0 0; color: var(--muted); font-size: 14px;">A new content-defined chunker that produces 38× fewer post-edit changes on Final Cut projects.</p>
</div>
<span class="pull meta">Engineering</span>
</article>
<article class="log-row">
<span class="meta">Apr 19, 2026</span>
<div>
<h3>Per-folder bandwidth budgets</h3>
<p style="margin: 4px 0 0; color: var(--muted); font-size: 14px;">Cap how much a single project can pull each month — useful for archive folders.</p>
</div>
<span class="pull meta">Product</span>
</article>
<article class="log-row">
<span class="meta">Apr 04, 2026</span>
<div>
<h3>S3 + R2 dual-region replication</h3>
<p style="margin: 4px 0 0; color: var(--muted); font-size: 14px;">Two providers, automatic failover. Enterprise tier only for now.</p>
</div>
<span class="pull meta">Infra</span>
</article>
</div>
</div>
</section>
```
### Layout 7 note — Release-note sections (Added, Fixed, Breaking, Known issues)
When Layout 7 is used for release-note Added/Fixed/Breaking/Known issues sections, **remove the header CTA row** (the `row-between` div with "View all" button). Release-note sections typically have no per-section destination; leaving a placeholder violates SKILL.md Step 2. Replace the entire row:
```html
<!-- REMOVE THIS from Layout 7 when used in release notes: -->
<div class="row-between" style="margin-bottom: 32px;">
<h2>Recent changes</h2>
<a class="btn btn-ghost btn-arrow" href="REPLACE_WITH_REAL_URL">View all</a>
</div>
<!-- WITH THIS: -->
<h2 style="margin-bottom: 32px;">Added</h2>
<!-- or Fixed, Breaking changes, Known issues -->
```
---
## List block — Known issues / Upgrade note
Use `.list` for itemized issue rows or step-by-step upgrade guidance. Keep the list clear, short, and factual; avoid long prose inside each bullet.
```html
<section class="section" id="known-issues" data-od-id="known-issues">
<div class="container">
<div class="row-between" style="margin-bottom: 16px;">
<h2>Known issues</h2>
<span class="meta">—</span>
</div>
<ul class="list">
<li>Sync may lag by up to 30 seconds after a large file upload.</li>
<li>Drag-and-drop is disabled for archived projects in this release.</li>
<li>Mobile app notifications may arrive late on iOS 18.</li>
</ul>
</div>
</section>
<section class="section" id="upgrade-note" data-od-id="upgrade-note">
<div class="container">
<div class="row-between" style="margin-bottom: 16px;">
<h2>Upgrade note</h2>
<span class="meta">—</span>
</div>
<ol class="list">
<li>Update to v2.3.1 using your normal release process.</li>
<li>Restart the sync service after deployment.</li>
<li>Verify release notes in the internal changelog link.</li>
</ol>
</div>
</section>
```
## Layout 8 — Comparison table (pricing, plan matrix, before/after)
Hairline borders, mono numerics, one column highlighted via an accent border. Don't put the whole row in surface-color — that screams "table".
```html
<section class="section" data-od-id="pricing">
<div class="container">
<div style="text-align: center; max-width: 36ch; margin: 0 auto 56px;">
<p class="eyebrow">PRICING</p>
<h2>One row of features. Three lines of pricing.</h2>
</div>
<table class="ds-table">
<thead>
<tr>
<th>Feature</th>
<th class="num-col">Solo</th>
<th class="num-col">Team</th>
<th class="num-col">Enterprise</th>
</tr>
</thead>
<tbody>
<tr><td>Sync engine v3</td><td class="num-col">✓</td><td class="num-col">✓</td><td class="num-col">✓</td></tr>
<tr><td>Per-folder budgets</td><td class="num-col">—</td><td class="num-col">✓</td><td class="num-col">✓</td></tr>
<tr><td>SAML / SCIM</td><td class="num-col">—</td><td class="num-col">—</td><td class="num-col">✓</td></tr>
<tr><td>Dedicated infra</td><td class="num-col">—</td><td class="num-col">—</td><td class="num-col">✓</td></tr>
<tr style="border-top: 1px solid var(--fg);">
<td><strong>Monthly</strong></td>
<td class="num-col"><strong>$0</strong></td>
<td class="num-col"><strong>$4 / seat</strong></td>
<td class="num-col"><strong>Talk to us</strong></td>
</tr>
</tbody>
</table>
</div>
</section>
```
---
## Section rhythm — when in doubt
For a 5-section landing:
1. Hero (Layout 1 or 2)
2. Features (Layout 3)
3. Stats *or* quote (Layout 4 or 5)
4. Split detail (custom, using `grid-2-1` / `grid-1-2`)
5. CTA + footer (Layout 6)
For a 4-section docs/marketing index:
1. Hero center (Layout 1)
2. Log list (Layout 7)
3. CTA + footer (Layout 6)
Two stat rows in a row, two quote blocks in a row, two feature triplets in a row — all visual fatigue. Alternate.
SKILL.md
---
name: release-notes-one-pager
description: |
Release notes one-page HTML with highlights, Added, Fixed, Breaking changes,
Known issues, and Upgrade note. Writes explicit "None" style sections
whenever the user does not provide details.
triggers:
- "release notes"
- "changelog"
- "what's new"
- "version update"
- "change log"
- "release summary"
od:
mode: prototype
platform: desktop
scenario: engineering
preview:
type: html
entry: index.html
design_system:
requires: true
sections: [color, typography, layout, components]
example_prompt: "Write release notes for v2.3.1 with Added, Fixed, Breaking changes, Known issues, and an Upgrade note."
---
# Release Notes One-Pager Skill
Produce a single-page release notes document in HTML.
## Resource map
```
release-notes-one-pager/
├── SKILL.md ← this file
├── example.html ← quality bar and style reference
├── assets/
│ └── template.html ← local seed file to copy to project index.html
└── references/
├── checklist.md ← P0 / P1 / P2 gates
└── layouts.md ← local section skeletons
```
Do not write CSS from scratch unless the user explicitly asks for a bespoke structure.
## Workflow
### Step 0 — Pre-flight
1. Read `assets/template.html`.
2. Read `references/layouts.md`.
3. Read active `DESIGN.md` and map it to the six `:root` variables.
### Step 1 — Start from the shared seed
Copy `assets/template.html` to project `index.html`.
Update:
- `<title>`
- topnav logo text
- topnav link labels (destinations are pre-wired to `#added`, `#fixed`, `#upgrade-note`)
- topnav CTA label and `href` destination, or omit the topnav CTA entirely if no real destination exists
- ensure the topnav link targets exist by adding matching section `id` attributes
### Step 2 — Build release-note structure
Inside `<main id="content">`, compose this section order:
1. Hero (Layout 1 or 2): version, date, one-sentence summary.
2. Added (use Layout 7 log-list; section root must include `id="added"`).
3. Fixed (use Layout 7 log-list; section root must include `id="fixed"`).
4. Breaking changes (use Layout 7 log-list, or one row explicitly saying "None"; section root must include `id="breaking-changes"`).
5. Known issues (Layout 7 or card list; section root must include `id="known-issues"`).
6. Upgrade note (short steps list or explicit no-action statement; section root must include `id="upgrade-note"`).
7. Closing CTA strip (Layout 6).
For every CTA in the emitted HTML (topnav, hero, closing strip), replace both the visible label and the `href` destination with real, safe values. If no real destination is available, omit the CTA entirely—do not use a placeholder such as `href="#"`, a misleading page-anchor, or `REPLACE_WITH_REAL_URL`. Hero CTAs are optional; only add them when real destinations exist.
### Step 3 — Honesty rules for missing details
If the user does not provide details, do not invent them. Write explicit placeholders:
- Summary: `No summary provided.`
- Added: `No additions provided`
- Fixed: `No fixes provided`
- Breaking changes: `None`
- Known issues: `None reported`
- Upgrade note: `No upgrade actions required based on provided information`
If release version or date is missing, use `—` and label the field rather than guessing.
### Step 4 — Self-check
Run `references/checklist.md`. Every P0 must pass.
### Step 5 — Emit artifact
Wrap output as:
```
<artifact identifier="release-notes-one-pager" type="text/html" title="Release Notes">
<!doctype html>
<html>...</html>
</artifact>
```
One sentence before the artifact. Nothing after `</artifact>`.