feat(01-04): Playwright capture toolchain + DSGN-02 approval gallery (D-14, D-12)

- screenshot.mjs: committed real-browser capture tool — tiny node static
  server (theme/ docroot), sheet fullPage capture (390px), 4 mockup captures
  via file:// (network CDN renders; committed screen.png fallback on failure —
  mockups never edited, D-06), basemap capture gated on the proof page's
  RAMS-BASEMAP-READY console signal + 2s tile settle (1280x800); blank gate
  >=10KB per capture; wired as root+theme `capture`
- gallery.html: the DSGN-02 mechanism template — 4 mockup-vs-sheet comparison
  rows + basemap direction row + token ledger (13 typography specimens + 6 hero
  color swatches, names only per D-07) + approval scope strip; CDN-free, built
  from dist/rams.css + var()-only page shell; every later UI phase extends
  this same page
- audit.mjs: gallery.html registered as an implementation page — audit
  covers its <style> block + style attributes + class harvest (354
  declarations, still 0 off-token)
- verified: CAPTURE OK 6/6 (45KB-1.2MB), gallery render check 0 failed
  requests / all 6 images decode, AUDIT CLEAN
This commit is contained in:
araharah-executor
2026-10-22 08:31:14 +08:00
parent 0b3eebf7d0
commit b462936678
4 changed files with 483 additions and 0 deletions
+290
View File
@@ -0,0 +1,290 @@
<!DOCTYPE html>
<!--
araharah — DSGN-02 approval gallery (the fidelity gate mechanism)
==================================================================
THIS PAGE IS THE DSGN-02 MECHANISM TEMPLATE. Every later UI phase
(Mini App shell, search, route pick, guidance, backgrounded
turn-cards, ...) extends THIS SAME PAGE with one comparison row
per new product screen:
<section> — h2.card-header plate: SCREEN NAME + card-ref
left : the mockup reference render (read-only, D-06)
right : the implementation capture (Playwright, D-14)
note : which sheet/product families the row compares
The token ledger and approval strip below stay unchanged. The
automated evidence stays `npm run audit` (zero off-token values,
D-13) — run it from the repo root. Phase approval is recorded in
the phase SUMMARY (DSGN-02's durable artifact trail).
Captures are regenerated by: npm run capture (D-14 toolchain).
theme/screenshots/ is gitignored (regenerated artifacts) — after
a fresh checkout run `npm run capture`, then serve theme/ and
open this page, e.g.:
npm run basemap:preview --workspace theme
→ http://localhost:8123/gallery.html
CDN-free by construction (D-16): only the token package's built
stylesheet + local captures; no network dependency at all.
-->
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>ARAHARAH — DSGN-02 Approval Gallery</title>
<!-- token package: Rams tokens + self-hosted Geist / Space Grotesk -->
<link rel="stylesheet" href="dist/rams.css">
<style>
/*
* Page shell — gallery layout only, not component API.
* Every value is a token reference (the audit scans this
* block: it must stay var()-only, zero roundedness, no
* shadows or blur).
*/
body {
background: var(--shell-bg);
color: var(--shell-on-surface);
font: var(--type-body-md);
letter-spacing: var(--type-body-md-tracking);
padding: var(--spacing-margin);
}
main {
display: flex;
flex-direction: column;
gap: var(--spacing-space-lg);
max-width: var(--spacing-gutter-desktop);
margin: 0 auto;
}
main > section {
display: flex;
flex-direction: column;
gap: var(--spacing-space-sm);
}
/* sheet-furniture pattern (plan 01-02): section plates ride the
* shell slots so the register stays legible under both schemes */
main > section > h2.card-header {
color: var(--shell-on-surface);
border-bottom-color: var(--shell-on-surface);
}
.gallery-masthead {
display: flex;
flex-direction: column;
gap: var(--spacing-space-xs);
border-bottom: 2px solid var(--shell-on-surface);
padding-bottom: var(--spacing-space-md);
}
.evidence {
border: 1px solid var(--shell-on-surface);
padding: var(--spacing-space-sm) var(--spacing-space-md);
}
.compare-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: var(--spacing-space-sm);
}
.compare-row figure {
margin: 0;
display: flex;
flex-direction: column;
gap: var(--spacing-space-xs);
}
.compare-row img,
.full-row img {
width: 100%;
height: auto;
display: block;
border: 1px solid var(--color-matte-black);
background: var(--color-surface);
}
.compare-note {
color: var(--shell-on-surface);
}
.ledger {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(var(--spacing-space-2xl), 1fr));
gap: var(--spacing-space-md) var(--spacing-space-lg);
margin: 0;
}
.ledger > div {
display: flex;
flex-direction: column;
gap: var(--spacing-space-xs);
border-left: 1px solid var(--shell-on-surface);
padding-left: var(--spacing-space-sm);
}
.ledger dd {
margin: 0;
}
.swatch {
display: inline-block;
width: var(--spacing-space-lg);
height: var(--spacing-space-lg);
border: 1px solid var(--color-matte-black);
vertical-align: middle;
margin-right: var(--spacing-space-xs);
}
.approval {
border: 2px solid var(--color-matte-black);
padding: var(--spacing-space-md);
display: flex;
flex-direction: column;
gap: var(--spacing-space-xs);
background: var(--color-soft-beige);
color: var(--color-matte-black);
}
.swatch-signal-orange { background: var(--color-signal-orange); }
.swatch-functional-red { background: var(--color-functional-red); }
.swatch-olive-green { background: var(--color-olive-green); }
.swatch-soft-beige { background: var(--color-soft-beige); }
.swatch-matte-black { background: var(--color-matte-black); }
.swatch-chassis-subtle { background: var(--color-chassis-subtle); }
@media (max-width: 480px) {
.compare-row { grid-template-columns: 1fr; }
}
</style>
</head>
<body>
<main>
<!-- Masthead -->
<header class="gallery-masthead">
<p class="text-label-sm">ARAHARAH // TELEMETRY RAMS</p>
<h1 class="text-display-lg-mobile">DSGN-02 APPROVAL GALLERY</h1>
<p class="text-body-md">Rams implementation vs mockups — side-by-side, token by token. Left columns are the mockup reference renders; right columns are the implementation component sheet rendered from the token package.</p>
<div class="evidence">
<p class="text-label-md">AUTOMATED EVIDENCE (D-13)</p>
<p class="text-body-sm">`npm run audit` exits 0 — zero off-token values across every rendered declaration (colors, typography, radius, shadows, blur all derive from theme/src/tokens.css). Re-run it from the repo root; the gallery is the human side of the same gate.</p>
</div>
</header>
<!-- Row 1 — active route guidance -->
<section>
<h2 class="card-header">
<span class="text-label-sm">MOCKUP VS IMPLEMENTATION</span>
<span class="card-ref">ACTIVE ROUTE GUIDANCE</span>
</h2>
<div class="compare-row">
<figure>
<figcaption class="text-label-sm">MOCKUP — REFERENCE RENDER</figcaption>
<img src="screenshots/mockup-active_route_guidance.png" alt="Active route guidance mockup render">
</figure>
<figure>
<figcaption class="text-label-sm">IMPLEMENTATION — COMPONENT SHEET</figcaption>
<img src="screenshots/sheet-full.png" alt="Rams component sheet rendered from the token package">
</figure>
</div>
<p class="compare-note text-body-sm">Compare: maneuver banner + arrow (SEC-05), telemetry card (SEC-04), event chips (SEC-03), headline / telemetry-num typography, buttons (SEC-01).</p>
</section>
<!-- Row 2 — navigate map overview -->
<section>
<h2 class="card-header">
<span class="text-label-sm">MOCKUP VS IMPLEMENTATION</span>
<span class="card-ref">NAVIGATE MAP OVERVIEW</span>
</h2>
<div class="compare-row">
<figure>
<figcaption class="text-label-sm">MOCKUP — REFERENCE RENDER</figcaption>
<img src="screenshots/mockup-navigate_map_overview.png" alt="Navigate map overview mockup render">
</figure>
<figure>
<figcaption class="text-label-sm">IMPLEMENTATION — COMPONENT SHEET</figcaption>
<img src="screenshots/sheet-full.png" alt="Rams component sheet rendered from the token package">
</figure>
</div>
<p class="compare-note text-body-sm">Compare: layer-control toggle (SEC-06), chips / status badges (SEC-03), label typography, inputs (SEC-02). The map canvas itself is the basemap row below.</p>
</section>
<!-- Row 3 — community incident reporter -->
<section>
<h2 class="card-header">
<span class="text-label-sm">MOCKUP VS IMPLEMENTATION</span>
<span class="card-ref">COMMUNITY INCIDENT REPORTER</span>
</h2>
<div class="compare-row">
<figure>
<figcaption class="text-label-sm">MOCKUP — REFERENCE RENDER</figcaption>
<img src="screenshots/mockup-community_incident_reporter.png" alt="Community incident reporter mockup render">
</figure>
<figure>
<figcaption class="text-label-sm">IMPLEMENTATION — COMPONENT SHEET</figcaption>
<img src="screenshots/sheet-full.png" alt="Rams component sheet rendered from the token package">
</figure>
</div>
<p class="compare-note text-body-sm">Compare: input fields (SEC-02), buttons (SEC-01), checkbox (SEC-06), hazard chips (SEC-03), body typography. The reporter's interactive modal flow is intentionally static here (out of scope, deferred).</p>
</section>
<!-- Row 4 — telegram live traffic feed -->
<section>
<h2 class="card-header">
<span class="text-label-sm">MOCKUP VS IMPLEMENTATION</span>
<span class="card-ref">TELEGRAM LIVE TRAFFIC FEED</span>
</h2>
<div class="compare-row">
<figure>
<figcaption class="text-label-sm">MOCKUP — REFERENCE RENDER</figcaption>
<img src="screenshots/mockup-telegram_live_traffic_feed.png" alt="Telegram live traffic feed mockup render">
</figure>
<figure>
<figcaption class="text-label-sm">IMPLEMENTATION — COMPONENT SHEET</figcaption>
<img src="screenshots/sheet-full.png" alt="Rams component sheet rendered from the token package">
</figure>
</div>
<p class="compare-note text-body-sm">Compare: full typography specimen (SEC-00) — display, headline, body, labels, telemetry-num — plus chips (SEC-03), buttons (SEC-01), inputs (SEC-02), telemetry card (SEC-04).</p>
</section>
<!-- Final row — basemap direction -->
<section>
<h2 class="card-header">
<span class="text-label-sm">BASEMAP DIRECTION</span>
<span class="card-ref">RAMS LIBERTY // KL METRO</span>
</h2>
<div class="full-row">
<figure>
<img src="screenshots/basemap-kl.png" alt="Rams Liberty basemap rendering over KL with the reserved signal-orange route vector">
</figure>
</div>
<p class="compare-note text-body-sm">Grayscale / muted land + water grounding, desaturated roads, muted labels — every cartographic color derived from the token package (D-09), labels in Rams-adjacent hosted fonts (D-10). Signal-orange appears nowhere except the route vector placeholder, reserved for Phase 5 (D-08). Confirm: no orange in the cartography, no saturated non-red hues, hierarchy by road weight, not tone.</p>
</section>
<!-- Token ledger — names + rendered specimens only (D-07: no numeric restatement) -->
<section>
<h2 class="card-header">
<span class="text-label-sm">TOKEN LEDGER</span>
<span class="card-ref">NAMES ONLY // D-07</span>
</h2>
<p class="text-body-sm">Typography scale — every name rendered in its own token (the package owns every numeric value):</p>
<dl class="ledger">
<div><dt class="text-label-sm">DISPLAY-LG</dt><dd class="text-display-lg">DISPLAY-LG</dd></div>
<div><dt class="text-label-sm">DISPLAY-LG-MOBILE</dt><dd class="text-display-lg-mobile">DISPLAY-LG-MOBILE</dd></div>
<div><dt class="text-label-sm">HEADLINE-LG</dt><dd class="text-headline-lg">HEADLINE-LG</dd></div>
<div><dt class="text-label-sm">HEADLINE-LG-MOBILE</dt><dd class="text-headline-lg-mobile">HEADLINE-LG-MOBILE</dd></div>
<div><dt class="text-label-sm">HEADLINE-MD</dt><dd class="text-headline-md">HEADLINE-MD</dd></div>
<div><dt class="text-label-sm">HEADLINE-SM</dt><dd class="text-headline-sm">HEADLINE-SM</dd></div>
<div><dt class="text-label-sm">BODY-LG</dt><dd class="text-body-lg">BODY-LG</dd></div>
<div><dt class="text-label-sm">BODY-MD</dt><dd class="text-body-md">BODY-MD</dd></div>
<div><dt class="text-label-sm">BODY-SM</dt><dd class="text-body-sm">BODY-SM</dd></div>
<div><dt class="text-label-sm">LABEL-LG</dt><dd class="text-label-lg">LABEL-LG</dd></div>
<div><dt class="text-label-sm">LABEL-MD</dt><dd class="text-label-md">LABEL-MD</dd></div>
<div><dt class="text-label-sm">LABEL-SM</dt><dd class="text-label-sm">LABEL-SM</dd></div>
<div><dt class="text-label-sm">TELEMETRY-NUM</dt><dd class="text-telemetry-num">18:42</dd></div>
</dl>
<p class="text-body-sm">Hero color tokens — swatches render the tokens themselves:</p>
<dl class="ledger">
<div><dt class="text-label-sm">SIGNAL-ORANGE</dt><dd class="text-label-md"><span class="swatch swatch-signal-orange"></span>SIGNAL-ORANGE</dd></div>
<div><dt class="text-label-sm">FUNCTIONAL-RED</dt><dd class="text-label-md"><span class="swatch swatch-functional-red"></span>FUNCTIONAL-RED</dd></div>
<div><dt class="text-label-sm">OLIVE-GREEN</dt><dd class="text-label-md"><span class="swatch swatch-olive-green"></span>OLIVE-GREEN</dd></div>
<div><dt class="text-label-sm">SOFT-BEIGE</dt><dd class="text-label-md"><span class="swatch swatch-soft-beige"></span>SOFT-BEIGE</dd></div>
<div><dt class="text-label-sm">MATTE-BLACK</dt><dd class="text-label-md"><span class="swatch swatch-matte-black"></span>MATTE-BLACK</dd></div>
<div><dt class="text-label-sm">CHASSIS-SUBTLE</dt><dd class="text-label-md"><span class="swatch swatch-chassis-subtle"></span>CHASSIS-SUBTLE</dd></div>
</dl>
</section>
<!-- Approval strip -->
<section class="approval">
<p class="text-label-md">APPROVAL SCOPE // DSGN-02</p>
<p class="text-body-md">This gate covers the fidelity bar — every component family on the sheet renders from the token package, verified token-by-token against the mockup renders — and the basemap direction (Rams-constant cartography, accent reserved). It does NOT cover interactive flows: incident-report modal flows, map interaction, and animation polish are intentionally out of scope for this phase (deferred per the phase context).</p>
<p class="text-body-sm">Resume signal for the phase checkpoint: "approved" (fidelity bar met, basemap direction good) — or a list of specific screens / components / basemap aspects to fix and iterate before DSGN-02 is recorded.</p>
</section>
</main>
</body>
</html>