Give the two pattern pages dedicated CSS instead of inline styles
Loop and Archive are meant to model real usage of this system — what an
actual project would write, not a documentation-only shortcut — so they
should look like it. Both were previously ~100% inline-styled: every
div's layout, spacing, and color set by hand on the element instead of
in a stylesheet (108 inline style= attributes combined).
Added _sass/site/patterns.css: real classes for the page-specific shapes
neither page shares with an actual component (a dashboard shell, a phone
frame, a status-strip day, a capture dock) — composed from the same
var(--ad-*) tokens every component reads. Also added .field-stack to
field.css itself (a genuinely reusable vertical-fields composition,
alongside the existing .form-row for side-by-side fields), since that
one belongs with the component, not the pages.
Loop: 70 inline styles -> 3 (a one-off danger-tone label and the dialog
demo's own width/min-height, consistent with how every other component
page leaves demo-specific sizing inline).
Archive: 38 -> 4 (one-off spacing/sizing choices, same standard).
Left card-row.md's own remaining inline styles alone — consolidating its
row-content pattern with Loop/Archive's would mean deciding CardRow's
real content-composition API, which is a component design decision, not
an obvious inline-to-class extraction.
Verified: rake build succeeds, screenshots of both patterns are visually
identical to before (compared against the pre-refactor renders).
diff --git a/_patterns/archive.md b/_patterns/archive.md
index db5d2d6..27b71dd 100644
--- a/_patterns/archive.md
+++ b/_patterns/archive.md
@@ -14,56 +14,56 @@ Three screens, abbreviated to what each one adds.
<figure class="example">
<figcaption>Home — recent captures</figcaption>
<div>
-<div style="width: 300px; height: 560px; background: var(--ad-color-surface-page); border-radius: var(--ad-radius-lg); box-shadow: var(--ad-shadow-float); overflow:hidden; display:flex; flex-direction:column; position:relative;">
- <header style="padding: 16px var(--ad-space-m) var(--ad-space-2xs);">
- <span style="font-weight:600; font-size: var(--ad-step-1); letter-spacing:-0.01em;">Archive</span>
+<div class="pattern-phone">
+ <header class="pattern-phone__header">
+ <span class="pattern-phone__brand">Archive</span>
</header>
- <div style="flex:1; overflow:hidden; padding: 0 var(--ad-space-m);">
- <div style="display:flex; align-items:baseline; gap:8px; margin: var(--ad-space-s) 0 var(--ad-space-xs);">
- <span style="font-weight:700; font-size: var(--ad-step-0);">Recent</span>
- <span class="type-label" style="margin-left:auto;">3 tracked</span>
+ <div class="pattern-phone__body pattern-phone__body--dock">
+ <div class="pattern-list-heading">
+ <span class="pattern-list-heading__title">Recent</span>
+ <span class="type-label">3 tracked</span>
</div>
<ul class="card">
<li>
<span class="avatar" data-square><svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 8 12 3 3 8v8l9 5 9-5V8Z" /><path d="M3 8l9 5 9-5M12 13v8" /></svg></span>
- <div style="flex:1; min-width:0;">
- <div style="font-weight:550; font-size: var(--ad-step--1); white-space:nowrap; overflow:hidden; text-overflow:ellipsis;">Dishwasher manual</div>
- <div class="type-label" style="margin-top:2px;">PDF · 2.1MB</div>
+ <div class="pattern-row">
+ <div class="pattern-file-title pattern-file-title--truncate">Dishwasher manual</div>
+ <div class="type-label pattern-file-meta">PDF · 2.1MB</div>
</div>
- <span class="type-mono" style="color: var(--ad-color-text-tertiary); font-size: var(--ad-step--2);">2h</span>
+ <span class="type-mono pattern-row__meta">2h</span>
</li>
<li>
<span class="avatar" data-square><svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 8 12 3 3 8v8l9 5 9-5V8Z" /><path d="M3 8l9 5 9-5M12 13v8" /></svg></span>
- <div style="flex:1; min-width:0;">
- <div style="font-weight:550; font-size: var(--ad-step--1);">Sofa receipt</div>
- <div class="type-label" style="margin-top:2px;">JPG · 640KB</div>
+ <div class="pattern-row">
+ <div class="pattern-file-title">Sofa receipt</div>
+ <div class="type-label pattern-file-meta">JPG · 640KB</div>
</div>
- <span class="type-mono" style="color: var(--ad-color-text-tertiary); font-size: var(--ad-step--2);">1d</span>
+ <span class="type-mono pattern-row__meta">1d</span>
</li>
<li>
<span class="avatar" data-square><svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 8 12 3 3 8v8l9 5 9-5V8Z" /><path d="M3 8l9 5 9-5M12 13v8" /></svg></span>
- <div style="flex:1; min-width:0;">
- <div style="font-weight:550; font-size: var(--ad-step--1);">Passport scan</div>
- <div class="type-label" style="margin-top:2px;">PDF · 890KB</div>
+ <div class="pattern-row">
+ <div class="pattern-file-title">Passport scan</div>
+ <div class="type-label pattern-file-meta">PDF · 890KB</div>
</div>
- <span class="type-mono" style="color: var(--ad-color-text-tertiary); font-size: var(--ad-step--2);">4d</span>
+ <span class="type-mono pattern-row__meta">4d</span>
</li>
</ul>
</div>
- <div style="position:absolute; left:0; right:0; bottom:0; display:flex; justify-content:center; padding: 14px 20px 22px; background: linear-gradient(to top, var(--ad-color-surface-page) 65%, transparent);">
+ <div class="pattern-dock">
<button data-variant="primary" data-size="lg" data-block>
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8a2 2 0 0 1 2-2h1.5l1-1.5h9l1 1.5H20a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8Z" /><circle cx="12" cy="13" r="3.5" /></svg>
Take a photo
</button>
</div>
</div>
-<div style="width: 300px; height: 560px; background: var(--ad-color-surface-page); border-radius: var(--ad-radius-lg); box-shadow: var(--ad-shadow-float); overflow:hidden; display:flex; flex-direction:column;">
- <header style="display:flex; align-items:center; gap:8px; padding: 16px var(--ad-space-m) var(--ad-space-2xs);">
+<div class="pattern-phone">
+ <header class="pattern-phone__header">
<button class="icon-button" data-size="sm" aria-label="Back"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 18l-6-6 6-6" /></svg></button>
- <span style="font-weight:600; font-size: var(--ad-step-0);">Save</span>
+ <span class="pattern-phone__title">Save</span>
</header>
- <div style="flex:1; padding: 0 var(--ad-space-m);">
- <div style="height: 180px; border-radius: var(--ad-radius-lg); border: 1px solid var(--ad-color-border-default); background: repeating-linear-gradient(45deg, transparent 0 8px, var(--ad-color-surface-fill-2) 8px 9px), var(--ad-color-surface-fill); display:flex; flex-direction:column; align-items:center; justify-content:center; gap:6px; margin-top: var(--ad-space-s);">
+ <div class="pattern-phone__body">
+ <div class="pattern-photo-slot">
<svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 8 12 3 3 8v8l9 5 9-5V8Z" /><path d="M3 8l9 5 9-5M12 13v8" /></svg>
<span class="type-label">Photo preview</span>
</div>
@@ -72,27 +72,27 @@ Three screens, abbreviated to what each one adds.
<input placeholder="Dishwasher manual" />
</label>
</div>
- <div style="padding: var(--ad-space-m); display:flex; gap: var(--ad-space-2xs);">
- <button style="flex:1;">Retake</button>
- <button data-variant="primary" style="flex:1;">Save</button>
+ <div class="pattern-phone__footer">
+ <button>Retake</button>
+ <button data-variant="primary">Save</button>
</div>
</div>
-<div style="width: 300px; height: 560px; background: var(--ad-color-surface-page); border-radius: var(--ad-radius-lg); box-shadow: var(--ad-shadow-float); overflow:hidden; display:flex; flex-direction:column;">
- <header style="display:flex; align-items:center; gap:8px; padding: 16px var(--ad-space-m) var(--ad-space-2xs);">
+<div class="pattern-phone">
+ <header class="pattern-phone__header">
<button class="icon-button" data-size="sm" aria-label="Back"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 18l-6-6 6-6" /></svg></button>
<span class="type-label">Archive</span>
</header>
- <div style="flex:1; overflow:hidden; padding: 0 var(--ad-space-m) var(--ad-space-l);">
- <h1 style="font-weight:700; font-size: var(--ad-step-2); letter-spacing:-0.01em; margin: var(--ad-space-xs) 0 var(--ad-space-2xs);">Dishwasher manual</h1>
- <div style="display:flex; gap:6px; flex-wrap:wrap; margin-bottom: var(--ad-space-m);">
+ <div class="pattern-phone__body">
+ <h1 class="pattern-detail-title">Dishwasher manual</h1>
+ <div class="pattern-tag-row">
<span class="tag">location:kitchen</span>
<span class="tag" data-dashed>+ add tag</span>
</div>
- <div class="type-label" style="margin-bottom: var(--ad-space-2xs);">File</div>
+ <div class="type-label pattern-aside__label--tight">File</div>
<ul class="card">
<li>
<span class="avatar" data-square><svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 8 12 3 3 8v8l9 5 9-5V8Z" /><path d="M3 8l9 5 9-5M12 13v8" /></svg></span>
- <div style="flex:1; font-size: var(--ad-step--1);">PDF · 2.1MB</div>
+ <div class="pattern-row" style="font-size: var(--ad-step--1);">PDF · 2.1MB</div>
</li>
</ul>
<div class="type-label" style="margin: var(--ad-space-m) 0 var(--ad-space-2xs);">Notes</div>
diff --git a/_patterns/loop.md b/_patterns/loop.md
index fac98fa..8bb5b24 100644
--- a/_patterns/loop.md
+++ b/_patterns/loop.md
@@ -9,115 +9,116 @@ summary: >-
Loop is not a real product — it is the system's own reference layout for a
dense, table-ish list screen: the shape a recurring-task tracker would take
if it were rebuilt on this token set. Every visible value below is a
-component from this system; nothing here is page-specific CSS beyond a
-couple of inline widths.
+component from this system, composed with real page-specific CSS
+(`_sass/site/patterns.css`) rather than inline styles — the same thing an
+actual project built on this system would write.
<figure class="example">
<figcaption>Loop</figcaption>
<div data-layout="stack">
-<div style="border: 1px solid var(--ad-color-border-hairline); border-radius: var(--ad-radius-lg); overflow: hidden; background: var(--ad-color-surface-page);">
- <header style="display:flex; align-items:center; gap: var(--ad-space-s); padding: var(--ad-space-s) var(--ad-space-l); border-bottom: 1px solid var(--ad-color-border-hairline);">
- <span style="font-family: var(--ad-font-mono); font-weight:600; letter-spacing:0.02em; font-size: var(--ad-step-0);">Loop</span>
- <div style="flex:1;"></div>
+<div class="pattern-dashboard">
+ <header class="pattern-dashboard__header">
+ <span class="pattern-dashboard__title">Loop</span>
+ <div class="pattern-spacer"></div>
<button data-variant="primary" data-size="sm">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 5v14M5 12h14" /></svg>
New
</button>
<span class="avatar">km</span>
</header>
- <div style="display:flex;">
- <div style="flex:1; min-width:0; max-width: 34rem; padding: var(--ad-space-l);">
- <section style="margin-bottom: var(--ad-space-l);">
- <div style="display:flex; align-items:baseline; gap: var(--ad-space-2xs); margin-bottom: var(--ad-space-s);">
+ <div class="pattern-dashboard__body">
+ <div class="pattern-dashboard__main">
+ <section class="pattern-section">
+ <div class="pattern-section__heading">
<span class="type-label" style="color: var(--ad-color-danger);">Overdue</span>
<span class="badge" data-tone="danger">2</span>
</div>
- <ul class="card" style="border:none; border-radius:0; background:none;">
- <li style="border-top: 1px solid var(--ad-color-border-hairline);">
- <button aria-label="Complete" style="all:unset; width:18px; height:18px; border-radius:4px; flex-shrink:0; cursor:pointer; border: 1.5px solid var(--ad-color-danger);"></button>
- <div style="flex:1; min-width:0;">
- <div style="font-weight:500; color: var(--ad-color-danger);">Call Mom</div>
- <div class="type-mono" style="color: var(--ad-color-text-tertiary); font-size: var(--ad-step--2);">every 2 weeks</div>
+ <ul class="card pattern-list">
+ <li>
+ <button class="pattern-complete" data-tone="danger" aria-label="Complete"></button>
+ <div class="pattern-row">
+ <div class="pattern-row__title" data-tone="danger">Call Mom</div>
+ <div class="type-mono pattern-row__meta">every 2 weeks</div>
</div>
- <span class="type-mono" style="color: var(--ad-color-danger); font-size: var(--ad-step--2); white-space:nowrap;">3d overdue</span>
+ <span class="type-mono pattern-row__trailing" data-tone="danger">3d overdue</span>
</li>
<li>
- <button aria-label="Complete" style="all:unset; width:18px; height:18px; border-radius:4px; flex-shrink:0; cursor:pointer; border: 1.5px solid var(--ad-color-danger);"></button>
- <div style="flex:1; min-width:0;">
- <div style="font-weight:500; color: var(--ad-color-danger);">Water the ferns</div>
- <div class="type-mono" style="color: var(--ad-color-text-tertiary); font-size: var(--ad-step--2);">every 5 days</div>
+ <button class="pattern-complete" data-tone="danger" aria-label="Complete"></button>
+ <div class="pattern-row">
+ <div class="pattern-row__title" data-tone="danger">Water the ferns</div>
+ <div class="type-mono pattern-row__meta">every 5 days</div>
</div>
- <span class="type-mono" style="color: var(--ad-color-danger); font-size: var(--ad-step--2); white-space:nowrap;">1d overdue</span>
+ <span class="type-mono pattern-row__trailing" data-tone="danger">1d overdue</span>
</li>
</ul>
</section>
- <section>
- <div class="type-label" style="margin-bottom: var(--ad-space-s);">Upcoming</div>
- <ul class="card" style="border:none; border-radius:0; background:none;">
- <li style="border-top: 1px solid var(--ad-color-border-hairline);">
- <button aria-label="Complete" style="all:unset; width:18px; height:18px; border-radius:4px; flex-shrink:0; cursor:pointer; border: 1.5px solid var(--ad-color-text-secondary);"></button>
- <div style="flex:1; min-width:0;">
- <div style="font-weight:500;">Renew passport</div>
- <div class="type-mono" style="color: var(--ad-color-text-tertiary); font-size: var(--ad-step--2);">every 10 years</div>
+ <section class="pattern-section">
+ <div class="type-label pattern-aside__label">Upcoming</div>
+ <ul class="card pattern-list">
+ <li>
+ <button class="pattern-complete" aria-label="Complete"></button>
+ <div class="pattern-row">
+ <div class="pattern-row__title">Renew passport</div>
+ <div class="type-mono pattern-row__meta">every 10 years</div>
</div>
- <span class="type-mono" style="color: var(--ad-color-text-secondary); font-size: var(--ad-step--2); white-space:nowrap;">in 3 months</span>
+ <span class="type-mono pattern-row__trailing" data-tone="secondary">in 3 months</span>
</li>
<li>
- <button aria-label="Complete" style="all:unset; width:18px; height:18px; border-radius:4px; flex-shrink:0; cursor:pointer; border: 1.5px solid var(--ad-color-text-secondary);"></button>
- <div style="flex:1; min-width:0;">
- <div style="font-weight:500;">Dentist checkup</div>
- <div class="type-mono" style="color: var(--ad-color-text-tertiary); font-size: var(--ad-step--2);">every 6 months</div>
+ <button class="pattern-complete" aria-label="Complete"></button>
+ <div class="pattern-row">
+ <div class="pattern-row__title">Dentist checkup</div>
+ <div class="type-mono pattern-row__meta">every 6 months</div>
</div>
- <span class="type-mono" style="color: var(--ad-color-text-secondary); font-size: var(--ad-step--2); white-space:nowrap;">in 2 weeks</span>
+ <span class="type-mono pattern-row__trailing" data-tone="secondary">in 2 weeks</span>
</li>
<li>
- <button aria-label="Complete" style="all:unset; width:18px; height:18px; border-radius:4px; flex-shrink:0; cursor:pointer; border: 1.5px solid var(--ad-color-text-secondary);"></button>
- <div style="flex:1; min-width:0;">
- <div style="font-weight:500;">Rotate mattress</div>
- <div class="type-mono" style="color: var(--ad-color-text-tertiary); font-size: var(--ad-step--2);">every 6 months</div>
+ <button class="pattern-complete" aria-label="Complete"></button>
+ <div class="pattern-row">
+ <div class="pattern-row__title">Rotate mattress</div>
+ <div class="type-mono pattern-row__meta">every 6 months</div>
</div>
- <span class="type-mono" style="color: var(--ad-color-text-secondary); font-size: var(--ad-step--2); white-space:nowrap;">in 5 weeks</span>
+ <span class="type-mono pattern-row__trailing" data-tone="secondary">in 5 weeks</span>
</li>
</ul>
</section>
</div>
- <aside style="width: 15rem; flex-shrink:0; background: var(--ad-color-surface-fill); border-left: 1px solid var(--ad-color-border-hairline); padding: var(--ad-space-l);">
- <div class="type-label" style="margin-bottom: var(--ad-space-s);">This week</div>
- <div style="display:flex; background: var(--ad-color-surface-card); border: 1px solid var(--ad-color-border-hairline); border-radius: var(--ad-radius-md); overflow:hidden; margin-bottom: var(--ad-space-l);">
- <div style="flex:1; display:flex; flex-direction:column; align-items:center; gap:4px; padding: 8px 0; border-right: 1px solid var(--ad-color-border-hairline);">
- <span class="type-mono" style="font-size:9px; color: var(--ad-color-text-tertiary); text-transform:uppercase;">S</span>
- <span style="font-size:13px; font-weight:600; color: var(--ad-color-text-primary);">0</span>
+ <aside class="pattern-dashboard__aside">
+ <div class="type-label pattern-aside__label">This week</div>
+ <div class="pattern-week">
+ <div class="pattern-week__day">
+ <span class="type-mono pattern-week__label">S</span>
+ <span class="pattern-week__value">0</span>
</div>
- <div style="flex:1; display:flex; flex-direction:column; align-items:center; gap:4px; padding: 8px 0; border-right: 1px solid var(--ad-color-border-hairline);">
- <span class="type-mono" style="font-size:9px; color: var(--ad-color-text-tertiary); text-transform:uppercase;">M</span>
- <span style="font-size:13px; font-weight:600; color: var(--ad-color-text-primary);">1</span>
+ <div class="pattern-week__day">
+ <span class="type-mono pattern-week__label">M</span>
+ <span class="pattern-week__value">1</span>
</div>
- <div style="flex:1; display:flex; flex-direction:column; align-items:center; gap:4px; padding: 8px 0; border-right: 1px solid var(--ad-color-border-hairline);">
- <span class="type-mono" style="font-size:9px; color: var(--ad-color-text-tertiary); text-transform:uppercase;">T</span>
- <span style="font-size:13px; font-weight:600; color: var(--ad-color-text-primary);">0</span>
+ <div class="pattern-week__day">
+ <span class="type-mono pattern-week__label">T</span>
+ <span class="pattern-week__value">0</span>
</div>
- <div style="flex:1; display:flex; flex-direction:column; align-items:center; gap:4px; padding: 8px 0; background: var(--ad-color-accent-soft); border-right: 1px solid var(--ad-color-border-hairline);">
- <span class="type-mono" style="font-size:9px; color: var(--ad-color-text-tertiary); text-transform:uppercase;">W</span>
- <span style="font-size:13px; font-weight:600; color: var(--ad-color-accent-ink);">2</span>
+ <div class="pattern-week__day" data-active>
+ <span class="type-mono pattern-week__label">W</span>
+ <span class="pattern-week__value">2</span>
</div>
- <div style="flex:1; display:flex; flex-direction:column; align-items:center; gap:4px; padding: 8px 0; border-right: 1px solid var(--ad-color-border-hairline);">
- <span class="type-mono" style="font-size:9px; color: var(--ad-color-text-tertiary); text-transform:uppercase;">T</span>
- <span style="font-size:13px; font-weight:600; color: var(--ad-color-text-primary);">0</span>
+ <div class="pattern-week__day">
+ <span class="type-mono pattern-week__label">T</span>
+ <span class="pattern-week__value">0</span>
</div>
- <div style="flex:1; display:flex; flex-direction:column; align-items:center; gap:4px; padding: 8px 0; border-right: 1px solid var(--ad-color-border-hairline);">
- <span class="type-mono" style="font-size:9px; color: var(--ad-color-text-tertiary); text-transform:uppercase;">F</span>
- <span style="font-size:13px; font-weight:600; color: var(--ad-color-text-primary);">1</span>
+ <div class="pattern-week__day">
+ <span class="type-mono pattern-week__label">F</span>
+ <span class="pattern-week__value">1</span>
</div>
- <div style="flex:1; display:flex; flex-direction:column; align-items:center; gap:4px; padding: 8px 0; ">
- <span class="type-mono" style="font-size:9px; color: var(--ad-color-text-tertiary); text-transform:uppercase;">S</span>
- <span style="font-size:13px; font-weight:600; color: var(--ad-color-text-primary);">0</span>
+ <div class="pattern-week__day">
+ <span class="type-mono pattern-week__label">S</span>
+ <span class="pattern-week__value">0</span>
</div>
- </div>
- <div class="type-label" style="margin-bottom: var(--ad-space-2xs);">Series</div>
+ </div>
+ <div class="type-label pattern-aside__label--tight">Series</div>
<div>
- <div style="padding: var(--ad-space-2xs) 0; font-size: var(--ad-step--1); color: var(--ad-color-text-secondary);">Call Mom</div>
- <div style="padding: var(--ad-space-2xs) 0; border-top: 1px solid var(--ad-color-border-hairline); font-size: var(--ad-step--1); color: var(--ad-color-text-secondary);">Water the ferns</div>
- <div style="padding: var(--ad-space-2xs) 0; border-top: 1px solid var(--ad-color-border-hairline); font-size: var(--ad-step--1); color: var(--ad-color-text-secondary);">Renew passport</div>
+ <div class="pattern-series-item">Call Mom</div>
+ <div class="pattern-series-item">Water the ferns</div>
+ <div class="pattern-series-item">Renew passport</div>
</div>
</aside>
</div>
@@ -134,7 +135,7 @@ couple of inline widths.
<dialog open style="width: 22rem;">
<header>New series</header>
<div>
- <div style="display:flex; flex-direction:column; gap: var(--ad-space-s);">
+ <div class="field-stack">
<label class="field">
Note
<input placeholder="Call Mom" />
diff --git a/_sass/components/field.css b/_sass/components/field.css
index b92908b..e3aa192 100644
--- a/_sass/components/field.css
+++ b/_sass/components/field.css
@@ -184,6 +184,9 @@ form {
grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr));
gap: var(--ad-space-xs);
}
+/* Vertically stacked fields — the other half of form layout alongside
+ .form-row's side-by-side pair. */
+.field-stack { display: flex; flex-direction: column; gap: var(--ad-space-s); }
fieldset {
display: flex;
flex-direction: column;
diff --git a/_sass/site/patterns.css b/_sass/site/patterns.css
new file mode 100644
index 0000000..99c5567
--- /dev/null
+++ b/_sass/site/patterns.css
@@ -0,0 +1,193 @@
+/* Dedicated CSS for the two reference patterns (Loop, Archive) — this is
+ what an actual project built on this system would write: real classes for
+ the page-specific shapes (an app shell, a phone frame, a status-strip
+ day), composed from the same var(--ad-*) tokens every component reads,
+ rather than hand-styling every element inline. Nothing here is a system
+ component — see _sass/components/ for those — this is what sits between
+ them on a real screen. */
+
+/* -- Loop (dashboard) ----------------------------------------------------- */
+
+.pattern-dashboard {
+ border: 1px solid var(--ad-color-border-hairline);
+ border-radius: var(--ad-radius-lg);
+ overflow: hidden;
+ background: var(--ad-color-surface-page);
+}
+.pattern-dashboard__header {
+ display: flex;
+ align-items: center;
+ gap: var(--ad-space-s);
+ padding: var(--ad-space-s) var(--ad-space-l);
+ border-bottom: 1px solid var(--ad-color-border-hairline);
+}
+.pattern-dashboard__title {
+ font-family: var(--ad-font-mono);
+ font-weight: 600;
+ letter-spacing: 0.02em;
+ font-size: var(--ad-step-0);
+}
+.pattern-dashboard__body { display: flex; }
+.pattern-dashboard__main {
+ flex: 1;
+ min-width: 0;
+ max-width: 34rem;
+ padding: var(--ad-space-l);
+}
+.pattern-dashboard__aside {
+ width: 15rem;
+ flex-shrink: 0;
+ background: var(--ad-color-surface-fill);
+ border-left: 1px solid var(--ad-color-border-hairline);
+ padding: var(--ad-space-l);
+}
+.pattern-spacer { flex: 1; }
+
+.pattern-section { margin-bottom: var(--ad-space-l); }
+.pattern-section:last-child { margin-bottom: 0; }
+.pattern-section__heading {
+ display: flex;
+ align-items: baseline;
+ gap: var(--ad-space-2xs);
+ margin-bottom: var(--ad-space-s);
+}
+
+/* A section list without Card's own border/background — the divider
+ between rows still comes from `.card > li`; only the outer frame is
+ suppressed, since the dashboard's own border already contains it. */
+.pattern-list { border: none; border-radius: 0; background: none; }
+.pattern-list li:first-child { border-top: 1px solid var(--ad-color-border-hairline); }
+
+.pattern-complete {
+ all: unset;
+ width: 18px;
+ height: 18px;
+ border-radius: 4px;
+ flex-shrink: 0;
+ cursor: pointer;
+ border: 1.5px solid var(--ad-color-text-secondary);
+}
+.pattern-complete[data-tone="danger"] { border-color: var(--ad-color-danger); }
+
+.pattern-row { flex: 1; min-width: 0; }
+.pattern-row__title { font-weight: 500; }
+.pattern-row__title[data-tone="danger"] { color: var(--ad-color-danger); }
+.pattern-row__meta { color: var(--ad-color-text-tertiary); font-size: var(--ad-step--2); }
+.pattern-row__trailing { font-size: var(--ad-step--2); white-space: nowrap; }
+.pattern-row__trailing[data-tone="danger"] { color: var(--ad-color-danger); }
+.pattern-row__trailing[data-tone="secondary"] { color: var(--ad-color-text-secondary); }
+
+.pattern-week {
+ display: flex;
+ background: var(--ad-color-surface-card);
+ border: 1px solid var(--ad-color-border-hairline);
+ border-radius: var(--ad-radius-md);
+ overflow: hidden;
+ margin-bottom: var(--ad-space-l);
+}
+.pattern-week__day {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 4px;
+ padding: 8px 0;
+ border-right: 1px solid var(--ad-color-border-hairline);
+}
+.pattern-week__day:last-child { border-right: none; }
+.pattern-week__day[data-active] { background: var(--ad-color-accent-soft); }
+.pattern-week__label {
+ font-size: 9px;
+ color: var(--ad-color-text-tertiary);
+ text-transform: uppercase;
+}
+.pattern-week__value { font-size: 13px; font-weight: 600; color: var(--ad-color-text-primary); }
+.pattern-week__day[data-active] .pattern-week__value { color: var(--ad-color-accent-ink); }
+
+.pattern-aside__label { margin-bottom: var(--ad-space-s); }
+.pattern-aside__label--tight { margin-bottom: var(--ad-space-2xs); }
+
+.pattern-series-item {
+ padding: var(--ad-space-2xs) 0;
+ font-size: var(--ad-step--1);
+ color: var(--ad-color-text-secondary);
+}
+.pattern-series-item:not(:first-child) { border-top: 1px solid var(--ad-color-border-hairline); }
+
+/* -- Archive (mobile) ------------------------------------------------------ */
+
+.pattern-phone {
+ width: 300px;
+ height: 560px;
+ background: var(--ad-color-surface-page);
+ border-radius: var(--ad-radius-lg);
+ box-shadow: var(--ad-shadow-float);
+ overflow: hidden;
+ display: flex;
+ flex-direction: column;
+ position: relative;
+}
+.pattern-phone__header {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ padding: 16px var(--ad-space-m) var(--ad-space-2xs);
+}
+/* The home screen's app-level wordmark — one step larger than a page's own
+ contextual title next to a back button. */
+.pattern-phone__brand { font-weight: 600; font-size: var(--ad-step-1); letter-spacing: -0.01em; }
+.pattern-phone__title { font-weight: 600; font-size: var(--ad-step-0); }
+.pattern-phone__body {
+ flex: 1;
+ overflow: hidden;
+ padding: 0 var(--ad-space-m) var(--ad-space-l);
+}
+/* The home screen's list runs under a floating capture dock instead of
+ ending in its own bottom padding. */
+.pattern-phone__body--dock { padding-bottom: 0; }
+
+.pattern-list-heading {
+ display: flex;
+ align-items: baseline;
+ gap: 8px;
+ margin: var(--ad-space-s) 0 var(--ad-space-xs);
+}
+.pattern-list-heading__title { font-weight: 700; font-size: var(--ad-step-0); }
+.pattern-list-heading .type-label { margin-left: auto; }
+
+.pattern-dock {
+ position: absolute;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ display: flex;
+ justify-content: center;
+ padding: 14px 20px 22px;
+ background: linear-gradient(to top, var(--ad-color-surface-page) 65%, transparent);
+}
+
+/* The one patterned background this system permits, and only here: an
+ empty photo slot. */
+.pattern-photo-slot {
+ height: 180px;
+ border-radius: var(--ad-radius-lg);
+ border: 1px solid var(--ad-color-border-default);
+ background: repeating-linear-gradient(45deg, transparent 0 8px, var(--ad-color-surface-fill-2) 8px 9px),
+ var(--ad-color-surface-fill);
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ gap: 6px;
+ margin-top: var(--ad-space-s);
+}
+
+.pattern-detail-title { font-weight: 700; font-size: var(--ad-step-2); letter-spacing: -0.01em; margin: var(--ad-space-xs) 0 var(--ad-space-2xs); }
+.pattern-tag-row { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: var(--ad-space-m); }
+
+.pattern-file-title { font-weight: 550; font-size: var(--ad-step--1); }
+.pattern-file-title--truncate { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
+.pattern-file-meta { margin-top: 2px; }
+
+.pattern-phone__footer { padding: var(--ad-space-m); display: flex; gap: var(--ad-space-2xs); }
+.pattern-phone__footer button { flex: 1; }
diff --git a/lib/build.rb b/lib/build.rb
index 5a56bb7..2a7bee1 100644
--- a/lib/build.rb
+++ b/lib/build.rb
@@ -54,6 +54,7 @@ CSS_FILES = %w[
site/shell.css
site/example.css
site/tokens-docs.css
+ site/patterns.css
].freeze
# [source file (absolute or relative to ROOT), url, section label for the